/* ============================================================================
   Trade Software - Hoja de estilos
   Desarrollado por Carlos David Donoso Cordero (ddchack).
   Identidad visual propia: "bold minimalism" industrial/retail.
   Tipografía: Funnel Display (titulares) + Karla (texto). Iconos: Phosphor.
   ========================================================================= */

:root {
  --color-fondo: #F7F3EB;
  --color-superficie: #FFFFFF;
  --color-texto: #22223B;
  --color-texto-suave: #565670;
  /* Chip de cifra sobre la foto del hero. Va traslúcido para que la imagen
     asome, y tiene pareja oscura más abajo: si no la tuviera, en tema oscuro el
     texto se aclararía y el fondo no, que es como se volvieron invisibles. */
  --hero-chip-bg: rgba(247, 243, 235, .94);
  --color-acento: #E9940A;
  --color-acento-fuerte: #9A6103;
  --color-energia: #FF5A1F;
  /* El naranja de marca SOBRE FONDO CLARO no llega al mínimo de lectura: medido,
     2,79:1 sobre el chip del precio. Éste es el más claro que sí cumple (4,54:1)
     conservando el tono, para no perder el carácter por oscurecer de más.
     Se usa sólo para TEXTO; para rellenos sigue valiendo --color-energia. */
  --color-energia-txt: #C24418;
  --color-borde: #22223B;
  --color-borde-suave: #E2DACB;
  --color-gris: #A9A69E;
  --color-alerta-alto: #D7263D;
  /* Misma historia que --color-energia-txt, y por el mismo motivo: el rojo de
     arriba alimenta RELLENOS sólidos (.btn-peligro, .sd-kpi-ic, el medidor de
     contraseña, la barra de .ff-row--alerta), así que no se puede aclarar ni
     invertir sin romper cinco componentes; pero como TEXTO se queda en 4,48:1
     sobre el fondo de página, por debajo del mínimo de la casa. Éste es su
     variante para TINTA: 5,94:1 en claro.
     La pareja oscura ya existía suelta dentro del @media de tema oscuro; ahora
     que el token está declarado también aquí, quien lo use se invierte solo y no
     hace falta repetir el selector en el bloque oscuro. */
  --color-alerta-alto-txt: #B42318;
  --color-alerta-bajo: #1B7A43;
  /* Ámbar del punto medio: ni alarma ni visto bueno, "no se sabe todavía".
     Oscuro a propósito, porque siempre lleva glifo BLANCO encima (.sd-kpi-ic). */
  --color-alerta-medio: #A25C00;
  --color-alerta-fondo: #FFF1DC;
  --radio: 10px;
  --radio-lg: 16px;
  --sombra-dura: 4px 4px 0px var(--color-borde);
  --sombra-dura-sm: 2px 2px 0px var(--color-borde);
  --glow-energia: 0 8px 24px rgba(255, 90, 31, 0.35);
  /* ESCALA DE TEXTO PEQUEÑO. Medido en estas pantallas: 13 tamaños distintos
     entre .62 y .86rem —.72, .74, .76, .78, .80, .82…— que por separado parecen
     razonables y juntos hacen que nada rime. Diferencias de .02rem (0,3 px) no
     se perciben como jerarquía: sólo como desorden.
     Tres pasos bastan para lo que hay que distinguir: el rótulo en mayúsculas,
     el texto de apoyo y el contenido de una tarjeta. */
  /* ESCALA DE SEPARACIÓN. Medido en las clases de estas pantallas: 37 valores
     distintos entre gap, padding y margen —prácticamente todos los enteros del 1
     al 12—. Una diferencia de 1 px no es jerarquía, es ruido acumulado de haber
     ajustado cada pieza por su cuenta.
     Cinco pasos, y el de 2 px existe a propósito: un rótulo sobre su cifra tiene
     que ir apretado, y meterlo en 4 los separaría. */
  --esp-2xs: 2px;   /* apilado apretado: rótulo sobre cifra */
  --esp-xs: 4px;    /* dentro de un elemento: icono y texto */
  --esp-sm: 8px;    /* entre elementos hermanos */
  --esp-md: 12px;   /* entre bloques de una tarjeta */
  --esp-lg: 16px;   /* entre tarjetas */
  --txt-micro: .7rem;    /* 11.2px - etiquetas en mayúsculas, metadatos */
  --txt-sec: .78rem;     /* 12.5px - subtítulos, ayudas, notas */
  --txt-peq: .86rem;     /* 13.8px - contenido de tarjeta */
  /* Tres pasos mas de la misma escala. Salieron de contar: .72rem aparecia 60 veces
       a pelo en las dos hojas, .82rem otras 60 y .9rem 35, sin ningun token que las
       nombrara, mientras que solo 47 font-size de 800 usaban var(--txt-*). Cada
       token vale EXACTAMENTE el literal que sustituye, asi que cambiarlos no mueve
       ni un pixel: lo que cambia es que ahora hay un solo sitio donde tocarlos.
       Los valores vecinos (.8, .84, .85, .76, .74...) se quedan a pelo a proposito:
       redondearlos a un token si cambiaria tamanos. */
  --txt-xs: .72rem;      /* 11.5px - rotulos de filtro y KPI, cifras secundarias */
  --txt-sm: .82rem;      /* 13.1px - texto de dialogos, listas y buscadores */
  --txt-md: .9rem;       /* 14.4px - plantillas, tarjetas de aviso, botones chicos */
  --fuente-titulo: "Bricolage Grotesque", "Funnel Display", system-ui, sans-serif;
  --fuente-cuerpo: "Instrument Sans", "Karla", system-ui, sans-serif;
  --barra-inferior-alto: 64px;
  --textura-linea: rgba(34, 34, 59, 0.05);
  --sombra-suave: 0 1px 2px rgba(34, 34, 59, 0.05), 0 10px 24px rgba(34, 34, 59, 0.08);
  --color-superficie-alt: #F1EADD;
  /* Chrome de la app (barras superior/inferior): tinta con texto crema. */
  --chrome-bg: #22223B;
  --chrome-texto: #F7F3EB;
  /* Fondo del hero del panel admin. Tiene token PROPIO y valor FIJO en ambos temas
     a propósito: antes se pintaba con --color-texto, que el tema oscuro voltea a
     crema — el mismo color que su titular (--chrome-texto) — y la cabecera de 26
     pantallas quedaba crema sobre crema (contraste 1:1, texto invisible). Tampoco
     sirve --chrome-bg: en oscuro vale igual que --color-superficie y el hero se
     confundiría con las tarjetas de debajo. */
  --hero-admin-bg: #22223B;
  --ease-out: cubic-bezier(0.22, 0.9, 0.3, 1);
  --dur-1: 0.15s;
  --dur-2: 0.3s;
  /* Escala de espaciado base 4px (aditiva; no remapea paddings existentes). */
  --e1: 4px;
  --e2: 8px;
  --e3: 12px;
  --e4: 16px;
  --e5: 24px;
  --e6: 32px;
  --e7: 48px;
  /* Radios que faltaban, coherentes con --radio:10px / --radio-lg:16px. */
  --radio-sm: 8px;
  --radio-md: 12px;
  --radio-pill: 999px;
  /* Escala tipográfica en rem, coherente con los tamaños ya usados. */
  --text-2xs: .7rem;
  --text-xs: .78rem;
  --text-sm: .85rem;
  --text-md: .95rem;
  --text-lg: 1.1rem;
  --text-xl: 1.35rem;
  --text-2xl: 1.7rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-fondo: #14151F;
    --color-superficie: #1D1E2C;
    --color-texto: #F3F0E7;
    --color-texto-suave: #B9B7C6;
    /* La pareja oscura del chip del hero: misma superficie que las tarjetas,
       traslúcida igual. Con esto el número pasa de 1,03:1 a legible. */
    --hero-chip-bg: rgba(29, 30, 44, .92);
    /* Sobre fondo oscuro el naranja de marca ya cumple (5,28:1): no hace falta
       apagarlo, y así el precio conserva su color en el tema en que se ve bien. */
    --color-energia-txt: #FF5A1F;
    --color-borde: #F3F0E7;
    --color-borde-suave: #3B3C4A;
    --textura-linea: rgba(243, 240, 231, 0.05);
    --sombra-suave: 0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 26px rgba(0, 0, 0, 0.42);
    --color-superficie-alt: #262736;
    --chrome-bg: #1D1E2C;
    --chrome-texto: #F3F0E7;
    /* Realce ámbar ("nota"): en oscuro pasa de crema fijo a ámbar oscuro, con el
       texto/íconos de acento en ámbar CLARO. Así todo lo que usa estas dos
       variables (notas, chips de contexto, enlaces, íconos) queda legible sin
       tocar cada componente. OJO: un badge (.almacen-ficha-foto-hint) usaba
       --color-acento-fuerte como FONDO con texto blanco; se le da fondo energía
       aparte, más abajo en este mismo bloque de oscuro. */
    --color-alerta-fondo: #382C12;
    --color-acento-fuerte: #F0B24A;
  }
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  background-color: var(--color-fondo);
  background-image: radial-gradient(var(--textura-linea) 1px, transparent 1.4px);
  background-size: 22px 22px;
  background-attachment: fixed;
  color: var(--color-texto);
  font-family: var(--fuente-cuerpo);
  font-size: 16px;
  line-height: 1.55;
  -webkit-tap-highlight-color: transparent;
}

h1, h2, h3, h4 {
  font-family: var(--fuente-titulo);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.15;
  margin: 0 0 8px;
  color: var(--color-texto);
}
h1 { font-size: 2.05rem; font-weight: 800; }
h2 { font-size: 1.35rem; }
h3 { font-size: 1.12rem; }
h4 { font-size: 1rem; }
p { line-height: 1.6; }

a { color: var(--color-acento-fuerte); }

::selection { background: var(--color-energia); color: #FFFFFF; }

@media (min-width: 900px) {
  ::-webkit-scrollbar { width: 10px; height: 10px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: var(--color-borde-suave);
    border-radius: 99px;
    border: 2px solid var(--color-fondo);
  }
  ::-webkit-scrollbar-thumb:hover { background: var(--color-acento); }
}

/* EL ANILLO DE FOCO, CON EL NARANJA QUE SÍ SE VE SOBRE PAPEL CLARO.
   Estaba en --color-energia (#FF5A1F), que en tema oscuro va sobrado —5,82 contra
   el fondo de página— pero en claro se queda en 2,82, por debajo del 3:1 que pide
   WCAG para un indicador de foco. Y con outline-offset:3px el anillo se apoya
   precisamente en el fondo de página, que es el peor de los cuatro casos.

   No se cambia a otro color: --color-energia-txt ya existe para este mismo problema
   —es el mismo naranja rebajado a #C24418 en claro y VOLTEADO al #FF5A1F original en
   oscuro— así que el foco queda en 4,59 en claro y 5,82 en oscuro sin inventar un
   token nuevo ni perder el color de la marca. Se descartó buscar un color único que
   valiera para los dos temas: los tres que pasaban (rojo, azul, violeta) o se leen
   como error o son colores que ya significan otra cosa en los gráficos. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 3px solid var(--color-energia-txt);
  outline-offset: 3px;
}

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

.contenedor {
  max-width: 480px;
  margin: 0 auto;
  padding: 16px 16px calc(var(--barra-inferior-alto) + 24px + env(safe-area-inset-bottom));
}

.contenedor-admin {
  max-width: 1100px;
  margin: 0 auto;
  padding: 16px 16px calc(var(--barra-inferior-alto) + 24px + env(safe-area-inset-bottom));
}

.contenedor-ancha {
  max-width: 480px;
  margin: 0 auto;
  padding: 16px 16px calc(var(--barra-inferior-alto) + 24px + env(safe-area-inset-bottom));
}

@media (min-width: 900px) {
  .contenedor { max-width: 560px; padding-bottom: 32px; }
  .contenedor-ancha { max-width: 1080px; padding-bottom: 32px; }
}

/* --- App shell: sidebar de escritorio para las vistas de promotor -------
   En móvil el sidebar no se muestra (navegación por barra inferior). En
   escritorio (>=900px) se reemplaza la barra inferior por un sidebar fijo,
   igual en espíritu al del panel admin, y el contenido usa un ancho amplio
   con grids en vez de simular una pantalla de celular. --- */
.app-layout { display: block; }
.app-sidebar { display: none; }
.app-main { min-width: 0; }

@media (min-width: 900px) {
  .app-layout.tiene-sidebar {
    display: flex;
    align-items: flex-start;
    max-width: 1300px;
    margin: 0 auto;
  }
  .app-sidebar {
    display: flex;
    flex-direction: column;
    gap: 2px;
    position: sticky;
    top: 0;
    height: 100vh;
    width: 250px;
    flex: 0 0 250px;
    background: var(--color-texto);
    color: #F8F5F1;
    padding: 18px 14px;
    overflow-y: auto;
  }
  .app-sidebar a {
    display: flex;
    align-items: center;
    gap: 10px;
    color: #F8F5F1;
    text-decoration: none;
    padding: 12px 10px;
    border-radius: var(--radio);
    border-left: 3px solid transparent;
    font-weight: 700;
    font-size: var(--txt-md, .9rem);
    transition: background-color .15s ease, border-color .15s ease;
  }
  .app-sidebar a:hover { background: rgba(255,255,255,.08); }
  .app-sidebar a.activo { background: rgba(255,90,31,.16); border-left-color: var(--color-energia); padding-left: 7px; color: #fff; }
  .app-sidebar hr { border-color: rgba(255,255,255,.15); margin: 16px 0; width: 100%; }
  .app-main { flex: 1; min-width: 0; }
  .app-layout.tiene-sidebar .topbar { display: none; }
  .app-layout.tiene-sidebar .contenedor { max-width: 1100px; padding: 24px 28px calc(var(--barra-inferior-alto)); }
  .bottom-nav { display: none; }
}

/* --- Barra superior --------------------------------------------------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 14px 16px;
  padding-top: calc(14px + env(safe-area-inset-top));
  background: var(--chrome-bg);
  color: var(--chrome-texto);
  border-bottom: 0;
  box-shadow: 0 4px 18px rgba(34, 34, 59, 0.18);
}
.topbar .marca {
  font-family: var(--fuente-titulo);
  font-weight: 700;
  font-size: 1.15rem;
  /* min-width:0 porque este span es un flex item DENTRO de .topbar y a la vez un
     flex CONTAINER: su texto pasa a ser un ítem anónimo dimensionado a su ancho
     máximo, y con el min-width:auto por defecto no podía encogerse. Por eso un
     texto con white-space:normal no se partía. Medido en la ficha de flooring a
     375 px: "Flooring · ALMACENES JACOME ARGENTINA Y 5 DE JUNIO - RIOBAMBA"
     ocupaba 617 px y arrastraba TODA la página a 675 sobre un viewport de 360. */
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--chrome-texto);
}
.topbar .icon-btn { color: var(--chrome-texto); }
@media (hover: hover) {
  .topbar .icon-btn:hover { background: rgba(247, 243, 235, 0.14); }
}
.topbar-acciones { display: flex; align-items: center; gap: 4px; }
.topbar-campana { position: relative; }
.topbar-campana-badge {
  position: absolute; top: 4px; right: 4px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px;
  /* COLOR FIJO A PROPÓSITO, sin token.
     El badge lleva texto BLANCO a 10 px: sobre el naranja puro #FF5A1F daba 3,12,
     por debajo del 4,5 que le toca a ese tamaño (medido en producción sobre la
     campanita del promotor, que marcaba "94"). Sobre #C24418 sube a 5,08.

     El primer intento fue --color-energia-txt, y el control en modo oscuro lo tumbó:
     ese token está pensado para TEXTO —oscuro sobre fondo claro, claro sobre fondo
     oscuro— así que en oscuro se voltea al #FF5A1F original y el badge volvía a 3,12.
     Como fondo de un texto blanco hace falta lo contrario: un tono que se mantenga
     oscuro en los dos temas. De ahí el literal. */
  background: #C24418; color: #fff;
  font-size: .62rem; font-weight: 800; line-height: 17px; text-align: center;
  border: 2px solid var(--chrome-bg, #22223B);
  box-sizing: content-box;
}
/* Pop al aparecer o subir el número (lo dispara app.js al refrescar en vivo) */
.topbar-campana-badge.es-pop { animation: campanaPop .45s var(--ease-out, cubic-bezier(.2, .8, .2, 1)); }
@keyframes campanaPop {
  0% { transform: scale(.4); }
  55% { transform: scale(1.25); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .topbar-campana-badge.es-pop { animation: none; }
}

/* --- Notificaciones (página) ----------------------------------------- */
.notif-retos { display: grid; gap: 12px; margin-bottom: 8px; }
.notif-reto {
  border: 2px solid var(--color-borde); border-radius: var(--radio-lg, 18px);
  background: var(--color-superficie); box-shadow: var(--sombra-dura-sm);
  padding: 14px;
}
.notif-reto-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.notif-reto-top strong { font-size: 1rem; }
.notif-reto-fechas { font-size: var(--txt-xs, .72rem); font-weight: 700; color: var(--color-texto-suave); display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.notif-reto-desc { margin: 4px 0 0; font-size: var(--txt-sm, .82rem); }
.notif-reto-marcador { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px; margin: 12px 0 8px; }
.notif-reto-equipo { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.notif-reto-equipo--b { align-items: flex-end; text-align: right; }
.notif-reto-nombre { font-size: .8rem; font-weight: 700; }
.notif-reto-puntos { font-family: var(--fuente-titulo); font-size: 1.6rem; font-weight: 800; line-height: 1; }
.notif-reto-equipo.es-lider .notif-reto-puntos { color: var(--color-acento); }
.notif-reto-vs { font-size: var(--txt-micro, .7rem); font-weight: 800; color: var(--color-texto-suave); }
.notif-reto-barra { display: flex; height: 12px; border-radius: 999px; overflow: hidden; border: 1px solid var(--color-borde); }
.notif-reto-barra-a { background: var(--color-acento); }
.notif-reto-barra-b { background: var(--color-energia, #FF5A1F); }
.notif-reto-pie { margin: 8px 0 0; font-size: var(--txt-sec, .78rem); }
.notif-reto-premio {
  display: inline-flex; align-items: center; gap: 6px; margin: 8px 0 0;
  padding: 6px 12px; border-radius: 999px; font-size: var(--txt-sec, .78rem); font-weight: 700;
  background: color-mix(in srgb, var(--color-acento) 16%, transparent);
  color: var(--color-acento);
}
.notif-reto-premio i { font-size: .95rem; }

.notif-lista { display: grid; gap: 10px; }
.notif-card {
  display: flex; gap: 12px; align-items: flex-start;
  border: 1px solid var(--color-borde); border-radius: var(--radio, 12px);
  background: var(--color-superficie); padding: 12px; text-decoration: none; color: inherit;
}
.notif-card.es-nueva { border-color: var(--color-acento); box-shadow: 0 0 0 1px var(--color-acento) inset; }
.notif-card.es-enlace { cursor: pointer; }
@media (hover: hover) { .notif-card.es-enlace:hover { background: var(--color-superficie-alt); } }

/* --- Swipe-para-borrar (estilo Gmail) --------------------------------- */
.notif-swipe-ayuda { display: flex; align-items: center; gap: 6px; margin: -4px 0 10px; font-size: var(--txt-sec, .78rem); }
.notif-swipe-ayuda i { color: var(--color-acento); font-size: 1rem; }
.ntf-swipe {
  position: relative;
  border-radius: var(--radio, 12px);
  overflow: hidden;
  transition: height .25s ease, opacity .25s ease, margin .25s ease;
}
.ntf-swipe-tras {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: flex-end;
  padding-right: 24px;
  background: var(--color-alerta-alto, #D7263D); color: #fff;
  font-size: 1.35rem; border-radius: var(--radio, 12px);
  opacity: 0; transition: opacity .12s ease;
}
.ntf-swipe.es-arrastrando .ntf-swipe-tras { opacity: 1; }
.ntf-swipe .notif-card {
  position: relative; z-index: 1;
  touch-action: pan-y;   /* el dedo puede seguir haciendo scroll vertical */
  user-select: none;
  -webkit-user-drag: none;
  will-change: transform;
}
.ntf-swipe.es-eliminada { opacity: 0; margin-bottom: -10px; }

/* Confirmación de borrado */
.ntf-confirm {
  border: 0; border-radius: var(--radio-lg, 18px);
  background: var(--color-superficie); color: var(--color-texto);
  padding: 24px 22px; max-width: 340px; width: calc(100vw - 48px);
  text-align: center; box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
}
.ntf-confirm::backdrop { background: rgba(34, 34, 59, .6); backdrop-filter: blur(2px); }
.ntf-confirm-icono {
  width: 56px; height: 56px; margin: 0 auto 10px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(215, 38, 61, .12); color: var(--color-alerta-alto, #D7263D);
  font-size: 1.5rem;
}
.ntf-confirm h3 { margin: 0 0 4px; font-size: 1.15rem; }
.ntf-confirm p { margin: 0 0 16px; font-size: .84rem; overflow-wrap: anywhere; }
.ntf-confirm-acciones { display: flex; flex-direction: column; gap: 8px; }
.ntf-confirm-acciones .btn { width: 100%; justify-content: center; }
.notif-card-icono {
  width: 40px; height: 40px; flex: 0 0 auto; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; font-size: 1.2rem;
  background: var(--color-superficie-alt); color: var(--color-acento);
}
.notif-card-icono--reto { background: #FDEBD3; color: var(--color-energia, #FF5A1F); }
.notif-card-cuerpo { flex: 1; min-width: 0; }
.notif-card-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.notif-card-top strong { font-size: var(--txt-md, .9rem); }
.notif-card-cuerpo p { margin: 3px 0 0; font-size: .84rem; color: var(--color-texto-suave); line-height: 1.4; }

/* --- Admin Notificaciones (pestañas + formularios) -------------------- */
.ntf-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.ntf-tab {
  flex: 0 0 auto; border: 1.5px solid var(--color-borde); background: var(--color-superficie);
  color: var(--color-texto); border-radius: 999px; padding: 9px 16px; font-weight: 700; font-size: .84rem;
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px; transition: background-color .12s ease, color .12s ease;
}
@media (hover: hover) { .ntf-tab:hover { background: var(--color-superficie-alt); } }
/* Píldora activa en naranja de marca: alto contraste y legible en ambos modos
   (antes usaba var(--color-texto), que en modo oscuro se veía gris con texto
   blanco casi perdido). */
/* Blanco sobre #FF5A1F daba 3,12:1 y el texto es de 13,4 px con peso 700 —por
   debajo del umbral de "texto grande"—, así que exige 4,5. Se baja al tono oscuro
   de la misma familia (#C24418 = --color-energia-txt): 5,08:1. Es el mismo arreglo
   que ya lleva .percha-tab.activo, y --color-energia NO se voltea en oscuro, así
   que fallaba en los dos temas. */
.ntf-tab.activo {
  background: var(--color-energia-txt, #C24418); color: #fff;
  border-color: var(--color-energia-txt, #C24418);
  box-shadow: 0 2px 10px rgba(194, 68, 24, .30);
}
.ntf-tab.activo i { color: #fff; }
.ntf-equipos { display: grid; gap: 12px; margin: 10px 0; }
@media (min-width: 620px) { .ntf-equipos { grid-template-columns: 1fr 1fr; } }
.ntf-equipo { border: 1px solid var(--color-borde); border-radius: var(--radio); padding: 12px; margin: 0; }
.ntf-equipo legend { font-weight: 800; font-size: var(--txt-sm, .82rem); padding: 0 6px; }
.ntf-ciudades-check { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 8px; }
.ntf-ciudades-check label { display: inline-flex; align-items: center; gap: 5px; font-size: .84rem; font-weight: 500; }
.ntf-fechas { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ntf-grid-2 { display: grid; grid-template-columns: 1fr; gap: 12px; }
@media (min-width: 620px) { .ntf-grid-2 { grid-template-columns: 1fr 1fr; } }
.ntf-reto-admin { border: 1px solid var(--color-borde); border-radius: var(--radio); padding: 12px; margin-bottom: 10px; }
.ntf-reto-admin-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ntf-reto-admin-score { font-size: .92rem; margin: 6px 0 0; }

/* --- Popup de bienvenida (mensaje del admin al iniciar sesión) --------- */
.popup-bienvenida {
  border: 2px solid var(--color-borde); border-radius: var(--radio-lg, 18px);
  box-shadow: var(--sombra-dura), 0 24px 48px rgba(34, 34, 59, 0.25);
  padding: 0; width: min(460px, calc(100vw - 24px)); max-height: calc(100dvh - 32px);
  overflow: hidden; background: var(--color-superficie); color: var(--color-texto);
}
.popup-bienvenida::backdrop { background: rgba(34, 34, 59, 0.6); backdrop-filter: blur(3px); }
.popup-bienvenida-inner { position: relative; display: flex; flex-direction: column; max-height: calc(100dvh - 32px); overflow-y: auto; }
.popup-bienvenida-cerrar {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 36px; height: 36px; border-radius: 50%; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; font-size: 1.1rem;
  background: rgba(34, 34, 59, .55); color: #fff;
}
/* La imagen/video se ajustan solos al ancho del móvil (auto). */
.popup-bienvenida-img { width: 100%; height: auto; display: block; max-height: 46vh; object-fit: contain; background: var(--color-superficie-alt); }
.popup-bienvenida-video { width: 100%; height: auto; display: block; max-height: 46vh; background: #000; }
.popup-bienvenida-cuerpo { padding: 16px 18px 20px; display: flex; flex-direction: column; gap: 8px; }
.popup-bienvenida-cuerpo h2 { margin: 0; font-size: 1.3rem; }
.popup-bienvenida-cuerpo p { margin: 0; font-size: var(--txt-md, .9rem); line-height: 1.5; color: var(--color-texto-suave); }
.popup-bienvenida-descarga { align-self: flex-start; margin-top: 4px; }
.popup-bienvenida-ok { margin-top: 6px; justify-content: center; }
.topbar .text-suave { color: rgba(247, 243, 235, 0.68); }
.punto {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--color-acento);
  display: inline-block;
}
/* Logo de marca (reemplaza el texto "TradeUp" + punto en topbar y sidebars). */
.marca-logo { height: 48px; width: auto; max-width: 230px; object-fit: contain; display: block; }
.topbar .marca { flex: 0 0 auto; }
/* El "0 0" de arriba existe para que el LOGO del promotor no se aplaste, así que no
   se toca. El título del panel es otra cosa —texto, no imagen— y sí debe ceder: sin
   esto no había min-width que valiera, porque flex-shrink:0 le prohibía encogerse
   por mucho min-width:0 que se le pusiera. Medido: 617 px de título arrastraban la
   página a 675 sobre un viewport de 360. */
.topbar .marca-titulo { flex: 0 1 auto; min-width: 0; }
.topbar .marca-titulo-txt { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Acciones pegadas al borde derecho POR COMPLETO: el margen negativo come todo
   el padding del topbar (16px); el aire que queda es solo el interno del
   icon-btn (icono centrado en 48px), sin achicar el área táctil. */
.topbar-acciones { margin-left: auto; margin-right: -16px; }
.admin-sidebar-marca .marca-logo { height: 44px; max-width: 210px; }
/* El icono cuadrado sólo se usa en el menú riel (lo enciende el bloque .rail-activo);
   por defecto —incluida la vista móvil— permanece oculto. */
.marca-logo-mini { display: none; }
.admin-sidebar-marca {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 22px;
  font-family: var(--fuente-titulo);
  font-weight: 700;
  font-size: 1.1rem;
}
.icon-btn {
  width: 48px; height: 48px;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: none; color: var(--color-texto);
  font-size: 1.4rem; cursor: pointer;
  border-radius: var(--radio);
  transition: background-color .15s ease;
}
@media (hover: hover) {
  .icon-btn:hover { background: var(--color-superficie-alt); }
}

/* --- Tarjetas / superficies -------------------------------------------- */
.tarjeta {
  background: var(--color-superficie);
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-md);
  box-shadow: var(--sombra-suave);
  padding: var(--e4);
  margin-bottom: 16px;
}
.tarjeta-plana {
  background: var(--color-superficie);
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-md);
  padding: var(--e4);
  margin-bottom: 12px;
  box-shadow: var(--sombra-suave);
}
/* La base "plana" NO se eleva: un bloque sin datos no es clicable, así que no
   debe fingir afordancia. Sólo .is-clickable recibe el empuje diagonal +
   sombra dura (mismo lenguaje que .btn / .almacen-card). */
.tarjeta-plana.is-clickable {
  cursor: pointer;
  transition: transform .1s ease, box-shadow .15s ease;
}
@media (hover: hover) {
  .tarjeta-plana.is-clickable:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0px var(--color-borde); }
}

/* --- Hero operativo / encabezados de página -----------------------------
   Desarrollado por Carlos David Donoso Cordero (ddchack). */
.page-hero {
  position: relative;
  overflow: hidden;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  background:
    linear-gradient(135deg, var(--color-superficie) 0%, var(--color-superficie) 62%, var(--color-alerta-fondo) 62%, var(--color-alerta-fondo) 100%);
  box-shadow: var(--sombra-dura);
  padding: 20px;
  margin-bottom: 18px;
}
.page-hero::after {
  content: "";
  position: absolute;
  right: -34px;
  top: -34px;
  width: 120px;
  height: 120px;
  border: 2px solid var(--color-borde);
  background:
    repeating-linear-gradient(135deg,
      transparent 0 10px,
      color-mix(in srgb, var(--color-acento) 36%, transparent) 10px 18px);
  transform: rotate(8deg);
  pointer-events: none;
}
.page-hero > * { position: relative; z-index: 1; }
.page-hero-main {
  display: grid;
  gap: 14px;
}
.page-hero-copy h1 {
  margin: 0 0 6px;
  font-size: clamp(1.8rem, 6vw, 3.1rem);
  letter-spacing: -0.02em;
}
.page-hero-copy p {
  margin: 0;
  max-width: 58ch;
  color: var(--color-texto-suave);
}
.page-hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}
.page-hero-actions .btn { width: auto; min-width: 190px; }
.hero-kpi-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px;
  margin-top: 16px;
}
.hero-kpi {
  min-width: 0;
  container-type: inline-size;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
  padding: 10px 11px;
}
/* KPI clicable (lleva a su sub-pestaña): se comporta como el resto pero con
   afordancia de enlace. La flecha se hereda en el <span> de la etiqueta. */
.hero-kpi--link { text-decoration: none; color: inherit; cursor: pointer; transition: transform .1s ease, border-color .12s ease; }
@media (hover: hover) { .hero-kpi--link:hover { transform: translateY(-1px); border-color: var(--color-acento); } }
.hero-kpi--link span i { font-size: .72em; opacity: .55; margin-left: 2px; vertical-align: middle; }
.hero-kpi strong {
  display: block;
  font-family: var(--fuente-titulo);
  font-size: clamp(1.1rem, 9cqw, 1.5rem);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hero-kpi > i {
  display: block;
  font-size: 1rem;
  color: var(--color-acento);
  margin-bottom: 2px;
}
.hero-kpi span {
  display: block;
  color: var(--color-texto-suave);
  font-size: var(--txt-xs, .72rem);
  font-weight: 700;
  letter-spacing: .04em;
  line-height: 1.15;
  margin-top: 5px;
  text-transform: uppercase;
}
.page-hero--field {
  background:
    linear-gradient(180deg, rgba(34, 34, 59, .56), rgba(34, 34, 59, .92)),
    url('/assets/img/brand/login-splash.jpg');
  background-size: cover;
  background-position: center;
  color: #F7F3EB;
}
.page-hero--field h1,
.page-hero--field .step-kicker { color: #F7F3EB; }
.page-hero--field .step-kicker::before { background: var(--color-energia); }
.page-hero--field p { color: rgba(247, 243, 235, .82); }
/* Las tres cifras sobre la foto del hero del promotor.
   El fondo va por TOKEN y no clavado: estaba en crema fijo mientras el texto
   usaba var(--color-texto), que en tema oscuro pasa a crema claro — resultado
   medido: 1,03:1, o sea números invisibles. Fondo y texto tienen que cambiar
   JUNTOS. (Tercera vez que aparece esta trampa: fondo claro fijo + texto
   heredado.) */
.page-hero--field .hero-kpi {
  background: var(--hero-chip-bg);
  color: var(--color-texto);
}
.page-hero--admin {
  background:
    linear-gradient(135deg, var(--hero-admin-bg) 0%, var(--hero-admin-bg) 58%, var(--color-acento) 58%, var(--color-acento) 100%);
  color: var(--chrome-texto);
}
.page-hero--admin h1,
.page-hero--admin .step-kicker { color: var(--chrome-texto); }
.page-hero--admin .step-kicker::before { background: var(--color-energia); }
.page-hero--admin p { color: rgba(247, 243, 235, .78); }
.page-hero--admin .hero-kpi {
  background: var(--color-superficie);
  color: var(--color-texto);
}
.page-hero--compact .page-hero-copy h1 { font-size: clamp(1.55rem, 4vw, 2.25rem); }
/* Cabecera de página ligera: kicker + h1 + acciones, sin franja de KPIs.
   Comparte inset, borde, margen y tratamiento con .page-hero; combinable con
   .page-hero--admin y .page-hero--compact.
   Desarrollado por Carlos David Donoso Cordero (ddchack). */
.page-hero--plain .page-hero-main { gap: 10px; }
.page-hero--plain .page-hero-copy h1 { margin-bottom: 0; }
.page-hero--auth {
  background:
    linear-gradient(135deg, var(--color-superficie) 0 64%, var(--color-alerta-fondo) 64% 100%);
}
.page-hero .wizard-header {
  margin: 16px 0 0;
}
.page-hero .wizard-paso-txt {
  margin-bottom: 10px;
}
.auth-card {
  border-width: 2px;
  box-shadow: var(--sombra-dura);
}
.hero-status-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  width: fit-content;
  border: 2px solid var(--color-borde);
  border-radius: 99px;
  background: var(--color-superficie);
  color: var(--color-texto);
  box-shadow: var(--sombra-dura-sm);
  padding: 8px 12px;
  font-size: var(--txt-sec, .78rem);
  font-weight: 800;
}
.page-section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin: 18px 0 10px;
}
.page-section-head h2,
.page-section-head h3 { margin: 0; }
.page-section-head p { margin: 3px 0 0; color: var(--color-texto-suave); font-size: .88rem; }
.insight-grid {
  display: grid;
  gap: 16px;
}
.filtro-panel {
  border: 2px solid var(--color-borde);
  box-shadow: var(--sombra-dura);
  background: var(--color-superficie);
}
.filtro-panel-grid {
  display: grid;
  gap: 0 14px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.filtro-campo { min-width: 0; }
/* Aviso de "no puedo saber en qué tienda estás" en el inicio del promotor.
   Reusa .tienda-hoy y sólo cambia el borde: es la MISMA caja que normalmente
   trae la gente de la tienda, así que el promotor entiende de qué le hablan. */
.tienda-hoy--aviso { border-left: 7px solid var(--color-alerta-medio, #A25C00); }
.tienda-hoy--aviso .tienda-hoy-lead i { color: var(--color-alerta-medio, #A25C00); }

/* Las visitas abiertas, como botones dentro del aviso. Cada uno tiene que ser
   cómodo de acertar con el pulgar: se toca de pie, en la tienda. */
.vctx-opciones { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 6px; }
.vctx-op {
  display: inline-flex; align-items: center; gap: 7px; min-height: 44px;
  text-align: left; text-decoration: none;
}
.vctx-op small { font-weight: 500; }

/* La tienda donde el promotor tiene el check-in, arriba de la lista. */
.almacen-ficha--aqui { border-left: 7px solid var(--color-acento); }
.almacen-aqui {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .68rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-acento-fuerte); margin-bottom: 2px;
}

/* --- Ruta del día (Cobertura) -------------------------------------------- */
.ruta-dia { margin-bottom: 16px; }
.ruta-dia h3 { display: flex; align-items: center; gap: 8px; }
.rd-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin-bottom: 12px; }
.rd-kpi {
  border: 2px solid var(--color-texto); border-radius: var(--radio, 10px);
  background: var(--color-fondo-tarjeta, #fff); padding: 9px 12px;
  box-shadow: 2px 2px 0 var(--color-texto);
}
/* MISMO IDIOMA QUE LOS DEMAS INDICADORES. Medido en seis pantallas del panel:
   el numero grande es 1,5rem / 800 / fuente de titulo y la etiqueta .72rem / 700
   en versalitas (sd-kpi, sa-kpi, rep-kpi, hero-kpi, pendiente-cifra). Estos eran
   los unicos a 1,25rem, con la fuente del cuerpo y una etiqueta sin versalitas:
   la misma cifra se leia de dos formas distintas segun la pantalla, que es
   exactamente lo que hace que un panel parezca una suma de piezas. */
.rd-kpi b {
  display: block;
  font-family: var(--fuente-titulo);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.rd-kpi span {
  font-size: var(--txt-xs, .72rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--color-texto-suave, #55556B);
}
/* El traslado es el número que se mira: se resalta sin gritar. */
.rd-kpi--traslado { border-left-width: 7px; border-left-color: var(--color-alerta-medio, #A25C00); }

.rd-tabla { font-size: .84rem; }
.rd-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Celda numérica genérica de tabla. Existía el `class="num"` en el HTML de
   admin/diagnostico-hora.php desde hacía tiempo, pero NO la regla: las columnas
   de cifras salían alineadas a la izquierda y con dígitos de ancho variable, que
   es justo lo que impide comparar una columna de un vistazo. */
/* También .tabla-admin, que es la otra clase base de tabla del panel. Medido en
   el navegador: en admin/sell-analitica.php las celdas `class="num"` salían
   alineadas a la IZQUIERDA porque la regla sólo cubría `.tabla`, y CSS no casa
   `.tabla` dentro de `tabla-admin` —son dos tokens distintos—. La clase estaba
   puesta en el marcado y no hacía nada.
   Y no es cosmético: dos cifras de una misma columna que no comparten el margen
   derecho no se pueden comparar de un vistazo, que es para lo que existe la
   columna. */
.tabla td.num, .tabla th.num,
.tabla-admin td.num, .tabla-admin th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.rd-sub {
  display: block; font-size: .66rem; font-weight: 500; text-transform: none;
  letter-spacing: 0; color: var(--color-texto-suave, #55556B);
}
.rd-nota {
  display: inline-block; font-size: .68rem; font-weight: 700; border-radius: 999px;
  padding: 1px 8px; margin-left: 6px;
  background: var(--color-borde-suave, #E2DACB); color: var(--color-texto-suave, #55556B);
}
.rd-nota--abierta { background: var(--color-alerta-fondo, #FFF1DC); color: var(--color-alerta-medio, #A25C00); }

/* Chincheta numerada de cada parada. */
.rd-pin span {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: #6B4FBB; color: #fff; font-weight: 800; font-size: .8rem;
  border: 2px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,.4);
  font-variant-numeric: tabular-nums;
}

/* --- Buscador automático del catálogo del admin -------------------------- */
.prod-buscar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.prod-buscar-campo { position: relative; flex: 1 1 320px; min-width: 0; display: flex; align-items: center; }
.prod-buscar-campo > i {
  position: absolute; left: 12px; pointer-events: none;
  color: var(--color-texto-suave, #55556B); font-size: 1rem;
}
.prod-buscar-campo input { margin: 0; padding-left: 38px; padding-right: 36px; width: 100%; }
/* Señal de que la búsqueda salió: sin ella, medio segundo de espera con la
   página quieta se lee como que el buscador no hace nada. */
.prod-buscar-spin {
  position: absolute; right: 12px; width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid var(--color-borde-suave, #E2DACB);
  border-top-color: var(--color-acento, #E9940A);
  animation: prodSpin .7s linear infinite;
}
@keyframes prodSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .prod-buscar-spin { animation: none; } }
.prod-buscar-estado { flex: 1 0 100%; margin: 0; font-size: var(--txt-sm, .82rem); }

/* La línea de cierre de "Acción requerida hoy": lo que está en cero, resumido.
   Sin botón, porque no hay nada que revisar. */
.pendiente-item--ok > div { display: flex; align-items: center; gap: 8px; }
.pendiente-item--ok i { color: var(--color-alerta-bajo, #1B7A43); font-size: 1.15rem; flex: 0 0 auto; }

.filtro-campo--limpiar { display: flex; align-items: flex-end; }
.filtro-campo--limpiar .btn { margin-bottom: 2px; }

/* Chips de "lo que está puesto ahora mismo". Sin ellos, el corte que se está
   viendo sólo se averiguaba abriendo cada desplegable uno a uno — y quien llega
   desde un enlace con filtros ve una tabla recortada sin explicación. */
.filtros-puestos {
  display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
  margin: -6px 0 14px;
}
.rot-filtros {
  font-size: var(--txt-xs, .72rem); letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; color: var(--color-texto-suave, #55556B);
}
.chip-puesto {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .8rem; font-weight: 600; line-height: 1.2;
  border: 2px solid var(--color-texto); border-radius: 999px;
  padding: 4px 6px 4px 11px; background: var(--color-fondo-tarjeta, #fff);
}
.chip-puesto-k {
  font-weight: 500; font-size: .68rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--color-texto-suave, #55556B);
}
/* La ✕ es un enlace, y tiene que ser cómoda de acertar también con el ratón. */
.chip-puesto-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 50%; flex: 0 0 auto;
  text-decoration: none; font-size: 1rem; line-height: 1;
  color: var(--color-texto-suave, #55556B); background: var(--color-borde-suave, #E2DACB);
}
.chip-puesto-x:hover, .chip-puesto-x:focus-visible {
  background: var(--color-alerta-alto, #D7263D); color: #fff;
}
.chip-puesto-x:focus-visible { outline: 2px solid var(--color-texto); outline-offset: 2px; }

/* Modal de foto de fachada: la ubicación GPS es la acción principal; "solo la
   foto" queda como enlace secundario para no dejar almacenes sin dirección. */
.mf-gps-acciones { display: flex; flex-direction: column; align-items: stretch; gap: 8px; }
.mf-gps-acciones .btn-primario { width: 100%; justify-content: center; }
.mf-gps-solofoto {
  border: none; background: none; color: var(--color-texto-suave);
  font-size: var(--txt-sm, .82rem); text-decoration: underline; cursor: pointer;
  font-family: var(--fuente-cuerpo); padding: 4px;
}
@media (hover: hover) { .mf-gps-solofoto:hover { color: var(--color-texto); } }
.filtro-panel label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 0;
}
.chart-shell {
  display: grid;
  gap: 16px;
}
@media (min-width: 980px) {
  .chart-shell { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}
@media (min-width: 980px) {
  .page-hero { padding: 26px 28px; }
  .page-hero-main {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
  }
  .insight-grid { grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); align-items: start; }
}
@media (max-width: 520px) {
  .page-hero { padding: 18px 16px; border-radius: var(--radio); }
  .page-hero-actions .btn { width: 100%; }
  .hero-kpi-strip { grid-template-columns: 1fr; }
  .page-hero--field .hero-kpi-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .page-hero--admin .hero-kpi-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .page-hero--field .hero-kpi { padding: 9px 7px; }
  .page-section-head { align-items: flex-start; flex-direction: column; }
}

/* --- Botones ------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  padding: 12px 20px;
  /* SIN ESTO, UN <a class="btn"> Y UN <button class="btn"> NO MIDEN IGUAL.
     El botón trae el line-height del navegador (~1.2) y se queda en 43 px, así que
     manda el min-height y sale a 48. El enlace hereda el del cuerpo (1.55 = 24,8 px)
     y llega a 53: rebasa el mínimo y crece. Cinco píxeles de diferencia entre dos
     piezas que se ponen una al lado de la otra —"Aplicar" y "Limpiar", "Ver ficha"
     y "Exportar"— y que el ojo espera idénticas. Medido en cobertura (53 vs 48) y
     en el inicio del panel (53 vs 48).
     1.2 y no otro valor: es el que ya usaban los <button>, así que los deja donde
     estaban y baja los enlaces hasta ellos. Con .btn-sm ambos siguen en 48 por el
     min-height, que es lo que ya pasaba. */
  line-height: 1.2;
  font-family: var(--fuente-cuerpo);
  font-weight: 700;
  font-size: 1rem;
  border-radius: var(--radio);
  border: 2px solid var(--color-borde);
  background: var(--color-superficie);
  color: var(--color-texto);
  cursor: pointer;
  text-decoration: none;
  box-shadow: var(--sombra-dura-sm);
  transition: transform .1s ease, box-shadow .15s ease, background-color .15s ease, opacity .15s ease;
  width: 100%;
}
.btn:active { transform: translate(2px, 2px); box-shadow: none; }
@media (hover: hover) {
  .btn:not(:disabled):hover {
    transform: translate(-1px, -1px);
    box-shadow: 5px 5px 0px var(--color-borde);
  }
  .btn-fantasma:not(:disabled):hover { box-shadow: none; transform: none; background: rgba(34,34,59,.06); }
}
.btn-primario {
  background: var(--color-acento);
  color: #22223B;
  border-color: var(--color-borde);
}
/* UN SOLO COLOR PARA LA ACCIÓN PRINCIPAL.
   Había DOS clases compitiendo por el mismo papel: .btn-energia pintaba el naranja
   rojizo #FF5A1F y .btn-primario el ámbar #E9940A, y ambas se usaban en acciones
   finales de flujo sin ninguna regla que las separase — "Guardar precio", "Guardar
   registro", "Guardar venta", "Enviar" y "Check-in" en energía; "Agregar almacén",
   "Guardar almacén" y "Registrar primer precio" en primario. En el inicio del
   promotor se veían las dos a la vez: el CTA del héroe casi rojo y el botón del
   estado vacío ámbar, a 400 px uno del otro.

   Se unifica en el ámbar (--color-acento), que además contrasta mejor con el texto
   navy: 6,40 contra 4,95. Se hace AQUÍ y no renombrando la clase en los 24 sitios
   que la usan: menos churn, cero riesgo de dejarse uno a medias, y el día que se
   quiera separar de verdad los dos papeles basta con volver a darle color propio.
   --color-energia sigue existiendo y sigue usándose donde SÍ significa algo: el
   badge de la campanita, los acentos de alerta y el tinte de la marca. */
.btn-energia {
  background: var(--color-acento);
  color: #22223B;
  border-color: var(--color-borde);
}
.btn-fantasma {
  background: transparent;
  box-shadow: none;
}

/* Nota de cierre de una alerta: plegada hasta que se pulsa "Resolver". La clase la
   pone el JS de admin/formularios.php; sin JS el campo se ve, que es el estado
   seguro. Se usa display:none y no visibility/opacity porque el campo debe salir
   del flujo: lo que se busca es recuperar los ~50 px que ocupaba en CADA alerta. */
.al-form--plegado .al-nota { display: none; }

/* --- Frase del día del promotor ------------------------------------------- */
/* ES UNA TARJETA, no una píldora suelta. Nació como un óvalo fino y quedaba fuera
   del sistema: toda la pantalla del promotor son .tarjeta con su borde y su sombra,
   y ese óvalo flotando entre la gamificación y el héroe rompía el ritmo — se leía
   como un aviso del navegador, no como parte de la app. Con el rótulo encima ocupa
   ~30 px más, y ese es el precio de que parezca lo que es: una cita del día.

   El overflow vive en .frase-dia-carril y no en la tarjeta: si lo pusiera en la
   tarjeta, recortaría también su sombra y su borde redondeado. */
/* MISMO CONTORNO QUE LA TARJETA DE GAMIFICACION que va justo encima. Lleva la clase
   .tarjeta, pero .tarjeta usa borde SUAVE, radio de 12 px y sombra blanda, mientras
   .gamif-topbar usa --color-borde, --radio-lg (18) y sombra DURA. Puestas una sobre
   otra en la misma pantalla, la diferencia se nota aunque no se sepa nombrar: dos
   piezas que deberian ser hermanas parecian de dos sitios. Se copian los tres
   tokens, no valores sueltos, para que sigan al tema. */
.frase-dia {
  padding: 10px 12px;
  border-color: var(--color-borde);
  border-radius: var(--radio-lg, 18px);
  box-shadow: var(--sombra-dura-sm);
}
.frase-dia-rotulo {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--txt-micro, .72rem); font-weight: 800;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--color-acento-fuerte);
}
.frase-dia-rotulo i { font-size: .95rem; }
/* UNA SOLA LINEA para la frase, pase lo que pase. El carril fija su alto al de una
   linea de texto: si algo hiciera envolver la frase —una fuente mas ancha, un zoom
   del sistema— la tarjeta seguiria midiendo lo mismo y la frase seguiria desfilando
   en vez de partirse en dos. Con el rotulo encima son dos lineas exactas. */
.frase-dia-carril {
  overflow: hidden;
  margin-top: 3px;
  height: calc(var(--txt-peq, .85rem) * 1.6);
  display: flex; align-items: center;
}
.frase-dia-pista {
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  will-change: transform;
  /* La duracion la pone el PHP segun el largo de la frase (--frase-dur): con un
     valor fijo, una frase corta y una larga recorren distancias muy distintas en el
     mismo tiempo y la corta se arrastra. El respaldo de 16 s es para el caso de que
     la variable no llegue. */
  animation: fraseDiaPasa var(--frase-dur, 16s) linear infinite;
}
/* LAS TRES COPIAS, CON EXACTAMENTE EL MISMO ESTILO.
   El carril lleva la frase tres veces: la del marcado y dos que genera el CSS. Tienen
   que medir lo MISMO al píxel o el bucle se desfasa — y se desfasó dos veces mientras
   se montaba esto: primero porque las comillas del span iban en pseudo-elementos y
   las de las copias en la cadena de content (10 px), y luego porque el ::before se
   quedó fuera de la regla compartida y no recibía ni relleno ni tamaño (15 px). Por
   eso los tres selectores van juntos y las comillas se componen en PHP: una sola
   cadena, tres veces la misma.

   Y por eso las copias las pone el CSS y no el HTML: el día que esta hoja no llegue
   —un despliegue a medias, el service worker con una versión vieja— el promotor ve
   la frase UNA vez como texto normal y no tres seguidas. Pasó en producción.

   La cursiva y las angulares son lo que la hace sonar a cita y no a instrucción. */
.frase-dia-txt,
.frase-dia-pista::before,
.frase-dia-pista::after {
  display: inline-flex; align-items: center;
  padding-right: 3rem;
  font-size: var(--txt-peq, .85rem); font-weight: 600; font-style: italic;
  color: var(--color-texto);
  white-space: nowrap;
}
.frase-dia-pista::before,
.frase-dia-pista::after { content: attr(data-frase); }
/* -33.3333% y no -50%: el carril lleva TRES copias idénticas, así que un tercio del
   recorrido es exactamente una de ellas y la siguiente queda donde estaba la
   anterior. Con -50% y tres copias el salto caería a media frase. */
@keyframes fraseDiaPasa {
  from { transform: translateX(0); }
  to   { transform: translateX(-33.3333%); }
}
/* En escritorio se acota la tarjeta, no se para la marquesina: si la frase es larga
   TIENE que desfilar, nunca recortarse. Acotando el carril, la pista siempre mide
   más que él y el bucle funciona en cualquier ancho. */
@media (min-width: 900px) {
  .frase-dia { max-width: 520px; margin-left: auto; margin-right: auto; }
}
/* CON «REDUCIR ANIMACIONES» NO SE MUEVE, PERO EL ALTO NO CAMBIA.
   Una línea con puntos suspensivos DE VERDAD —el texto pasa a block, porque el
   text-overflow sólo existe en contenedores de bloque y con inline-flex la frase se
   cortaba en seco— y la frase entera queda en el title de la tarjeta. */
@media (prefers-reduced-motion: reduce) {
  .frase-dia-pista { animation: none; display: block; }
  .frase-dia-pista::before,
  .frase-dia-pista::after { display: none; }
  .frase-dia-txt { display: block; padding-right: 0;
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* --- Cabecera del perfil: la ficha del promotor ---------------------------- */
/* La foto no se encoge y el texto sí: con nombres largos —"Guaman Mendoza Laura
   Alexandra"— el flex apretaba el avatar hasta convertirlo en una elipse. */
.perfil-hero-id { display: flex; align-items: center; gap: var(--esp-md); min-width: 0; }
.perfil-hero-foto { flex: 0 0 auto; }
.perfil-hero-txt { min-width: 0; }
.perfil-hero-txt h1 { font-size: clamp(1.35rem, 5vw, 1.9rem); line-height: 1.15; }
.perfil-hero-meta {
  display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 6px;
  font-size: var(--txt-peq, .85rem); color: rgba(247, 243, 235, .82);
}
.perfil-hero-meta span { display: inline-flex; align-items: center; gap: 5px; }
.perfil-hero-meta i { color: var(--color-acento); }
.perfil-hero .avatar-grande { width: 72px; height: 72px; border: 3px solid rgba(255,255,255,.88); box-shadow: 0 4px 14px rgba(0,0,0,.22); }

/* Acciones que la persona busca primero. Evitan recorrer toda la ficha para
   encontrar los tres acordeones de edición. */
.perfil-atajos {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--esp-sm); margin: -2px 0 16px;
}
.perfil-atajos a {
  min-width: 0; display: grid; grid-template-columns: 38px minmax(0, 1fr);
  grid-template-rows: auto auto; column-gap: 9px; align-items: center;
  padding: 10px 12px; color: inherit; text-decoration: none;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md);
  background: var(--color-superficie); transition: transform .15s, border-color .15s;
}
.perfil-atajos a > span {
  grid-row: 1 / 3; width: 38px; height: 38px; display: inline-flex;
  align-items: center; justify-content: center; border-radius: 11px;
  background: var(--color-superficie-alt); color: var(--color-acento-fuerte); font-size: 1.05rem;
}
.perfil-atajos strong { font-size: var(--txt-sm, .82rem); line-height: 1.15; }
.perfil-atajos small { min-width: 0; color: var(--color-texto-suave); font-size: .68rem; line-height: 1.2; }
.perfil-atajos a:hover { transform: translateY(-1px); border-color: var(--color-energia); }
@media (max-width: 520px) {
  .perfil-atajos { gap: 7px; }
  .perfil-atajos a { display: flex; flex-direction: column; gap: 5px; padding: 9px 5px; text-align: center; }
  .perfil-atajos a > span { width: 36px; height: 36px; }
  .perfil-atajos small { display: none; }
}

.al-nota { display: inline-flex; min-width: 0; flex: 1 1 190px; }
.al-nota input { width: 100%; }

/* UN BOTÓN FANTASMA SOBRE LA CABECERA OSCURA ERA INVISIBLE.
   .btn-fantasma sólo quita el fondo: hereda de .btn el color de texto y de borde,
   que son --color-texto (#22223B). Y la cabecera de admin es un degradado
   135deg #22223B 0-58% / #E9940A 58-100%. O sea que todo botón fantasma que caiga
   en la primera franja es azul marino sobre azul marino: contraste 1,00, no se ve
   NADA. Medido en producción en formularios.php sobre "Configurar formularios",
   que se veía como una caja vacía; "Acerca de", a su lado, sí se leía sólo porque
   cae del lado naranja. Pasaba en 8 pantallas del panel, 9 botones.

   Por qué un fondo y no sólo aclarar el texto: el crema da 13,55 sobre el azul pero
   2,12 sobre el naranja — cambiar el color de letra sólo movería el problema de
   franja. Con el azul al 55 % debajo, el botón se funde con la franja oscura (sigue
   pareciendo fantasma) y se convierte en pastilla legible sobre la naranja:
   13,55 y 5,79. */
.page-hero--admin .btn-fantasma {
  background: rgba(34, 34, 59, .55);
  color: var(--chrome-texto);
  border-color: rgba(247, 243, 235, .45);
}
@media (hover: hover) {
  .page-hero--admin .btn-fantasma:hover {
    background: rgba(34, 34, 59, .78);
    border-color: var(--chrome-texto);
  }
}
.btn-peligro { background: var(--color-alerta-alto); color: #fff; }
.btn-sm { min-height: 48px; padding: 8px 14px; font-size: var(--txt-md, .9rem); width: auto; }
.btn[disabled], .btn:disabled {
  cursor: wait;
  opacity: .75;
  transform: none;
}

.spinner {
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  display: inline-block;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* --- Formularios --------------------------------------------------------- */
label {
  display: block;
  font-weight: 700;
  font-size: .85rem;
  margin: 14px 0 6px;
  color: var(--color-texto);
}
input[type=text], input[type=email], input[type=password], input[type=tel],
input[type=number], input[type=search], input[type=date], input[type=month], select, textarea {
  width: 100%;
  min-height: 48px;
  padding: 10px 12px;
  font-size: 16px;
  font-family: var(--fuente-cuerpo);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  color: var(--color-texto);
  transition: border-color .15s ease;
}
input[type=text]:focus, input[type=email]:focus, input[type=password]:focus,
input[type=tel]:focus, input[type=number]:focus, input[type=search]:focus,
input[type=date]:focus, input[type=month]:focus,
select:focus, textarea:focus {
  border-color: var(--color-acento-fuerte);
}
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] {
  border-color: var(--color-alerta-alto);
  box-shadow: inset 4px 0 0 var(--color-alerta-alto);
}
textarea { min-height: 90px; }
.ayuda { font-size: .8rem; color: var(--color-texto-suave); margin-top: 4px; }
.check-linea {
  display: flex; align-items: flex-start; gap: 10px; margin: 16px 0;
}
.check-linea input { width: 22px; height: 22px; margin-top: 2px; }

/* --- Alertas / flash ------------------------------------------------------ */
.alerta {
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  padding: 12px 14px;
  margin-bottom: 14px;
  font-weight: 600;
}
.alerta-error { background: #FDE2E1; border-color: var(--color-alerta-alto); color: #7A0E14; }
.alerta-exito { background: #E1F3E7; border-color: var(--color-alerta-bajo); color: #123D22; }
.alerta-info { background: var(--color-alerta-fondo); border-color: var(--color-acento); color: var(--color-acento-fuerte); }

/* --- Toasts (avisos flotantes con auto-cierre) --------------------------- */
.toast-contenedor {
  position: fixed;
  left: 50%;
  bottom: calc(var(--barra-inferior-alto) + 16px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  /* 90 y no 60: con 60 empataba con .hoja-ov y .modal-ts-ov, y en un empate manda el
     orden del documento. El contenedor de avisos se crea la PRIMERA vez que sale uno,
     asi que si ya habia salido alguno antes quedaba por delante del modal en el DOM y
     el modal lo tapaba. Resultado: el promotor pulsaba "Guardar" con la hoja abierta,
     el aviso de "no tengo tu ubicacion" se dibujaba DEBAJO y para el no pasaba nada.
     Un aviso es la ultima capa de la interfaz: si algo lo tapa, deja de existir. */
  z-index: 90;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: min(440px, calc(100vw - 24px));
  pointer-events: none;
}
@media (min-width: 900px) {
  .toast-contenedor { bottom: 24px; left: auto; right: 24px; transform: none; }
}
/* Con el wizard de registro abierto el toast sube por encima de su botonera.
   Sin esto se tapan: .wizard-actions es sticky abajo (z-index 18) y el toast va
   fijo 6 px más abajo con z-index 60, así que el aviso caía JUSTO encima del
   botón que el promotor acaba de tocar. Medido a 375x812: la barra ocupa
   [666,738] y el toast del error de GPS [626,746] — la tapa entera.
   El toast crece hacia ARRIBA desde su borde inferior, así que basta con subir
   ese borde por encima de 812-666 = 146 px; 64+94 = 158 dejan 12 px de aire y no
   dependen de cuántas líneas ocupe el mensaje.
   Sólo por debajo de 900 px: arriba el contenedor se va abajo-derecha y no
   compite con nada. La clase la pone wizard.js en el <body>. */
@media (max-width: 899px) {
  body.wizard-abierto .toast-contenedor {
    bottom: calc(var(--barra-inferior-alto) + 94px + env(safe-area-inset-bottom));
  }
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: 2px solid var(--color-borde);
  border-left-width: 6px;
  border-radius: var(--radio);
  background: var(--color-superficie);
  color: var(--color-texto);
  box-shadow: var(--sombra-dura);
  padding: 12px 12px 12px 14px;
  font-weight: 700;
  font-size: .92rem;
  animation: toast-in .22s ease;
}
.toast .toast-icono { font-size: 1.3rem; line-height: 1.2; flex: 0 0 auto; }
.toast .toast-cuerpo { flex: 1; min-width: 0; padding-top: 1px; }
.toast .toast-cerrar {
  flex: 0 0 auto;
  width: 32px; height: 32px; min-height: 32px;
  margin: -4px -4px -4px 0;
  font-size: 1rem;
  color: var(--color-texto-suave);
}
.toast-exito { border-left-color: var(--color-alerta-bajo); }
.toast-exito .toast-icono { color: var(--color-alerta-bajo); }
.toast-error { border-left-color: var(--color-alerta-alto); }
.toast-error .toast-icono { color: var(--color-alerta-alto); }
.toast-info { border-left-color: var(--color-acento); }
.toast-info .toast-icono { color: var(--color-acento-fuerte); }
.toast.saliendo { animation: toast-out .2s ease forwards; }
@keyframes toast-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: translateY(0); } }
@keyframes toast-out { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(14px); } }
@media (prefers-reduced-motion: reduce) {
  .toast, .toast.saliendo { animation: none; }
}

/* --- Grid de marcas -------------------------------------------------------- */
/* minmax(0, 1fr) y no 1fr a secas. UN 1fr LLEVA UN MÍNIMO IMPLÍCITO DE auto, o sea
   que la columna se niega a encogerse por debajo del contenido más ancho que tenga
   dentro. Con marcas como "CONTINENTAL" —una palabra que no parte— las tres columnas
   salían a 113 px: 3x113 + 2x10 de hueco = 359 sobre un contenedor de 328. Medido en
   producción a 375 px: las teselas llegaban a x=358 cuando la tarjeta acaba en 344,
   así que la tercera columna se comía el margen derecho y quedaba pegada al borde de
   la pantalla. El minmax(0,...) permite que la pista baje de su min-content, y el
   overflow-wrap parte el nombre largo en vez de estirarlo. */
.grid-marcas {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}
.marca-tile { min-width: 0; overflow-wrap: anywhere; }
@media (min-width: 900px) {
  .grid-marcas { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
}

/* --- Listas que pasan a grid multi-columna en escritorio -------------------- */
@media (min-width: 900px) {
  .lista-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 12px;
    align-items: start;
  }
  .lista-grid .almacen-card,
  .lista-grid .almacen-ficha,
  .lista-grid .tarjeta-plana { margin-bottom: 0; }

  /* Feed de inicio: TODAS las cards con la MISMA altura. grid-auto-rows:1fr iguala
     las filas a la más alta; la card llena su celda y el chip de marca se ancla
     abajo (margin-top:auto) para que el relleno extra se vea intencional. */
  .lista-grid--feed { align-items: stretch; grid-auto-rows: 1fr; }
  .lista-grid--feed .feed-card { height: 100%; align-items: stretch; }
  .lista-grid--feed .feed-card .info { display: flex; flex-direction: column; }
  .lista-grid--feed .feed-card .info .feed-marca { margin-top: auto; }

  /* Marcas: mismo tratamiento. Sin esto las tarjetas salían de dos alturas
     distintas segun lo largo que fuera el nombre del distribuidor. */
  .lista-grid--marcas { align-items: stretch; grid-auto-rows: 1fr; }
}

/* --- Layout de 2 columnas en escritorio (ej. perfil) ------------------------ */
.dos-columnas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 16px;
  align-items: start;
}
.marca-tile {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 14px 6px;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
  font-weight: 700; font-size: .85rem; text-align: center;
  cursor: pointer;
  min-height: 92px;
  color: var(--color-texto);
  font-family: var(--fuente-cuerpo);
  transition: transform .1s ease, box-shadow .15s ease;
}
@media (hover: hover) {
  .marca-tile:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0px var(--color-borde); }
}
.marca-tile.activa { background: var(--color-acento); color: var(--color-texto); }
.marca-tile.dashed { border-style: dashed; }
.marca-tile .inicial {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--color-texto); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--fuente-titulo);
}
.marca-tile-logo {
  width: 40px;
  height: 40px;
  object-fit: contain;
}
.marca-tile.activa .inicial {
  background: var(--color-superficie);
  color: var(--color-texto);
}

/* --- Wizard / stepper ------------------------------------------------------ */
.wizard-progreso {
  height: 8px; background: var(--color-borde-suave); border-radius: 99px;
  overflow: hidden; margin-bottom: 6px;
}
.wizard-progreso > div {
  height: 100%;
  background: linear-gradient(90deg, var(--color-acento), var(--color-energia));
  border-radius: 99px;
  transition: width .35s var(--ease-out);
}
.wizard-paso-txt {
  font-size: .8rem; font-weight: 700; color: var(--color-texto-suave);
  margin-bottom: 16px;
}
.wizard-header { margin-bottom: 16px; }
.wizard-steps {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 6px;
  margin-top: 10px;
}
.wizard-step {
  min-height: 42px;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-superficie);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--sombra-suave);
  font-size: .95rem;
  transition: border-color .15s ease, background-color .15s ease;
}
.wizard-step.activo { background: var(--color-acento); border-color: var(--color-acento-fuerte); }
.wizard-step.completo { background: #E1F3E7; border-color: var(--color-alerta-bajo); }
.wizard-alert { margin: 0 0 14px; }
.wizard-alert:empty { display: none; }

/* --- Banner de "modo visita" (varios registros seguidos en el mismo almacén) */
.visita-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  border: 2px solid var(--color-energia);
  border-radius: var(--radio-lg);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
  padding: 12px 14px;
  margin-bottom: 14px;
}
.visita-banner-info {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
.visita-banner-info > i {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--color-energia);
  color: #FFFFFF;
  font-size: 1.15rem;
}
.visita-banner-info div { min-width: 0; }
.visita-banner-info strong {
  display: block;
  font-family: var(--fuente-titulo);
  font-size: .95rem;
}
.visita-banner-info span {
  display: block;
  font-size: var(--txt-sm, .82rem);
  color: var(--color-texto-suave);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.visita-banner-form { margin: 0; flex: 0 0 auto; }
.visita-banner-form .btn { width: auto; }
@media (max-width: 480px) {
  .visita-banner { flex-direction: column; align-items: stretch; }
  .visita-banner-info span { white-space: normal; }
  .visita-banner-form .btn { width: 100%; }
}

.offline-banner {
  display: none;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}
.offline-banner.visible { display: flex; }
.selected-note {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  border: 1px solid var(--color-acento);
  border-radius: var(--radio);
  background: var(--color-alerta-fondo);
  font-weight: 700;
}
.lista-stack {
  display: grid;
  gap: 10px;
  margin-top: 10px;
}
.skeleton-card {
  min-height: 76px;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  background: linear-gradient(100deg,
    var(--color-superficie) 40%,
    var(--color-superficie-alt) 50%,
    var(--color-superficie) 60%);
  background-size: 200% 100%;
  box-shadow: var(--sombra-suave);
  animation: shimmer 1.3s linear infinite;
}
@keyframes shimmer { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) {
  .skeleton-card { animation: none; background: var(--color-superficie-alt); }
}
.empty-state-mini {
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr);
  gap: 10px;
  align-items: center;
  border: 1px dashed var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-superficie);
  padding: 12px;
  color: var(--color-texto);
}
.empty-state-mini i {
  width: 42px;
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-alerta-fondo);
  color: var(--color-acento-fuerte);
  box-shadow: var(--sombra-dura-sm);
  font-size: 1.25rem;
}
.empty-state-mini strong { display: block; font-family: var(--fuente-titulo); line-height: 1.15; }
.empty-state-mini span { display: block; color: var(--color-texto-suave); font-size: .84rem; margin-top: 2px; }
.form-panel {
  background: var(--color-superficie);
  border: 2px dashed var(--color-borde);
  border-radius: var(--radio);
  padding: 14px;
  margin-top: 10px;
}

/* --- Participación en percha: filas dinámicas marca + frentes ----------- */
.percha-filas {
  margin-top: 10px;
}
.percha-fila {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 90px 44px;
  gap: 8px;
  align-items: center;
  margin-bottom: 10px;
}
.percha-fila select,
.percha-fila input {
  margin: 0;
}
.percha-fila-quitar {
  width: 44px;
  height: 44px;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  color: var(--color-alerta-alto);
}

/* --- Buscador de modelo por fragmento (app/ventas.php) --------------------- */
.venta-modelo-buscador { position: relative; min-width: 0; }
.venta-modelo-buscar { margin: 0; width: 100%; }
.venta-modelo-resultados {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
  background: var(--color-superficie); border: 2px solid var(--color-borde); border-radius: var(--radio);
  box-shadow: var(--sombra-suave); max-height: 260px; overflow-y: auto; padding: 4px;
}
.venta-modelo-resultado {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 7px 8px; border: none; background: none; border-radius: 8px; cursor: pointer; color: var(--color-texto);
}
.venta-modelo-resultado:focus-visible { background: var(--color-superficie-alt); }
@media (hover: hover) {
  .venta-modelo-resultado:hover { background: var(--color-superficie-alt); }
}
.venta-modelo-resultado img { width: 34px; height: 34px; object-fit: contain; border-radius: 6px; background: var(--color-superficie-alt); flex: 0 0 auto; }
.venta-modelo-resultado span { display: flex; flex-direction: column; min-width: 0; gap: 0; }
.venta-modelo-resultado strong { font-size: var(--txt-sm, .82rem); }
.venta-modelo-resultado .text-suave { font-size: .74rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.venta-modelo-vacio { padding: 10px 8px; font-size: var(--txt-sm, .82rem); }

/* --- Lectura de factura por cámara (OCR, app/ventas.php) ------------------- */
.venta-ocr-details { margin-top: 10px; }
.venta-ocr-toggle {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-weight: 700; font-size: .84rem; color: var(--color-acento-fuerte); list-style: none;
}
.venta-ocr-toggle::-webkit-details-marker { display: none; }
.venta-ocr-caja { margin-top: 8px; padding: 12px; border: 1.5px dashed var(--color-borde-suave); border-radius: var(--radio-lg); }
.venta-ocr-confirmar {
  margin-top: 10px; padding: 12px 14px; border-radius: var(--radio-lg);
  border: 2px solid var(--color-acento); background: var(--color-alerta-fondo);
}
.venta-ocr-confirmar p { margin: 0 0 10px; font-weight: 700; font-size: var(--txt-peq, .86rem); display: flex; align-items: flex-start; gap: 7px; }
.venta-ocr-confirmar-acciones { display: flex; gap: 8px; flex-wrap: wrap; }
#numero_factura.venta-ocr-pendiente { border-color: var(--color-acento); box-shadow: 0 0 0 3px var(--color-alerta-fondo); }
/* Recorte de zona para el OCR: touch-action none permite pintar el recuadro
   con el dedo sin que la página haga scroll; el marco oscurece el resto de la
   foto con un box-shadow gigante recortado por el overflow del lienzo. */
.venta-ocr-recorte { margin-top: 12px; display: grid; gap: 10px; }
.venta-ocr-lienzo {
  position: relative;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  overflow: hidden;
  border-radius: var(--radio-lg);
  border: 1.5px solid var(--color-borde-suave);
  cursor: crosshair;
  line-height: 0;
}
.venta-ocr-lienzo img { width: 100%; display: block; pointer-events: none; }
.venta-ocr-marco {
  position: absolute;
  border: 2px dashed var(--color-acento);
  background: rgba(255, 255, 255, .08);
  box-shadow: 0 0 0 9999px rgba(20, 18, 40, .45);
  pointer-events: none;
}
.venta-ocr-recorte-acciones { display: flex; gap: 8px; flex-wrap: wrap; }
.step-hero {
  border-left: 6px solid var(--color-acento);
  padding: 4px 0 8px 12px;
  margin-bottom: 14px;
}
.step-hero h2 {
  font-size: 1.35rem;
  margin-bottom: 2px;
}
.step-hero p {
  margin: 0;
  color: var(--color-texto-suave);
}
.step-kicker {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--txt-xs, .72rem);
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--color-acento-fuerte);
  margin-bottom: 3px;
}
.step-kicker::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-energia);
  flex: 0 0 auto;
}
.registro-contexto {
  position: sticky;
  top: calc(76px + env(safe-area-inset-top));
  z-index: 15;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  padding: 8px;
  margin-bottom: 16px;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
}
.context-chip {
  min-width: 0;
  min-height: 44px;
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  column-gap: 6px;
  align-items: center;
  border: 1px solid var(--color-borde-suave);
  border-radius: 4px;
  padding: 7px;
  background: var(--color-fondo);
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color .12s ease, background-color .12s ease;
}
@media (hover: hover) {
  .context-chip:hover { border-color: var(--color-borde); background: var(--color-superficie); }
}
.context-chip:active { transform: translateY(1px); }
.context-chip i {
  grid-row: span 2;
  font-size: 1.05rem;
}
.context-chip span {
  /* var(--txt-micro) y no .65rem: era el texto MAS PEQUENO de toda la app del
     promotor (10,4 px) y no es decoracion, es el rastro de lo que lleva elegido
     mientras registra un precio. */
  font-size: var(--txt-micro, .7rem);
  font-weight: 800;
  text-transform: uppercase;
  color: var(--color-texto-suave);
}
.context-chip strong {
  display: block;
  min-width: 0;
  font-size: var(--txt-sec, .78rem);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.context-chip.completo {
  border-color: var(--color-borde);
  background: var(--color-alerta-fondo);
}
.context-clear {
  /* Con 5 chips (Marca·Categoría·Modelo·Precio·Almacén) + limpiar = 6 celdas,
     el botón llena la 6ª celda (móvil 2 col = rejilla 3×2 exacta). */
  grid-column: auto;
  justify-self: end;
  width: 48px;
  min-height: 48px;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  color: var(--color-alerta-alto);
  box-shadow: var(--sombra-dura-sm);
  cursor: pointer;
}
.precio-layout {
  display: grid;
  gap: 12px;
}
@media (min-width: 720px) { .precio-layout { grid-template-columns: minmax(0, 1fr) 260px; } }

/* Bloque de promoción (registro de precio) */
.promo-check {
  display: flex; align-items: center; gap: 10px; margin-top: 14px; padding: 12px 14px; cursor: pointer;
  border: 1px solid var(--color-borde); border-radius: var(--radio, 12px); background: var(--color-superficie);
  font-weight: 700; font-size: var(--txt-md, .9rem);
}
.promo-check input { width: 22px; height: 22px; flex: 0 0 auto; accent-color: var(--color-energia, #FF5A1F); }
.promo-check i { color: var(--color-energia, #FF5A1F); }
.promo-campos { display: grid; gap: 12px; margin-top: 10px; }
@media (min-width: 560px) { .promo-campos { grid-template-columns: 1fr 1fr; } }
.promo-campos.oculto { display: none; }
/* Badge de "promo" en históricos de precio */
.badge-promo { background: color-mix(in srgb, var(--color-energia, #FF5A1F) 16%, transparent); color: var(--color-energia, #FF5A1F); }
.hist-promo { display: inline-block; margin-top: 2px; }

/* Mapa de calor de precios (categoría × ciudad). Todas las ciudades reciben el
   mismo ancho; los nombres largos usan dos líneas sin deformar toda la matriz. */
.hm-tabla {
  table-layout: fixed;
  width: max(100%, calc(180px + (var(--hm-cols, 1) * 150px)));
}
.hm-tabla .hm-col-cat { width: 180px; }
.hm-tabla .hm-col-ciudad { width: 150px; }
.hm-tabla th, .hm-tabla td { text-align: center; white-space: nowrap; }
.hm-tabla thead th { height: 62px; vertical-align: middle; }
.hm-ciudad-nombre {
  display: -webkit-box; max-width: 138px; margin-inline: auto; overflow: hidden;
  white-space: normal; line-height: 1.18; text-wrap: balance;
  -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
.hm-tabla th:first-child, .hm-tabla td:first-child { text-align: left; }
.hm-celda { font-weight: 800; font-variant-numeric: tabular-nums; }
.hm-b2 { background: #1F7A45; color: #fff; }
.hm-b1 { background: color-mix(in srgb, #1F7A45 35%, transparent); }
.hm-n  { background: var(--color-superficie-alt, #F0ECE6); }
.hm-a1 { background: color-mix(in srgb, #C62828 35%, transparent); }
.hm-a2 { background: #C62828; color: #fff; }
.hm-nd { color: var(--color-texto-suave); }

.money-input {
  display: grid;
  grid-template-columns: 74px minmax(0, 1fr);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.money-input:focus-within {
  border-color: var(--color-energia);
  box-shadow: 0 0 0 4px rgba(255,90,31,.2);
}
.money-input span {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-texto);
  color: var(--color-superficie);
  font-family: var(--fuente-titulo);
  font-weight: 800;
  letter-spacing: .02em;
}
.money-input input {
  border: 0;
  border-radius: 0;
  min-height: 64px;
  font-size: 2.1rem;
  font-family: var(--fuente-titulo);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* El anillo de foco lo dibuja el contenedor (focus-within); se evita el doble
   contorno del input interno, que quedaría recortado por overflow:hidden. */
.money-input input:focus-visible { outline: none; }
.quick-amounts,
.nota-chips,
.confirm-checklist {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.quick-amounts button,
.nota-chips button {
  min-height: 48px;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  color: var(--color-texto);
  font-weight: 800;
  box-shadow: var(--sombra-dura-sm);
  cursor: pointer;
  padding: 7px 10px;
}
@media (min-width: 720px) {
  .registro-contexto {
    grid-template-columns: repeat(5, minmax(0, 1fr)) 48px;
  }
}
.referencia-precio {
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-superficie);
  padding: 14px;
  box-shadow: var(--sombra-suave);
}
.referencia-precio strong {
  display: block;
  font-family: var(--fuente-titulo);
  font-size: 1.15rem;
}
.referencia-precio p {
  margin: 5px 0 0;
  font-size: var(--txt-peq, .86rem);
}
.referencia-precio small {
  display: block;
  margin-top: 8px;
  color: var(--color-texto-suave);
}
.referencia-precio.alto { border-color: var(--color-alerta-alto); background: #FDE2E1; }
.referencia-precio.bajo { border-color: var(--color-alerta-bajo); background: #E1F3E7; }
.referencia-precio.cargando { opacity: .82; }
.referencia-estado {
  font-weight: 800;
}
.resumen-final dl {
  margin: 0;
  display: grid;
  gap: 8px;
}
.resumen-final dl div {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 12px;
  border-bottom: 1px solid var(--color-borde-suave);
  padding-bottom: 7px;
}
.resumen-editar {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 2px solid var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-superficie);
  color: var(--color-acento-fuerte);
  font-family: var(--fuente-cuerpo);
  font-weight: 800;
  font-size: var(--txt-xs, .72rem);
  padding: 5px 9px;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color .12s ease, background-color .12s ease;
}
@media (hover: hover) {
  .resumen-editar:hover { border-color: var(--color-borde); background: var(--color-alerta-fondo); }
}
.resumen-final dt {
  font-weight: 800;
  color: var(--color-texto-suave);
}
.resumen-final dd {
  margin: 0;
  font-weight: 800;
  text-align: right;
}
.confirm-checklist span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: #E1F3E7;
  padding: 8px 10px;
  font-weight: 800;
}

/* --- Guía de "código nuevo" (escaneado, no está en el catálogo) ------------- */
.nuevo-codigo-guia {
  border: 2px solid var(--color-acento);
  background: var(--color-alerta-fondo);
  border-radius: var(--radio);
  padding: 12px 14px;
  margin: 0 0 14px;
  box-shadow: var(--sombra-dura-sm);
  animation: ncgEntra .28s var(--ease-out);
  scroll-margin-top: 88px;
}
@keyframes ncgEntra { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .nuevo-codigo-guia { animation: none; } }
.ncg-cab { display: flex; align-items: center; gap: 10px; }
.ncg-ic {
  width: 38px; height: 38px; flex: 0 0 auto; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-acento); color: #22223B; font-size: 1.25rem;
  border: 2px solid var(--color-borde);
}
.ncg-tit { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.ncg-kicker { font-size: .62rem; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--color-acento-fuerte); }
.ncg-codigo { font-family: var(--fuente-titulo); font-size: 1.05rem; letter-spacing: .02em; font-variant-numeric: tabular-nums; word-break: break-all; }
.ncg-cerrar {
  width: 34px; height: 34px; flex: 0 0 auto; border: none; background: transparent;
  color: var(--color-texto-suave); border-radius: 8px; cursor: pointer; font-size: 1rem;
}
@media (hover: hover) { .ncg-cerrar:hover { background: color-mix(in srgb, var(--color-texto) 10%, transparent); } }
.ncg-pasos { display: flex; align-items: center; gap: 8px; margin: 10px 0 0; }
.ncg-paso {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--txt-sec, .78rem); font-weight: 800; color: var(--color-texto-suave);
  border: 1.5px solid var(--color-borde-suave); border-radius: 99px; padding: 4px 10px;
}
.ncg-paso b {
  width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-borde-suave); color: var(--color-texto); font-size: var(--txt-micro, .7rem);
}
.ncg-paso.activo { border-color: var(--color-acento); color: var(--color-acento-fuerte); background: var(--color-superficie); }
.ncg-paso.activo b { background: var(--color-acento); color: #22223B; }
.ncg-paso.ok { border-color: var(--color-alerta-bajo); color: var(--color-alerta-bajo); background: var(--color-superficie); }
.ncg-paso.ok b { background: var(--color-alerta-bajo); color: #fff; }
.ncg-flecha { color: var(--color-texto-suave); font-size: .8rem; }
.ncg-accion { margin: 10px 0 0; font-size: .88rem; line-height: 1.4; font-weight: 600; display: flex; gap: 7px; align-items: flex-start; }
.ncg-accion i { color: var(--color-acento-fuerte); flex: 0 0 auto; margin-top: 2px; font-size: 1.05rem; }
.nuevo-codigo-guia.ncg--ok { border-color: var(--color-alerta-bajo); background: var(--color-superficie); }
.nuevo-codigo-guia.ncg--ok .ncg-ic { background: var(--color-alerta-bajo); color: #fff; }
.nuevo-codigo-guia.ncg--ok .ncg-accion i { color: var(--color-alerta-bajo); }

.wizard-actions {
  position: sticky;
  bottom: calc(var(--barra-inferior-alto) + 10px + env(safe-area-inset-bottom));
  z-index: 18;
  display: grid;
  grid-template-columns: 1fr 1.3fr;
  gap: 10px;
  align-items: center;
  margin-top: 20px;
  padding: 10px;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura);
}
.wizard-actions #btnAtras.oculto + #btnSiguiente {
  grid-column: 1 / -1;
}

/* --- Tarjeta de almacén ------------------------------------------------------ */
.almacen-card {
  display: flex; gap: 12px; align-items: center;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio);
  padding: 12px; margin-bottom: 10px; background: var(--color-superficie);
  cursor: pointer;
  width: 100%;
  min-height: 78px;
  color: var(--color-texto);
  font-family: var(--fuente-cuerpo);
  text-align: left;
  box-shadow: var(--sombra-suave);
}
.almacen-card { transition: transform .1s ease, box-shadow .15s ease; }
@media (hover: hover) {
  .almacen-card:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0px var(--color-borde); }
}
.almacen-card.seleccionado {
  border-color: var(--color-acento);
  border-width: 2px;
  background: var(--color-alerta-fondo);
  box-shadow: var(--sombra-dura-sm);
}
.almacen-card img, .almacen-card .sin-foto {
  width: 56px; height: 56px; border-radius: 4px; object-fit: cover;
  border: 1px solid var(--color-borde-suave); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-fondo); color: var(--color-gris); font-size: 1.4rem;
}
.almacen-card .info { flex: 1; min-width: 0; }
.almacen-card .info strong { display: block; font-size: .95rem; }
.almacen-card .info span { font-size: .8rem; color: var(--color-texto-suave); }
/* Marca como chip pequeño al pie de la tarjeta: deja el modelo completo arriba.
   display:flex la fuerza a su propia línea (los span previos son inline). */
.almacen-card .info .feed-marca {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  padding: 2px 9px;
  font-size: .66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--color-texto);
  background: var(--color-superficie-alt);
  border: 1px solid var(--color-borde-suave);
  border-radius: 999px;
}
.almacen-card .info .feed-marca i { font-size: .8rem; color: var(--color-acento); }

/* Feed de inicio: cards ESTANDARIZADAS — todas miden lo mismo aunque el
   nombre del producto sea corto o larguísimo (se recorta a 2 líneas). */
.feed-card { align-items: flex-start; margin-bottom: 0; }
.feed-card > img, .feed-card > .sin-foto { width: 72px; height: 72px; }
.feed-card .info strong {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.25;
  min-height: calc(2 * 1.25em); /* reserva SIEMPRE 2 líneas: títulos alineados */
}

/* Feed de inicio: precio protagonista + almacén en la misma fila */
.almacen-card .info .feed-fila { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.almacen-card .info .feed-precio,
.feed-precio {
  display: inline-flex; align-items: center;
  font-family: var(--fuente-titulo); font-weight: 800; font-size: .95rem;
  /* El precio es el dato que se busca en la tarjeta y era el menos legible:
     2,79:1 sobre su propio chip. El relleno sigue siendo el naranja de marca;
     lo que cambia es el TEXTO, que ahora usa la variante legible. */
  color: var(--color-energia-txt); background: rgba(255, 90, 31, .1);
  border-radius: 999px; padding: 2px 10px;
}
.feed-almacen { display: inline-flex; align-items: center; gap: 4px; font-size: .8rem; color: var(--color-texto-suave); min-width: 0; }
.feed-almacen i { color: var(--color-acento); }

/* Botón "editar" de auto-corrección (solo aparece dentro de la primera hora
   del registro): tercer hijo del flex row de la card, ya centrado por el
   align-self propio, sin pisar la foto ni el título. */
.feed-editar-btn {
  flex: 0 0 auto;
  align-self: center;
  width: 38px; height: 38px;
  min-width: 44px; min-height: 44px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--color-borde-suave);
  border-radius: 50%;
  background: var(--color-superficie);
  color: var(--color-acento);
  font-size: 1.05rem;
  cursor: pointer;
  box-shadow: var(--sombra-suave);
  transition: transform .1s ease, box-shadow .15s ease, background-color .15s ease;
}
@media (hover: hover) {
  .feed-editar-btn:hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0px var(--color-borde); background: var(--color-superficie-alt); }
}

/* Press compartido para tarjetas/filas clicables: hunde levemente al tocar
   (declarado tras los :hover para ganar por orden de fuente durante el press). */
.tarjeta-plana.is-clickable:active,
.almacen-card:active,
.venta-modelo-resultado:active { transform: scale(.98); }

/* Diálogo de editar/anular registro reciente: reutiliza el look de .ntf-confirm */
.reg-edit-tiempo { margin: -10px 0 14px; font-size: var(--txt-sec, .78rem); font-weight: 700; color: var(--color-texto-suave); }
.reg-edit-money { margin: 0 0 16px; }

/* Accesos rápidos del inicio: 2 losetas bajo el hero (antes 4). "Registrar" y
   "Almacenes" salieron por duplicidad —el hero y la hoja del "+" ya llevan
   Registrar, y la barra inferior ya lleva Almacenes—, no por espacio: la rejilla
   es de UNA fila, así que pasar de 4 a 2 no recupera ni un píxel de alto. */
.atajos-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  margin: 14px 0 18px;
}
.atajo-tile {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 3px;
  background: var(--color-superficie); border: 2px solid var(--color-borde);
  border-radius: var(--radio); box-shadow: var(--sombra-dura-sm);
  padding: 12px 6px 10px; text-decoration: none; color: var(--color-texto);
  transition: transform var(--dur-1, .15s) var(--ease-out, ease), box-shadow var(--dur-1, .15s) var(--ease-out, ease);
}
@media (hover: hover) {
  .atajo-tile:hover { transform: translateY(-2px); box-shadow: var(--sombra-suave); }
}
.atajo-tile strong { font-size: var(--txt-sec, .78rem); line-height: 1.1; }
.atajo-tile small { font-size: .75rem; color: var(--color-texto-suave); line-height: 1.15; }
.atajo-ico {
  width: 40px; height: 40px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.25rem; color: var(--color-acento);
  background: var(--color-superficie-alt); margin-bottom: 2px;
}
.atajo-ico--energia { color: #fff; background: linear-gradient(135deg, var(--color-acento), var(--color-energia)); }
/* El subtítulo ya no se esconde en móvil: con dos columnas cada loseta pasa de
   ~85 a 168 px de ancho a 375 px, así que cabe. Se escondía porque con cuatro no
   entraba. */

/* --- App: lista de almacenes del asesor (buscador, filtros y acciones) ---
   Desarrollado por Carlos David Donoso Cordero (ddchack). */
.almacenes-toolbar {
  display: grid;
  gap: 10px;
  margin: 4px 0 14px;
}
.campo-busqueda {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  align-items: center;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.campo-busqueda > i { justify-self: center; color: var(--color-texto-suave); font-size: 1.2rem; }
.campo-busqueda input {
  border: 0;
  border-radius: 0;
  min-height: 48px;
  padding: 10px 12px 10px 2px;
  background: transparent;
}
.campo-busqueda input:focus { border: 0; }
.campo-busqueda input:focus-visible { outline: none; }
.campo-busqueda:focus-within {
  border-color: var(--color-energia);
  box-shadow: inset 0 0 0 2px var(--color-energia);
}
.almacenes-filtros {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.orden-almacenes { min-height: 44px; }
#contadorAlmacenes { font-size: var(--txt-sec, .78rem); font-weight: 700; margin-left: auto; }

.almacen-ficha {
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
  overflow: hidden;
  margin-bottom: 12px;
  transition: transform .1s ease, box-shadow .15s ease;
}
@media (hover: hover) {
  .almacen-ficha:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0 var(--color-borde); }
}
.almacen-ficha-top { display: flex; gap: 12px; align-items: flex-start; padding: 12px; }
.almacen-ficha-foto-btn {
  position: relative; width: 58px; height: 58px; flex: 0 0 auto;
  border-radius: 6px; border: 2px solid var(--color-borde);
  padding: 0; margin: 0; cursor: pointer; overflow: hidden;
  background: var(--color-fondo); color: var(--color-gris); font-size: 1.5rem;
  display: flex; align-items: center; justify-content: center;
}
.almacen-ficha-foto-btn img,
.almacen-ficha-foto-btn .sin-foto {
  width: 100%; height: 100%; object-fit: cover;
  display: flex; align-items: center; justify-content: center;
}
.almacen-ficha-foto-hint {
  position: absolute; right: -3px; bottom: -3px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--color-acento-fuerte, #FF5A1F); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: .6rem; border: 2px solid var(--color-superficie, #fff);
}
.almacen-ficha-top .info { flex: 1; min-width: 0; }
.almacen-ficha-top .info strong { display: block; font-size: 1rem; line-height: 1.2; }
.almacen-ficha-top .info .dir { display: block; font-size: var(--txt-sm, .82rem); color: var(--color-texto-suave); margin: 1px 0 7px; }
/* Dirección real calculada desde el GPS (OSM): la línea "de verdad" del local */
.almacen-ficha-top .info .dir-geo {
  display: flex; align-items: baseline; gap: 4px;
  font-size: .8rem; font-weight: 700; color: var(--color-texto);
  margin: -4px 0 7px;
}
.almacen-ficha-top .info .dir-geo i { color: var(--color-alerta-bajo, #1B7A43); font-size: .85rem; }
.almacen-tags { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }
.gps-badge {
  display: inline-flex; align-items: center; gap: 4px;
  /* var(--txt-micro) y no .66rem a pelo: el token existe para esto ("etiquetas en
     mayusculas, metadatos") y de paso sube de 10,6 a 11,2 px, que era el texto mas
     pequeno de todo el listado de tiendas. */
  font-size: var(--txt-micro, .7rem); font-weight: 800;
  padding: 3px 9px; border-radius: 99px; border: 1px solid;
}
.gps-badge.si { color: var(--color-alerta-bajo); background: #E1F3E7; border-color: var(--color-alerta-bajo); }
.gps-badge.no { color: var(--color-texto-suave); background: var(--color-fondo); border-color: var(--color-borde-suave); border-style: dashed; }
.almacen-tel { display: inline-flex; align-items: center; gap: 4px; font-size: .74rem; font-weight: 700; color: var(--color-texto-suave); }
/* Cercanía GPS: distancia por tarjeta + resaltado del almacén más cercano
   (orden "Más cercanos" y modo venta desde el botón central). */
.almacen-dist { display: inline-flex; align-items: center; gap: 4px; font-size: .74rem; font-weight: 800; color: var(--color-acento); }
.almacen-ficha--cercano { border-color: var(--color-acento); box-shadow: 4px 4px 0 var(--color-acento); position: relative; }
.almacen-cercano-badge {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--txt-micro, .7rem); font-weight: 800; letter-spacing: .02em;
  padding: 5px 12px;
  background: var(--color-acento); color: #fff;
}
.almacen-ficha-acciones {
  display: flex;
  gap: 8px;
  padding: 10px 12px;
  border-top: 2px solid var(--color-borde-suave);
  background: var(--color-superficie-alt);
}
.almacen-ficha-acciones .btn { margin: 0; }
.almacen-ficha-acciones .btn-registrar { flex: 1; }
.almacen-ficha-acciones .btn-icono { width: auto; flex: 0 0 auto; padding: 8px 13px; }
.almacen-ficha-acciones .btn-icono[aria-disabled="true"] { opacity: .4; pointer-events: none; box-shadow: none; }

/* --- Barra inferior de navegación ------------------------------------------- */
.bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  display: flex; justify-content: space-around; align-items: center;
  background: var(--chrome-bg);
  border-top: 1px solid rgba(247, 243, 235, 0.14);
  box-shadow: 0 -6px 20px rgba(34, 34, 59, 0.22);
  /* El alto CRECE con el área segura del sistema, no la absorbe. Como todo el
     proyecto usa box-sizing: border-box, poner sólo padding-bottom hacía que
     el relleno se comiera los 64px: en celulares con navegación por gestos la
     barra se quedaba con ~34px útiles y los iconos salían aplastados y
     cortados (se vio en el APK, pero afectaba igual a la PWA instalada). */
  height: calc(var(--barra-inferior-alto) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
}

/* OCULTARLA EN ESCRITORIO, OTRA VEZ Y AHORA SÍ.
   El bloque de >=900px que monta el sidebar ya ponía `.bottom-nav { display: none }`
   (app.css:292) y su comentario lo dice: en escritorio el sidebar REEMPLAZA a la
   barra. Pero la regla de aquí arriba vuelve a declarar `display: flex` sin media
   query, con la misma especificidad (0,1,0) y más abajo en la hoja: ganaba ésta y la
   barra no se ocultaba nunca. Un supervisor en su portátil —o el APK en una tablet
   apaisada— veía las DOS navegaciones a la vez, más el botón «+» flotante clavado
   encima del contenido. Se repite aquí, después, para que gane el orden. */
@media (min-width: 900px) {
  .bottom-nav { display: none; }
}
.bottom-nav a {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  color: rgba(247, 243, 235, 0.62); text-decoration: none;
  /* .72rem = 11,5 px, no .68 = 10,9. Es la navegacion principal de la app y la
     leen de pie, en un pasillo, muchas veces con sol de frente. El contraste ya
     estaba bien (6,25:1 los apagados, 4,95:1 el activo, medido sobre el fondo
     tinta), asi que lo unico corto era el tamano. Entra de sobra: la etiqueta mas
     larga, "Almacenes", mide 61 px en una casilla de 76. */
  font-size: var(--txt-xs, .72rem);
  font-weight: 700; min-height: 48px; justify-content: center;
  transition: color var(--dur-1) ease, transform var(--dur-1) var(--ease-out);
}
.bottom-nav a i { font-size: 1.35rem; }
.bottom-nav a:active { transform: scale(0.94); }
.bottom-nav a.activo { color: var(--color-energia); }
.bottom-nav .fab {
  position: relative; top: -20px;
  flex: 0 0 auto;
  width: 58px; height: 58px; border-radius: 50%;
  background: var(--color-energia); color: #22223B;
  display: flex; align-items: center; justify-content: center;
  border: 3px solid var(--chrome-texto);
  box-shadow: var(--glow-energia), 0 2px 0 rgba(34, 34, 59, 0.3);
  font-size: 1.6rem; text-decoration: none;
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) ease;
}
.bottom-nav .fab:active { transform: scale(0.9); }
@media (hover: hover) {
  .bottom-nav .fab:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(255, 90, 31, 0.45), 0 2px 0 rgba(34, 34, 59, 0.3); }
}

/* --- Admin: nav lateral / off-canvas ---------------------------------------- */
.admin-shell { display: flex; min-height: 100vh; }
.admin-sidebar {
  position: fixed; top: 0; bottom: 0; left: 0; width: 250px;
  background: var(--color-texto); color: #F8F5F1;
  transform: translateX(-100%); transition: transform .2s ease; z-index: 40;
  padding: 18px 14px; overflow-y: auto;
}
.admin-sidebar.abierto { transform: translateX(0); }
.admin-sidebar a {
  display: flex; align-items: center; gap: 10px;
  color: #F8F5F1; text-decoration: none; padding: 12px 10px;
  border-radius: var(--radio); border-left: 3px solid transparent;
  font-weight: 700; font-size: var(--txt-md, .9rem);
  transition: background-color .15s ease, border-color .15s ease;
}
.admin-sidebar a:hover { background: rgba(255,255,255,.08); }
.admin-sidebar a.activo { background: rgba(255,90,31,.16); border-left-color: var(--color-energia); padding-left: 7px; color: #fff; }

/* Sub-pestañas de secciones agrupadas (Reportes, Almacenes y productos). */
.admin-subtabs {
  display: flex; flex-wrap: wrap; gap: 7px;
  margin-bottom: 16px; padding-bottom: 12px;
  border-bottom: 2px solid var(--color-borde-suave);
}
/* Con diez secciones en Reportes, las píldoras ya no entraban en una línea y la
   fila partida empujaba el contenido hacia abajo. Se ajustaron tamaño de texto y
   relleno lo justo para recuperar el ancho: por debajo de esto el texto deja de
   leerse cómodamente, y en pantallas estrechas partir en dos filas es correcto
   —es preferible a un carrusel horizontal, que esconde secciones. */
.admin-subtab {
  flex: 0 0 auto; text-decoration: none;
  border: 1px solid var(--color-borde); background: var(--color-superficie);
  color: var(--color-texto); border-radius: 999px; padding: 6px 12px;
  font-weight: 700; font-size: var(--txt-sec, .78rem); line-height: 1.35;
  transition: background-color .12s ease;
}
@media (hover: hover) { .admin-subtab:hover { background: var(--color-superficie-alt); } }
.admin-subtab.activo { background: var(--color-texto); color: #fff; border-color: var(--color-texto); }

/* En modo oscuro, var(--color-texto) es claro: la píldora activa quedaría clara
   con texto blanco (ilegible). Se usa el naranja de marca en su lugar. Aplica a
   todos los tabs con este patrón (admin, gamificación, notificaciones). */
@media (prefers-color-scheme: dark) {
  .admin-subtab.activo,
  .gamif-tab.activo {
    background: var(--color-energia, #FF5A1F);
    border-color: var(--color-energia, #FF5A1F);
    /* Tinta, no blanco. Este remedio se escribio con color: #fff y medido da 3,12:1
       —por debajo del 4,5 que pide un texto de 0,84rem—, asi que arreglaba el
       "invisible" pero dejaba la pestana SELECCIONADA como la peor de la fila.
       Naranja de marca con tinta navy da 4,95:1 y es el mismo par que ya usan el
       avatar, la inicial de marca y el numero de parada unas lineas mas abajo. */
    color: #22223B;
  }
}
.admin-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,.4); z-index: 39; display: none;
}
.admin-overlay.visible { display: block; }
.admin-contenido { flex: 1; min-width: 0; }
.admin-sidebar-hr { border-color: rgba(255,255,255,.15); margin: 16px 0; }
.admin-topbar-ciudad { font-size: .8rem; }

@media (min-width: 980px) {
  .admin-sidebar { transform: translateX(0); position: sticky; height: 100vh; }
  .admin-overlay { display: none !important; }
  .admin-contenido { padding-left: 0; }
  .admin-shell { max-width: 1300px; margin: 0 auto; }
}

/* ===== Menú riel: se colapsa a solo iconos cuando el cursor está lejos =======
   Sólo en escritorio con puntero fino (en móvil/táctil no hay "rollover" fiable,
   así que el menú queda como estaba). El estado lo enciende el JS añadiendo
   .rail-activo al .admin-shell; .rail-abierto (en el <aside>) = expandido.

   TRUCO ANTI-SALTO: al expandir, el menú crece a 250px PERO lleva margin-right
   negativo de -178px, así su hueco en el layout sigue siendo 72px y el contenido
   NO se recoloca — el menú simplemente flota por encima (overlay con sombra). */
@media (min-width: 980px) and (hover: hover) {
  .admin-shell.rail-activo .admin-sidebar {
    /* min-width:0 es imprescindible: sin él, el flex item respeta el ancho de su
       contenido (min-width:auto) y el texto oculto —visibility:hidden aún ocupa
       espacio— mantiene el menú en ~250px ignorando el width:72px. */
    width: 72px; min-width: 0; overflow-x: hidden;
    transition: width .18s ease, margin-right .18s ease, box-shadow .18s ease;
  }
  /* Texto oculto con display:none (NO visibility:hidden): así deja de ocupar
     ancho y el icono queda centrado. Con visibility:hidden el texto seguía
     midiendo su ancho, empujaba el icono fuera del riel y el overflow lo
     recortaba —de ahí que no se viera ningún icono—. */
  .admin-shell.rail-activo .admin-sidebar .admin-nav-txt { display: none; }
  .admin-shell.rail-activo .admin-sidebar a { justify-content: center; }
  /* Icono algo más grande en el riel: es el único identificador visible. */
  .admin-shell.rail-activo .admin-sidebar a i { font-size: 1.3rem; }
  .admin-shell.rail-activo .admin-sidebar-marca { justify-content: center; }
  /* Riel = icono cuadrado de la app; se esconde el lockup ancho. */
  .admin-shell.rail-activo .marca-logo-full { display: none; }
  .admin-shell.rail-activo .marca-logo-mini {
    display: block; width: 40px; height: 40px; border-radius: 10px; object-fit: cover;
  }

  /* Expandido: por clase (JS), por hover directo o por foco de teclado (a11y). */
  .admin-shell.rail-activo .admin-sidebar.rail-abierto,
  .admin-shell.rail-activo .admin-sidebar:focus-within {
    width: 250px; margin-right: -178px; overflow-x: visible; z-index: 45;
    box-shadow: 0 18px 50px rgba(0, 0, 0, .40);
  }
  .admin-shell.rail-activo .admin-sidebar.rail-abierto .admin-nav-txt,
  .admin-shell.rail-activo .admin-sidebar:focus-within .admin-nav-txt { display: inline; }
  .admin-shell.rail-activo .admin-sidebar.rail-abierto a,
  .admin-shell.rail-activo .admin-sidebar:focus-within a { justify-content: flex-start; }
  .admin-shell.rail-activo .admin-sidebar.rail-abierto a i,
  .admin-shell.rail-activo .admin-sidebar:focus-within a i { font-size: inherit; }
  /* Al expandir vuelve el lockup completo y se esconde el icono cuadrado. */
  .admin-shell.rail-activo .admin-sidebar.rail-abierto .marca-logo-full,
  .admin-shell.rail-activo .admin-sidebar:focus-within .marca-logo-full { display: block; }
  .admin-shell.rail-activo .admin-sidebar.rail-abierto .marca-logo-mini,
  .admin-shell.rail-activo .admin-sidebar:focus-within .marca-logo-mini { display: none; }
}
@media (min-width: 980px) and (hover: hover) and (prefers-reduced-motion: reduce) {
  /* Sin animación de ancho: el menú aparece/desaparece de golpe (sigue funcionando). */
  .admin-shell.rail-activo .admin-sidebar { transition: none; }
}

/* ===== Página de Configuración ============================================== */
.config-form { max-width: 720px; }
.config-seccion { padding: 18px 18px 8px; }
.config-seccion-tit {
  display: flex; align-items: center; gap: 8px;
  font-size: 1.05rem; margin: 0 0 4px;
}
/* Cada ajuste es una fila clicable: el checkbox a la izquierda, título + ayuda. */
.config-switch {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 14px 10px; margin: 0 -10px;
  border-top: 1px solid var(--color-borde-suave); cursor: pointer;
}
.config-switch:first-of-type { border-top: none; }
.config-switch input[type="checkbox"] {
  width: 20px; height: 20px; margin-top: 2px; flex: 0 0 auto; cursor: pointer;
  accent-color: var(--color-energia, #FF5A1F);
}
.config-switch-cuerpo { display: flex; flex-direction: column; gap: 3px; }
.config-switch-tit { font-weight: 700; }
.config-switch-desc { font-size: .85rem; color: var(--color-texto-suave); line-height: 1.4; }
.config-campo { padding: 14px 0 6px; border-top: 1px solid var(--color-borde-suave); }
.config-campo > label { display: block; font-weight: 700; margin-bottom: 6px; }
.config-num { width: 140px; }
.config-acciones { margin-top: 16px; }

/* ===== Mi ruta de hoy (promotor) =========================================== */
/* CTA en inicio */
.ruta-cta {
  display: flex; align-items: center; gap: 12px; text-decoration: none;
  background: var(--color-texto); color: #fff; border-radius: var(--radio, 14px);
  padding: 14px 16px; margin-bottom: 14px; box-shadow: var(--sombra-suave, 0 6px 18px rgba(0,0,0,.08));
}
.ruta-cta-ico { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 12px;
  background: var(--color-energia, #FF5A1F); display: flex; align-items: center; justify-content: center; font-size: 1.25rem; }
.ruta-cta-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.ruta-cta-txt strong { font-size: 1rem; }
.ruta-cta-txt small { opacity: .85; font-size: .84rem; }
.ruta-cta-caret { opacity: .7; font-size: 1.1rem; }

.ruta-fecha { margin: 2px 0 0; opacity: .9; text-transform: capitalize; }
.ruta-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.ruta-card { padding: 14px 16px; }
.ruta-card-top { display: flex; align-items: flex-start; gap: 12px; }
.ruta-orden {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-texto); color: #fff; font-weight: 800; font-size: .85rem;
}
.ruta-card-info { flex: 1; min-width: 0; }
.ruta-almacen { margin: 0; font-size: 1.02rem; line-height: 1.25; }
.ruta-dir { margin: 3px 0 0; font-size: .85rem; color: var(--color-texto-suave); display: flex; align-items: flex-start; gap: 4px; }
/* align-items: center es obligatorio desde que los chips que navegan miden 44 px:
   por defecto un contenedor flex estira (stretch) a todos sus hijos, así que el
   <span> informativo —que no es un blanco táctil y se deja en su alto natural—
   se estiraba también a 44 y su texto quedaba pegado a 4 px del borde superior. */
.ruta-objetivos { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.chip-objetivo {
  font-size: .74rem; font-weight: 700; padding: 3px 10px; border-radius: 999px;
  background: var(--color-superficie-alt, #F0ECE6); color: var(--color-texto);
}
/* Chip de objetivo que ADEMÁS es botón (tras el check-in): abre la hoja de tareas. */
.chip-objetivo--tarea { border: 1px solid transparent; cursor: pointer; }
@media (hover: hover) { .chip-objetivo--tarea:hover { border-color: var(--color-acento-fuerte, #9A6103); } }
/* Los DOS chips que son control (el que navega a capturar y el que abre las
   tareas) suben al objetivo táctil de la casa. El <span> informativo NO: no se
   pulsa. OJO: .chip-objetivo--ir tiene más reglas ~5.000 líneas más abajo; quien
   toque una, que busque la otra. */
.chip-objetivo--ir,
.chip-objetivo--tarea {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 10px 14px; font-size: .8rem;
}

/* CTA "Tareas de la visita" en la tarjeta de ruta */
/* Check-in libre: un flujo único (tienda → GPS → foto), pensado para móvil. */
.ci-hero { margin-bottom: var(--esp-lg); }
.ci-foco {
  margin-bottom: var(--esp-lg); padding: 18px; border: 1px solid var(--color-borde-suave);
  border-radius: calc(var(--radio, 14px) + 4px); background: var(--color-superficie);
  box-shadow: var(--sombra-suave, 0 6px 18px rgba(0,0,0,.08));
}
.ci-foco-cab { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.ci-foco-cab h2 { font-size: 1.15rem; }
.ci-tiempo { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; color: var(--color-texto-suave); font-size: var(--txt-sec, .78rem); font-weight: 700; }
.ci-pasos {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px;
  list-style: none; margin: 18px 0 8px; padding: 0;
}
.ci-pasos li {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 5px;
  color: var(--color-texto-suave); font-size: var(--txt-xs, .72rem); font-weight: 750; text-align: center;
}
.ci-pasos li:not(:last-child)::after {
  content: ''; position: absolute; z-index: 0; top: 16px; left: calc(50% + 20px);
  width: calc(100% - 35px); height: 2px; background: var(--color-borde);
}
.ci-pasos li i {
  position: relative; z-index: 1; display: grid; place-items: center; width: 34px; height: 34px;
  border: 2px solid var(--color-borde); border-radius: 50%; background: var(--color-superficie);
  color: var(--color-texto-suave); font-size: 1rem;
}
.ci-pasos li.is-active { color: var(--color-texto); }
.ci-pasos li.is-active i { border-color: var(--color-energia); color: #22223B; background: var(--color-energia); box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-energia) 18%, transparent); }
.ci-pasos li.is-done i { border-color: var(--color-texto); background: var(--color-texto); color: var(--color-superficie); }
.ci-pasos li.is-done:not(:last-child)::after { background: var(--color-texto); }
.ci-estado { min-height: 20px; margin: 8px 0 12px; font-size: .84rem; text-align: center; }
.ci-foco-lista .ruta-card { margin: 0; border: 1px solid var(--color-borde-suave); box-shadow: none; background: var(--color-superficie-alt); }
.ci-foco-lista .ruta-orden { display: none; }
.ci-foco-lista .ruta-acciones [data-accion="checkin"] { width: 100%; min-height: 52px; justify-content: center; font-size: 1rem; }
.ci-foco-lista .ruta-acciones [data-accion="no_visita"] { display: none; }

.ci-picker { margin-top: 12px; border: 1px solid var(--color-borde); border-radius: calc(var(--radio, 14px) + 2px); background: var(--color-superficie); overflow: hidden; }
.ci-picker-sum { list-style: none; cursor: pointer; padding: 15px 16px; font-weight: 800; display: flex; align-items: center; gap: 12px; min-height: 68px; }
.ci-picker-sum::-webkit-details-marker { display: none; }
.ci-picker-ic { display: grid; place-items: center; flex: 0 0 auto; width: 40px; height: 40px; border-radius: 12px; background: color-mix(in srgb, var(--color-energia) 15%, transparent); }
.ci-picker-ic i { color: var(--color-energia-txt); font-size: 1.25rem; }
.ci-picker-txt { display: flex; flex: 1; min-width: 0; flex-direction: column; line-height: 1.25; }
.ci-picker-txt small { margin-top: 3px; color: var(--color-texto-suave); font-size: .76rem; font-weight: 500; }
.ci-picker-flecha { color: var(--color-texto-suave); transition: transform .18s ease; }
.ci-picker[open] .ci-picker-flecha { transform: rotate(180deg); }
.ci-picker[open] .ci-picker-sum { border-bottom: 1px solid var(--color-borde-suave); }
.ci-picker-cuerpo { padding: 14px 16px 16px; }
.ci-buscar { position: relative; display: block; margin-bottom: 12px; }
.ci-buscar > i { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); z-index: 1; color: var(--color-texto-suave); font-size: 1.1rem; pointer-events: none; }
.ci-buscar input { width: 100%; min-height: 48px; padding-left: 42px; border-radius: 12px; background: var(--color-superficie-alt); }
.ci-cerca { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 12px; padding: 9px 11px; border-radius: 11px; background: var(--color-alerta-fondo); color: var(--color-texto); font-size: .8rem; }
.ci-cerca i { flex: 0 0 auto; margin-top: 1px; color: var(--color-acento-fuerte); }
.ci-picker .form-lista { max-height: min(52vh, 500px); overflow-y: auto; padding: 2px; }
.ci-picker .form-tile {
  position: relative; display: grid; grid-template-columns: 42px minmax(0, 1fr) 38px;
  align-items: center; gap: 11px; min-height: 92px; height: auto; padding: 13px 10px 13px 12px;
  overflow: hidden;
  box-shadow: none; transition: border-color .15s ease, background-color .15s ease, transform .15s ease;
}
.ci-picker .form-tile[hidden] { display: none; }
.ci-picker .form-tile-ic { grid-column: 1; align-self: center; }
.ci-picker .form-tile-txt {
  grid-column: 2; align-self: center; min-width: 0; line-height: 1.24;
  overflow: hidden; overflow-wrap: anywhere; display: flex; flex-direction: column; align-items: flex-start;
}
.ci-picker .form-tile-txt strong { display: block; line-height: 1.18; text-align: left; }
.ci-picker .form-tile-txt > span {
  display: -webkit-box; margin-top: 3px; overflow: hidden; line-height: 1.28; max-height: 2.56em; text-align: left;
  -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
.ci-picker .form-tile-txt > .ci-tile-meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 5px; margin-top: 5px;
  font-size: var(--txt-micro, .7rem); font-weight: 800; line-height: 1;
}
.ci-picker .ff-alm-dist { color: var(--color-texto-suave); white-space: nowrap; }
.ci-picker .ff-alm-dist:not([hidden])::before { content: 'A '; font-weight: 600; }
.ci-cercano-tag {
  display: inline-flex; align-items: center; gap: 3px; padding: 3px 6px;
  border-radius: 999px; background: var(--color-alerta-fondo); color: var(--color-energia-txt);
  text-transform: uppercase; letter-spacing: .025em; white-space: nowrap;
}
.ci-cercano-tag[hidden] { display: none; }
.ci-picker .form-tile.is-cercano {
  border-color: var(--color-energia); background: color-mix(in srgb, var(--color-energia) 6%, var(--color-superficie));
  box-shadow: inset 4px 0 0 var(--color-energia);
}
.ci-elegir {
  grid-column: 3; align-self: center; justify-self: end; width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center; border-radius: 50%;
  background: var(--color-texto); color: var(--color-superficie); font-size: var(--txt-md, .9rem);
}
.ci-picker .form-tile:active { transform: scale(.985); }
.ci-sin-resultados { margin: 14px 0 2px; text-align: center; font-size: .85rem; }
.ci-historial { margin-top: var(--esp-lg); border-top: 1px solid var(--color-borde-suave); padding-top: var(--esp-md); }
.ci-historial > summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 48px; cursor: pointer; color: var(--color-texto-suave); font-size: .88rem; font-weight: 750; }
.ci-historial > summary span:first-child { display: inline-flex; align-items: center; gap: 7px; }
.ci-historial[open] > summary { color: var(--color-texto); margin-bottom: 10px; }
.ruta-card--nueva { box-shadow: 0 0 0 2px var(--color-energia, #FF5A1F); }
@media (max-width: 520px) {
  .ci-foco { margin-inline: -4px; padding: 15px 12px; }
  .ci-picker-cuerpo { padding-inline: 11px; }
  .ci-picker .form-tile { grid-template-columns: 38px minmax(0, 1fr) 36px; padding: 13px 8px 13px 10px; gap-inline: 9px; }
  .ci-picker .form-tile-ic { width: 38px; height: 38px; }
}
/* Visita en curso: la tienda donde el promotor está DE PIE. Mismo anillo que la
   recién agregada (nunca conviven: la plantilla da prioridad a --nueva) más un
   rótulo de texto, para no dejar el "estás aquí" sólo en color.
   El rótulo va en --color-energia-txt y no en --color-energia: el naranja puro
   sobre la tarjeta blanca da 3,12:1; el token de "naranja como texto" da 5,08:1
   en claro y 5,28:1 en oscuro. */
.ruta-card--activa { box-shadow: 0 0 0 2px var(--color-energia, #FF5A1F); }
.ruta-card-aqui {
  display: inline-flex; align-items: center; gap: 5px;
  margin: 0 0 8px;
  font-size: var(--txt-xs, .72rem); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-energia-txt);
}
@keyframes ciPulso {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-energia, #FF5A1F) 55%, transparent); }
  50% { box-shadow: 0 0 0 9px transparent; }
}
.btn.pulso { animation: ciPulso 1.2s ease-in-out 3; }

.ruta-tareas-cta {
  display: flex; align-items: center; gap: 12px; width: 100%; margin-top: 12px;
  padding: 12px 14px; border-radius: var(--radio, 14px); cursor: pointer; text-align: left;
  background: var(--color-superficie-alt, #F0ECE6); border: 1px solid var(--color-borde);
}
@media (hover: hover) { .ruta-tareas-cta:hover { border-color: var(--color-energia, #FF5A1F); } }
.ruta-tareas-cta-ic {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 11px; font-size: 1.2rem;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--color-energia, #FF5A1F) 16%, transparent); color: var(--color-energia, #FF5A1F);
}
.ruta-tareas-cta-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.ruta-tareas-cta-txt strong { font-size: .92rem; }
.ruta-tareas-cta-txt span { font-size: .8rem; }
.ruta-tareas-cta > .ph-caret-right { opacity: .5; }

.ruta-nota { margin: 10px 0 0; font-size: .85rem; background: var(--color-alerta-fondo, #FFF6E9); padding: 8px 10px; border-radius: var(--radio, 12px); }
.ruta-marca-tiempo { margin: 10px 0 0; font-size: .85rem; color: var(--color-texto-suave); font-variant-numeric: tabular-nums; }
.ruta-marca-tiempo.ok { color: var(--color-alerta-bajo, #1F7A45); font-weight: 700; }
.ruta-acciones { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.ruta-acciones:empty { display: none; }
/* En curso: el badge neutro se tiñe de marca para que "resalte" que está activa.
   El tinte al 18 % dejaba el texto naranja sobre naranja pálido en 2,52:1 en
   claro y 4,18:1 en oscuro — el único estado que se leía mal era justo el que
   dice cuál de las visitas está abierta. Naranja sólido con tinta navy, el mismo
   par de .btn-energia y .chip-filtro.is-activo: 4,95:1 e IDÉNTICO en los dos
   temas por ser los dos literales. El punto de .badge::before y el borde de
   .badge son currentColor, así que salen navy solos y el estado no queda
   transmitido sólo por color. */
.ruta-card[data-estado="en_curso"] [data-estado-badge] {
  background: var(--color-energia, #FF5A1F);
  color: #22223B;
}

/* ===== Hojas (overlays sin <dialog>) ======================================= */
body.hoja-abierta { overflow: hidden; }
.hoja-ov {
  position: fixed; inset: 0; z-index: 60; background: rgba(0, 0, 0, .55);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0; animation: hojaFade .18s ease;
}
/* IMPRESCINDIBLE: sin esto, el display:flex de arriba (especificidad de clase)
   le gana al [hidden] del navegador y las hojas quedan SIEMPRE visibles y
   apiladas (cámara + no-visita a la vez). El JS oculta con el atributo hidden. */
.hoja-ov[hidden] { display: none; }
@keyframes hojaFade { from { opacity: 0; } to { opacity: 1; } }
.hoja {
  width: 100%; max-width: 520px; background: var(--color-superficie, #fff);
  border-radius: 18px 18px 0 0; padding: 16px 16px calc(18px + env(safe-area-inset-bottom));
  box-shadow: 0 -12px 40px rgba(0, 0, 0, .3); animation: hojaSube .22s var(--ease-out, cubic-bezier(.2, .8, .2, 1));
  max-height: 92vh; overflow-y: auto;
}
@keyframes hojaSube { from { transform: translateY(24px); } to { transform: translateY(0); } }
@media (min-width: 620px) { .hoja-ov { align-items: center; } .hoja { border-radius: 18px; } }
.hoja-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.hoja-head strong { font-size: 1.05rem; }
.hoja-camara .hoja-head > div { display: flex; flex-direction: column; gap: 2px; }
.cam-paso { color: var(--color-energia-txt); font-size: .68rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; }
.hoja-acciones { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.hoja-acciones .btn { flex: 1 1 auto; justify-content: center; }

/* ===== Hoja de TAREAS de la visita (checklist con evidencia) =============== */
.hoja-alta { min-height: 42vh; }
.tareas-lista { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.tareas-cargando { text-align: center; padding: 18px 0; }
.tarea-item {
  border: 1px solid var(--color-borde); border-radius: var(--radio, 14px); padding: 12px;
  background: var(--color-superficie, #fff); transition: border-color .15s ease, background-color .15s ease;
}
.tarea-item--ok {
  border-color: var(--color-alerta-bajo, #1F7A45);
  background: color-mix(in srgb, var(--color-alerta-bajo, #1F7A45) 8%, transparent);
}
.tarea-head { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.tarea-check { width: 24px; height: 24px; flex: 0 0 auto; accent-color: var(--color-alerta-bajo, #1F7A45); }
.tarea-nom { font-size: .98rem; font-weight: 700; }
.tarea-fotos { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.tarea-foto { position: relative; width: 64px; height: 64px; border-radius: 10px; overflow: hidden; background: var(--color-superficie-alt, #F0ECE6); }
.tarea-foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tarea-foto-del {
  position: absolute; top: 2px; right: 2px; width: 22px; height: 22px; border-radius: 50%; border: none;
  background: rgba(0, 0, 0, .6); color: #fff; display: flex; align-items: center; justify-content: center;
  font-size: .8rem; cursor: pointer; padding: 0;
}
.tarea-foto-add {
  width: 64px; height: 64px; border-radius: 10px; cursor: pointer; padding: 0;
  border: 1.5px dashed var(--color-borde); background: transparent; color: var(--color-texto-suave);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  font-size: .68rem; font-weight: 700;
}
.tarea-foto-add i { font-size: 1.15rem; }
@media (hover: hover) { .tarea-foto-add:hover { border-color: var(--color-energia, #FF5A1F); color: var(--color-energia, #FF5A1F); } }

/* Escena de la cámara: vídeo en vivo o la foto capturada, mismo marco. */
.cam-escena {
  position: relative; margin-top: 12px; border-radius: var(--radio, 12px); overflow: hidden;
  background: #000; aspect-ratio: 3 / 4; max-height: 60vh;
}
.cam-escena video, .cam-escena img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* El atributo hidden DEBE ganarle al display:block de arriba: si no, tras
   capturar el <video> (con el stream ya detenido) queda negro tapando la foto. */
.cam-escena video[hidden], .cam-escena img[hidden] { display: none; }
/* Espejo en la cámara frontal (selfie), como cualquier app de cámara. */
.cam-escena video.cam-espejo { transform: scaleX(-1); }
.cam-vivo {
  position: absolute; top: 10px; left: 10px; display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 8px; border-radius: 999px; background: rgba(15,15,20,.72); color: #fff;
  font-size: var(--txt-micro, .7rem); font-weight: 800; letter-spacing: .03em; text-transform: uppercase;
}
.cam-vivo i { color: #ff4b55; font-size: .55rem; }
.cam-vivo[hidden] { display: none; }
.cam-voltear {
  position: absolute; z-index: 3; top: 9px; right: 9px; width: 42px; height: 42px;
  border: 1px solid rgba(255,255,255,.55); background: rgba(15,15,20,.72); color: #fff;
  box-shadow: none;
}
.cam-voltear:hover { background: rgba(15,15,20,.9); color: #fff; }
.cam-voltear[hidden] { display: none; }
.cam-guia {
  position: absolute; z-index: 2; top: 50%; left: 50%; width: min(62%, 250px); aspect-ratio: .76;
  transform: translate(-50%, -50%); border: 2px solid rgba(255,255,255,.82); border-radius: 48%;
  box-shadow: 0 0 0 999px rgba(0,0,0,.12); pointer-events: none;
}
.cam-guia > span {
  position: absolute; left: 50%; bottom: -34px; transform: translateX(-50%); padding: 5px 9px;
  border-radius: 999px; background: rgba(15,15,20,.72); color: #fff; font-size: var(--txt-micro, .7rem); font-weight: 750; white-space: nowrap;
}
.cam-guia[hidden] { display: none; }
.hoja-camara .hoja-acciones [data-cam-capturar],
.hoja-camara .hoja-acciones [data-cam-confirmar] { flex: 1 1 100%; min-height: 52px; font-size: .98rem; }
.hoja-camara .hoja-acciones [data-cam-repetir] { flex: 0 0 auto; margin-inline: auto; border-color: transparent; background: transparent; }
/* "Capturar" deshabilitado mientras la cámara no tenga un frame real (evita la
   foto en negro por captura antes de tiempo). */
[data-cam-capturar]:disabled { opacity: .5; cursor: not-allowed; }
/* Objetivos logrados en el check-out */
.co-objetivos { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 2px; }
.co-obj { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--color-borde); border-radius: 10px; font-weight: 700; cursor: pointer; }
.co-obj input { width: 20px; height: 20px; flex: 0 0 auto; accent-color: var(--color-energia, #FF5A1F); }
.cam-aviso {
  margin-top: 10px; padding: 10px 12px; border-radius: var(--radio, 12px);
  background: #FDE2E1; color: var(--color-alerta-alto, #B42318); font-size: .85rem; font-weight: 600;
}
.cam-escena .cam-aviso {
  position: absolute; inset: auto 10px 10px; margin: 0;
}

/* ===== Itinerario (calendario de visitas, admin) =========================== */
.itin-barra {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  justify-content: space-between; margin-bottom: 14px;
}
.itin-promotor { display: flex; align-items: center; gap: 8px; }
.itin-promotor select { min-width: 220px; }
.itin-mes-nav { display: flex; align-items: center; gap: 8px; }
.itin-mes-label { min-width: 150px; text-align: center; font-size: 1.02rem; }

.itin-cal-scroll { overflow-x: auto; }
.itin-cal { display: grid; grid-template-columns: repeat(7, minmax(104px, 1fr)); gap: 6px; min-width: 760px; }
.itin-cab {
  text-align: center; font-weight: 800; font-size: var(--txt-sec, .78rem); padding: 4px 0;
  color: var(--color-texto-suave); text-transform: uppercase; letter-spacing: .04em;
}
.itin-dia {
  background: var(--color-superficie); border: 1px solid var(--color-borde);
  border-radius: 10px; min-height: 112px; padding: 5px 5px 6px;
  display: flex; flex-direction: column;
}
.itin-dia--fuera { background: transparent; border: none; min-height: 0; }
.itin-dia--hoy { border-color: var(--color-energia, #FF5A1F); box-shadow: inset 0 0 0 1px var(--color-energia, #FF5A1F); }
.itin-dia-cab { display: flex; align-items: center; justify-content: space-between; }
.itin-dia-num { font-weight: 800; font-size: var(--txt-sm, .82rem); color: var(--color-texto-suave); padding-left: 3px; }
.itin-add {
  border: none; background: transparent; color: var(--color-texto-suave);
  width: 22px; height: 22px; border-radius: 6px; cursor: pointer; font-size: .95rem;
  display: flex; align-items: center; justify-content: center; opacity: .55; transition: opacity .12s, background-color .12s;
}
.itin-dia:hover .itin-add { opacity: 1; }
.itin-add:hover { background: var(--color-superficie-alt); color: var(--color-energia, #FF5A1F); opacity: 1; }

.itin-lista {
  list-style: none; margin: 4px 0 0; padding: 0; flex: 1;
  counter-reset: itv; display: flex; flex-direction: column; gap: 4px; min-height: 26px;
}
.itin-chip {
  counter-increment: itv; display: flex; align-items: center; gap: 5px;
  background: var(--color-superficie-alt, #F0ECE6); border-radius: 7px;
  padding: 3px 5px 3px 4px; font-size: .74rem; cursor: grab; line-height: 1.2;
}
.itin-chip:active { cursor: grabbing; }
.itin-chip::before {
  content: counter(itv); flex: 0 0 auto; width: 16px; height: 16px; border-radius: 50%;
  background: var(--color-energia, #FF5A1F); color: #fff; font-size: var(--txt-xs, .72rem); font-weight: 800;
  display: flex; align-items: center; justify-content: center;
}
.itin-chip-txt { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.itin-chip-x {
  flex: 0 0 auto; border: none; background: transparent; color: var(--color-texto-suave);
  cursor: pointer; padding: 0 2px; font-size: .8rem; opacity: 0; transition: opacity .12s;
}
.itin-chip:hover .itin-chip-x { opacity: .8; }
.itin-chip-x:hover { color: var(--color-alerta-alto, #B42318); opacity: 1; }
.itin-chip--bloq { cursor: default; opacity: .82; }
.itin-chip--bloq::before { background: var(--color-texto-suave); }
.itin-chip--fantasma { opacity: .35; }

/* Toggle Mes / Semana */
.itin-vista-toggle { display: inline-flex; border: 2px solid var(--color-borde); border-radius: 999px; overflow: hidden; }
.itin-vista-btn {
  display: inline-flex; align-items: center; gap: 5px; padding: 6px 14px;
  font-size: var(--txt-sm, .82rem); font-weight: 800; color: var(--color-texto); text-decoration: none;
  background: var(--color-superficie);
}
.itin-vista-btn + .itin-vista-btn { border-left: 2px solid var(--color-borde); }
.itin-vista-btn.activo { background: var(--color-texto); color: var(--color-superficie); }

/* Vista semana: mismas 7 columnas pero celdas mucho más altas (más aire). */
.itin-cal--semana .itin-dia { min-height: 300px; }

/* Vista "Todos" (solo lectura): aviso + chips con color por promotor. */
.itin-todos-aviso {
  display: flex; align-items: center; gap: 8px; margin: 0 0 12px;
  font-size: .85rem; color: var(--color-texto);
  background: var(--color-alerta-fondo); border: 1.5px solid var(--color-acento);
  border-radius: var(--radio); padding: 8px 12px;
}
.itin-todos-aviso i { color: var(--color-acento-fuerte); flex: 0 0 auto; }
.itin-chip--ver { border-left: 4px solid var(--color-gris); cursor: pointer; }
.itin-chip--ver::before { display: none; }   /* el color de borde sustituye al número de orden */
.itin-chip-prom {
  flex: 0 0 auto; max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .64rem; font-weight: 800; color: var(--color-texto-suave); text-transform: uppercase; letter-spacing: .02em;
}

/* Modal agregar */
.itin-resultados { list-style: none; margin: 8px 0 0; padding: 0; max-height: 240px; overflow-y: auto; }
.itin-res { display: flex; flex-direction: column; padding: 8px 10px; border-radius: 8px; cursor: pointer; }
.itin-res span { font-size: var(--txt-sm, .82rem); }
@media (hover: hover) { .itin-res:hover { background: var(--color-superficie-alt); } }
.itin-res-vacio { padding: 10px; font-size: .88rem; }
.itin-objetivos { display: flex; flex-wrap: wrap; gap: 10px; margin: 6px 0 4px; }
.itin-obj-chk { display: flex; align-items: center; gap: 6px; font-size: .88rem; font-weight: 600; }

/* Barra: grupo de botones de Excel */
.itin-excel { display: flex; gap: 8px; }

/* Importar */
.itin-imp-conteo { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 12px; }
.itin-imp-tabla td { vertical-align: top; }
.itin-imp-motivo { font-size: var(--txt-sec, .78rem); margin-top: 2px; }
.itin-imp-fila--error td { background: color-mix(in srgb, var(--color-alerta-alto, #B42318) 7%, transparent); }
.itin-imp-fila--revisar td { background: color-mix(in srgb, var(--color-energia, #FF5A1F) 7%, transparent); }
.itin-imp-fila--dup td { opacity: .7; }
.itin-imp-pasos { margin: 0 0 14px; padding-left: 20px; line-height: 1.7; }
.itin-imp-subir { border-top: 1px solid var(--color-borde-suave); padding-top: 14px; margin-top: 14px; }
.itin-imp-resumen { text-align: center; padding: 28px 18px; }
.itin-imp-ok { font-size: 3rem; color: var(--color-alerta-bajo, #1F7A45); display: block; }

/* ===== Formularios (motor genérico) ======================================== */
/* Picker del promotor */
.form-lista { display: flex; flex-direction: column; gap: 10px; }
.form-tile {
  display: flex; align-items: center; gap: 12px; text-decoration: none; color: inherit;
  background: var(--color-superficie); border: 1px solid var(--color-borde);
  border-radius: var(--radio, 14px); padding: 12px 14px;
}
@media (hover: hover) { .form-tile:hover { border-color: var(--color-energia, #FF5A1F); } }
.form-tile-ic {
  flex: 0 0 auto; width: 42px; height: 42px; border-radius: 12px; font-size: 1.3rem;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-superficie-alt, #F0ECE6); color: var(--color-acento-fuerte, #9A6103);
}
.form-tile-ic--alerta { background: #FDE2E1; color: var(--color-alerta-alto, #B42318); }
.form-tile-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.form-tile-txt strong { font-size: .98rem; }
.form-tile-txt span { font-size: .84rem; }
.form-tile-caret { opacity: .5; }
/* Distancia GPS del almacén en el picker de formularios (más cercano arriba). */
.ff-alm-dist {
  flex: 0 0 auto; font-size: var(--txt-sec, .78rem); font-weight: 700; white-space: nowrap;
  font-variant-numeric: tabular-nums; color: var(--color-acento-fuerte, #9A6103);
}
.ff-alm-dist[hidden] { display: none; }
/* El primero de la lista se resalta SOLO cuando ya se ordenó por cercanía GPS
   (el JS marca la lista con .cerca-ok); así no engaña en orden alfabético. */
.form-lista.cerca-ok .form-tile:first-child { border-color: var(--color-energia, #FF5A1F); box-shadow: 0 0 0 1px var(--color-energia, #FF5A1F); }
/* Los tiles del picker de check-in libre son botones de submit: se ven igual que los <a>. */
button.form-tile { font: inherit; text-align: left; cursor: pointer; width: 100%; }

/* Formulario genérico (campos) */
.form-generico { max-width: 640px; }
.ff-campo { margin-top: 14px; }
/* Botonera del final: en móvil los botones van a ancho completo y apilados,
   porque el promotor los pulsa con el pulgar y de pie. Desde 520 px se ponen en
   fila. El "Enviar" va primero: es el camino de siempre y no debe perder sitio
   por el atajo nuevo. */
.ff-acciones { display: flex; flex-direction: column; gap: 10px; }
.ff-acciones > * { width: 100%; justify-content: center; }
@media (min-width: 520px) {
  .ff-acciones { flex-direction: row; flex-wrap: wrap; align-items: center; }
  .ff-acciones > * { width: auto; }
}
/* "Cambiar almacén" es NAVEGACIÓN, no envío: salió de .ff-acciones, donde en
   móvil era un tercer .btn a ancho completo a 10 px de "Enviar" y un resbalón
   del pulgar abandonaba el formulario con las respuestas y la foto ya puestas.
   Aquí sube con aspecto de enlace, pero conserva los 44 px de blanco táctil. */
.ff-alm-nota {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin: 0 0 14px;
  font-size: var(--txt-md, .9rem); font-weight: 700; color: var(--color-texto);
}
.ff-alm-nota i { color: var(--color-acento-fuerte); flex: 0 0 auto; }
.ff-alm-nota-cambiar {
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 0 4px;
  color: var(--color-energia-txt);
  font-weight: 700; text-decoration: underline; text-underline-offset: 3px;
}
/* El rojo va por el token de TINTA, no por el de relleno: --color-alerta-alto
   (#D7263D) no se invierte en oscuro —alimenta fondos sólidos— y como texto de
   13 px daba 4,48:1 en claro y 3,66:1 en oscuro, que es justo lo que explica por
   qué el formulario no deja guardar. Con --color-alerta-alto-txt: 5,94:1 en
   claro y 6,59:1 en oscuro, sin repetir el selector en el bloque oscuro. */
.ff-req { color: var(--color-alerta-alto-txt); }
.ff-error { color: var(--color-alerta-alto-txt); font-size: var(--txt-sm, .82rem); font-weight: 600; margin: 4px 0 0; }
.ff-segmento { display: flex; gap: 8px; flex-wrap: wrap; }
/* Sí / No / N-A es el control más pulsado de la app (12 preguntas = 36
   pastillas): min-height del objetivo táctil de la casa. */
.ff-seg-op {
  display: flex; align-items: center; gap: 6px; padding: 10px 14px; cursor: pointer;
  min-height: 44px;
  border: 1px solid var(--color-borde); border-radius: 999px; font-weight: 700; font-size: var(--txt-md, .9rem);
}
.ff-seg-op:has(input:checked) { background: var(--color-texto); color: #fff; border-color: var(--color-texto); }

/* Admin: definiciones */
.ff-defs { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 10px; margin-bottom: 18px; }
.ff-def { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.ff-def--off { opacity: .6; }
.ff-def-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* REJILLA Y NO FILA QUE ENVUELVE. Era un flex con wrap, que sobre el papel reparte
   en horizontal — pero la hoja global da width:100% a inputs y selects, así que cada
   campo pedía el ancho entero y se comía una fila él solo: seis controles, seis
   filas, ~700 px de filtros sobre una tabla de datos. Es el mismo mecanismo que ya
   apareció en la barra de Status Day.
   El min-width:220px de los select tampoco ayudaba: impedía que encogieran. */
.ff-filtros {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
  align-items: end;
  margin-bottom: 12px;
}
.ff-filtros > input, .ff-filtros > select, .ff-campo input { width: 100%; min-width: 0; }
.ff-filtros select { min-width: 0; }
.ff-campo { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
/* Un campo de la rama no elegida quedaba visible pero deshabilitado: la regla
   display:flex de .ff-campo ganaba al display:none nativo de [hidden]. */
.ff-campo[hidden] { display: none !important; }
.ff-campo label { font-size: var(--txt-micro, .74rem); font-weight: 700; text-transform: uppercase;
                  letter-spacing: .03em; color: var(--color-texto-suave); }

/* Catálogo de modelos desplegable dentro de inventario y soporte. */
.ff-modelos-picker {
  margin-top: 5px; border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-md, 12px); background: var(--color-superficie); overflow: hidden;
}
.ff-modelos-picker[hidden] { display: none !important; }
.ff-modelos-picker > summary {
  display: flex; align-items: center; gap: 8px; min-height: 46px;
  padding: 9px 12px; cursor: pointer; list-style: none;
  color: var(--color-acento-fuerte); font-weight: 800; font-size: .84rem;
}
.ff-modelos-picker > summary::-webkit-details-marker { display: none; }
.ff-modelos-picker > summary::after { content: '⌄'; margin-left: auto; color: var(--color-texto-suave); }
.ff-modelos-picker[open] > summary::after { transform: rotate(180deg); }
.ff-modelos-cuerpo { padding: 0 10px 10px; border-top: 1px solid var(--color-borde-suave); }
.ff-modelos-cuerpo > input[type="search"] { width: 100%; min-width: 0; margin-top: 10px; }
.ff-modelos-lista { display: grid; gap: 6px; max-height: 310px; overflow-y: auto; overscroll-behavior: contain; }
.ff-modelos-grupo { margin: 7px 2px 2px; color: var(--color-texto-suave); font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; }
.ff-modelo-op {
  width: 100%; min-height: 48px; display: flex; flex-direction: column; align-items: flex-start;
  gap: 2px; padding: 8px 10px; border: 1px solid var(--color-borde-suave);
  border-radius: 10px; background: var(--color-superficie-alt); color: var(--color-texto);
  text-align: left; cursor: pointer;
}
.ff-modelo-op strong { font-size: .85rem; overflow-wrap: anywhere; }
.ff-modelo-op small { color: var(--color-texto-suave); font-size: var(--txt-xs, .72rem); }
.ff-modelo-op[hidden], .ff-modelos-grupo[hidden] { display: none; }
@media (hover: hover) { .ff-modelo-op:hover { border-color: var(--color-energia); background: var(--color-alerta-fondo); } }
/* Las acciones ocupan la fila entera: como columna, "Filtrar" salía del ancho de un
   select y competía visualmente con los campos en vez de cerrarlos. */
.ff-acciones { grid-column: 1 / -1; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ff-chk { display: flex; align-items: center; gap: 6px; font-weight: 600; }

/* Admin: detalle de una respuesta */
.ff-detalle { margin-bottom: 18px; }
.ff-detalle-lista { margin: 14px 0 0; }
.ff-detalle-fila { display: grid; grid-template-columns: 1fr; gap: 2px; padding: 10px 0; border-top: 1px solid var(--color-borde-suave); }
@media (min-width: 620px) { .ff-detalle-fila { grid-template-columns: 260px 1fr; gap: 14px; } }
.ff-detalle-fila dt { font-weight: 700; margin: 0; }
.ff-detalle-fila dd { margin: 0; }
.ff-detalle-foto { max-width: 220px; border-radius: var(--radio, 12px); display: block; }

/* Miniatura de evidencia en la tabla de respuestas (admin) */
.ff-th-foto { width: 60px; }
.ff-thumb { position: relative; display: block; width: 48px; height: 48px; border-radius: 10px; overflow: hidden; background: var(--color-superficie-alt); }
.ff-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ff-thumb--vacio { display: inline-flex; align-items: center; justify-content: center; color: var(--color-texto-suave, #9b917f); font-size: 1.2rem; }
.ff-thumb-n { position: absolute; right: 2px; bottom: 2px; background: rgba(0,0,0,.62); color: #fff; font-size: var(--txt-xs, .72rem); font-weight: 700; line-height: 1; padding: 2px 4px; border-radius: 6px; }

/* ===== Enlace de entidad (nombres clicables que antes eran texto plano) ===== */
.enlace-ent {
  cursor: pointer; color: var(--color-acento-fuerte, #9A6103); font-weight: inherit;
  border-bottom: 1px dotted currentColor; text-decoration: none;
}
@media (hover: hover) { .enlace-ent:hover { border-bottom-style: solid; } }

/* ===== Status Day =========================================================== */
/* .sd-filtros ya no maqueta nada: la pantalla usa el panel compartido
   (.filtro-panel + .filtro-panel-grid) como flooring, rotación y comparativo. Sólo
   conserva el margen inferior propio de la pantalla. */
.sd-filtros { margin-bottom: var(--esp-lg); }
/* Fecha con paso de día: las flechas no se encogen y el input se come el resto.
   min-width:0 en el input porque la hoja global le da width:100% y, sin eso, se
   negaría a caber junto a las dos flechas. */
.sd-dia { display: flex; align-items: center; gap: 6px; }
.sd-dia input[type=date] { flex: 1 1 auto; min-width: 0; }
.sd-dia-paso { flex: 0 0 auto; width: 40px; padding-left: 0; padding-right: 0; }
/* Las acciones envuelven por lo mismo que .filtro-accion en cobertura: aquí pueden
   llegar a ser TRES (Hoy, Limpiar, Exportar) y no caben en una celda del grid. */
.sd-acciones { flex-wrap: wrap; gap: 8px; }
.sd-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); gap: var(--esp-md); margin-bottom: var(--esp-lg); }
.sd-kpi {
  display: flex; align-items: center; gap: 12px;
  background: var(--color-superficie); border: 1px solid var(--color-borde);
  border-radius: var(--radio, 14px); padding: 12px 14px; box-shadow: var(--sombra-dura-sm);
}
.sd-kpi-ic {
  width: 40px; height: 40px; flex: 0 0 auto; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.3rem; color: #fff; background: var(--color-gris);
}
.sd-kpi-txt { display: flex; flex-direction: column; min-width: 0; }
.sd-kpi-num { display: block; font-family: var(--fuente-titulo); font-size: 1.5rem; font-weight: 800; line-height: 1.05; font-variant-numeric: tabular-nums; }
.sd-kpi-num small { font-size: var(--txt-sm, .82rem); font-weight: 700; color: var(--color-texto-suave); }
.sd-kpi-lbl { font-size: var(--txt-xs, .72rem); font-weight: 700; color: var(--color-texto-suave); text-transform: uppercase; letter-spacing: .03em; }
/* Badges de color por tipo: colores FIJOS + icono blanco/oscuro → iguales en
   claro y oscuro (no dependen de variables que se voltean). */
.sd-kpi--cob .sd-kpi-ic  { background: var(--color-energia); }
.sd-kpi--ok .sd-kpi-ic   { background: var(--color-alerta-bajo); }
.sd-kpi--pend .sd-kpi-ic { background: var(--color-acento); color: #22223B; }
.sd-kpi--sin .sd-kpi-ic  { background: var(--color-alerta-alto); }
.sd-kpi--prom .sd-kpi-ic { background: var(--color-texto); color: var(--color-superficie); }
/* Cobertura ocupa 2 columnas y lleva su barra debajo del icono+número. */
.sd-kpi--cobertura { grid-column: span 2; flex-direction: column; align-items: stretch; gap: var(--esp-sm); }
.sd-kpi--cobertura .sd-kpi-row { display: flex; align-items: center; gap: var(--esp-md); }
.sd-barra, .sd-mini-barra { height: 8px; border-radius: 999px; background: var(--color-superficie-alt, #Eee); overflow: hidden; margin-top: var(--esp-sm); }
.sd-mini-barra { height: 6px; margin: 0 0 3px; min-width: 70px; }
.sd-barra span, .sd-mini-barra span { display: block; height: 100%; background: var(--color-energia, #FF5A1F); border-radius: 999px; }
.sd-tit { font-size: 1.05rem; margin: 20px 0 10px; display: flex; align-items: center; gap: var(--esp-sm); }
.sd-just { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--esp-md); }
.sd-just-item { padding: 12px 14px; }
.sd-just-cab { display: flex; flex-direction: column; margin-bottom: var(--esp-xs); }
.sd-just-foto { max-width: 120px; border-radius: 10px; margin-top: var(--esp-sm); display: block; }

/* Por promotor: tarjetas con anillo de avance (donut conic-gradient) + chips de
   estado. Los chips usan un PUNTO de color (no texto de color), así el número y
   la etiqueta van en --color-texto y quedan legibles en claro Y oscuro. */
.sd-prom-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--esp-md); }
.sd-prom-card {
  background: var(--color-superficie); border: 1px solid var(--color-borde);
  border-radius: var(--radio); padding: 14px 16px; box-shadow: var(--sombra-dura-sm);
  transition: transform .1s ease, box-shadow .15s ease;
}
@media (hover: hover) { .sd-prom-card:hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0 var(--color-borde); } }
.sd-prom-card--alerta { border-left: 4px solid var(--color-alerta-alto); }
/* Chip "Sólo con incidencias": esconde por CSS las tarjetas sin alerta. Nace
   apagado a propósito — la pantalla también sirve para ver que la jornada va
   bien. El gráfico no se filtra: es un <canvas> y habría que rehacer el dataset;
   el reorden de la consulta ya sube arriba las barras que importan. */
.sd-prom-grid--solo-alerta .sd-prom-card:not(.sd-prom-card--alerta) { display: none; }
.sd-prom-top { display: flex; align-items: center; gap: var(--esp-md); }
.sd-ring {
  --pct: 0; --ring: var(--color-energia);
  position: relative; width: 62px; height: 62px; flex: 0 0 auto; border-radius: 50%;
  background: conic-gradient(var(--ring) calc(var(--pct) * 1%), var(--color-superficie-alt) 0);
  display: grid; place-items: center;
}
.sd-ring::before { content: ''; position: absolute; inset: 5px; border-radius: 50%; background: var(--color-superficie); }
.sd-ring-num { position: relative; z-index: 1; font-family: var(--fuente-titulo); font-weight: 800; font-size: 1.05rem; font-variant-numeric: tabular-nums; color: var(--color-texto); }
.sd-ring-num small { font-size: .62rem; font-weight: 700; }
.sd-ring--ok { --ring: var(--color-alerta-bajo); }
.sd-ring--parcial { --ring: var(--color-acento); }
.sd-ring--alerta { --ring: var(--color-alerta-alto); }
.sd-ring--cero { --ring: var(--color-borde-suave); }
.sd-prom-id { min-width: 0; display: flex; flex-direction: column; gap: var(--esp-2xs); }
.sd-prom-nombre {
  border: 0; background: none; padding: 0; text-align: left; cursor: pointer;
  font-family: var(--fuente-titulo); font-weight: 700; font-size: 1rem; line-height: 1.18; color: var(--color-texto);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
@media (hover: hover) { .sd-prom-nombre:hover { text-decoration: underline; text-decoration-color: var(--color-acento); text-underline-offset: 2px; } }
/* ÁREA TÁCTIL DE 24 px SIN MOVER NADA.
   Medido en producción: el nombre del promotor —que es la acción principal de la
   tarjeta, abre su ficha— era un objetivo de 19 px de alto, y los "Ver visita" de
   las secciones de incidencias, de 20. El mínimo de WCAG 2.2 es 24.

   Relleno con margen negativo del mismo tamaño en vez de un ::after absoluto: aquí
   no sirve el pseudo-elemento porque .sd-prom-nombre es un -webkit-box con
   line-clamp y overflow:hidden, que lo recortaría. El margen negativo devuelve
   exactamente lo que el padding añade, así que la maqueta queda donde estaba y sólo
   crece la superficie que responde al dedo. */
.sd-prom-nombre { padding: 3px 0; margin: -3px 0; }
/* Se quitó el acotado a .sd-just-item: al medir producción, .celda-enlace sale a
   21,8 px en las 278 filas de Almacenes y en comparativo y flooring también. Era un
   problema del panel entero, no de una pantalla. La regla global va más abajo, junto
   a la definición de la clase. */
.sd-prom-meta { display: flex; flex-wrap: wrap; gap: var(--esp-xs) 10px; font-size: .76rem; color: var(--color-texto-suave); }
.sd-prom-ciudad, .sd-prom-checkin { display: inline-flex; align-items: center; gap: var(--esp-xs); }
.sd-prom-ciudad i, .sd-prom-checkin i { color: var(--color-acento); font-size: .85rem; }
.sd-prom-stats { display: flex; flex-wrap: wrap; gap: var(--esp-sm); margin-top: var(--esp-md); }
.sd-stat {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 999px;
  font-size: .74rem; font-weight: 600; color: var(--color-texto-suave);
  border: 1.5px solid var(--color-borde-suave); background: var(--color-superficie);
}
.sd-stat::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--dot, var(--color-gris)); flex: 0 0 auto; }
.sd-stat b { font-weight: 800; font-size: .84rem; color: var(--color-texto); font-variant-numeric: tabular-nums; }
.sd-stat--ok { --dot: var(--color-alerta-bajo); }
.sd-stat--pend { --dot: var(--color-acento); }
.sd-stat--sin { --dot: var(--color-alerta-alto); }
.sd-stat--sin:not(.sd-stat--nil) { border-color: var(--color-alerta-alto); }
.sd-stat--nil { opacity: .45; }

/* Gráficos del Status Day */
.sd-graficos { display: grid; grid-template-columns: 1fr; gap: var(--esp-md); margin-bottom: var(--esp-sm); }
@media (min-width: 760px) { .sd-graficos { grid-template-columns: minmax(0, 320px) minmax(0, 1fr); } }
.sd-grafico { padding: 14px 16px; }
.sd-grafico h3 { font-size: .98rem; }
.sd-canvas-wrap { position: relative; height: 260px; }

/* Galería de fotos de check-in */
/* grid-auto-rows: 1fr iguala el alto de TODA la fila. Sin él, cada tarjeta
   medía según su texto y la hora quedaba a una altura distinta en cada una:
   un almacén de nombre corto la subía y uno de dos líneas la bajaba. */
.sd-galeria { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--esp-md); grid-auto-rows: 1fr; }
.sd-checkin {
  margin: 0; background: var(--color-superficie);
  border: 1px solid var(--color-borde); border-radius: var(--radio, 14px);
  overflow: hidden;
  /* Columna flex + height 100%: la tarjeta llena su celda de la rejilla y el
     pie puede empujarse al fondo. */
  display: flex; flex-direction: column; height: 100%;
}
.sd-checkin-foto { width: 100%; aspect-ratio: 3 / 4; object-fit: cover; display: block; background: var(--color-superficie-alt); }
.sd-checkin figcaption {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; font-size: var(--txt-sm, .82rem);
  flex: 1;   /* ocupa lo que sobre, para que el pie caiga abajo del todo */
}
/* Nombre y almacén: DOS líneas siempre, se use una o dos. Recortar a dos evita
   que un nombre largo empuje el resto, y reservar las dos alinea la hora entre
   tarjetas aunque el texto ocupe sólo una. */
.sd-checkin figcaption > strong,
.sd-checkin figcaption > .text-suave {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; line-height: 1.25;
}
.sd-checkin figcaption > strong { min-height: calc(2 * 1.25em); }
.sd-checkin figcaption > .text-suave { min-height: calc(2 * 1.25em); font-size: .76rem; }
/* La hora abre el pie y arrastra con ella al botón: los dos quedan pegados al
   fondo, a la misma altura en todas las tarjetas de la fila. */
.sd-checkin-hora { margin-top: auto; }
.sd-checkin figcaption > .celda-enlace { align-self: flex-start; }
.sd-checkin-hora { color: var(--color-texto-suave); font-size: var(--txt-sec, .78rem); font-variant-numeric: tabular-nums; padding-top: 4px; }
/* Marca de "fuera de zona" (geocerca) en Status Day */
.sd-checkin { position: relative; }
.sd-checkin--fuera { border-color: var(--color-alerta-alto, #B42318); }
.sd-fuera-badge {
  position: absolute; top: 6px; left: 6px; display: inline-flex; align-items: center; gap: 4px;
  background: var(--color-alerta-alto, #B42318); color: #fff; font-size: .68rem; font-weight: 800;
  padding: 3px 7px; border-radius: 999px; line-height: 1;
}
/* -txt y no el de relleno. Estaba usando --color-alerta-alto (#D7263D), que es el
   rojo para FONDOS, con el de texto puesto sólo como respaldo — o sea que el respaldo
   era el correcto y la variable no. Medido en producción sobre "Visitas fuera de
   zona": 4,48 contra el fondo de página, por debajo del 4,5 que le toca a 16,8 px.
   Con --color-alerta-alto-txt sube a 5,94, y en oscuro el token ya se voltea a
   #FF6B78 solo. Las otras ~30 reglas de texto del proyecto ya usaban la -txt; ésta
   era la única que se había quedado con la de relleno. */
.sd-tit--alerta { color: var(--color-alerta-alto-txt, #B42318); }
.sd-just-item--alerta { border-color: var(--color-alerta-alto, #B42318); }

/* Ficha de promotor (modal) — rediseño: avatar con iniciales, tiles 2x2 con
   badge de icono y pie con contacto. Los colores de los badges son FIJOS
   (iguales en claro y oscuro); textos/valores en variables (legibles en ambos).
   El ancho lo da .modal-ts (min(440px,...)); a ese ancho los tiles 2x2 entran
   sin recortar las etiquetas. */
.us-cab { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.us-avatar { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; border: 2px solid var(--color-borde); }
.us-avatar--ph {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-acento); color: #22223B;
  font-family: var(--fuente-titulo); font-weight: 800; font-size: 1.4rem; letter-spacing: .01em;
}
.us-avatar.oculto { display: none; }
.us-cab-txt { min-width: 0; }
.us-cab-txt h3 { font-size: 1.18rem; line-height: 1.15; }
.us-sub { font-size: .85rem; }

.us-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 0 0 16px; }
.us-stat {
  display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; column-gap: 10px;
  align-items: center;
  border: 1.5px solid var(--color-borde-suave); border-radius: var(--radio);
  background: var(--color-superficie); padding: 10px 12px;
  color: inherit; text-decoration: none;   /* cada métrica es un enlace a su sección */
  transition: border-color var(--dur-1), transform var(--dur-1), box-shadow var(--dur-1);
}
@media (hover: hover) {
  .us-stat:hover { border-color: var(--color-acento); transform: translateY(-1px); box-shadow: var(--sombra-suave); }
}
.us-stat:focus-visible { outline: 2px solid var(--color-acento); outline-offset: 2px; }
.us-stat-ic {
  grid-row: 1 / 3; width: 38px; height: 38px; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.15rem; color: #fff;
}
.us-stat-go { grid-row: 1 / 3; grid-column: 3; align-self: center; color: var(--color-texto-suave); font-size: .95rem; opacity: .45; transition: color var(--dur-1), opacity var(--dur-1); }
.us-stat:hover .us-stat-go { color: var(--color-acento); opacity: 1; }
.us-stat--vis .us-stat-ic { background: var(--color-acento); color: #22223B; }
.us-stat--pre .us-stat-ic { background: var(--color-energia); }
.us-stat--ven .us-stat-ic { background: var(--color-alerta-bajo); }
.us-stat--for .us-stat-ic { background: #2C63E9; }
.us-stat-val { font-family: var(--fuente-titulo); font-weight: 800; font-size: 1.35rem; line-height: 1; font-variant-numeric: tabular-nums; align-self: end; }
.us-stat-lbl { font-size: var(--txt-xs, .72rem); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--color-texto-suave); line-height: 1.18; align-self: start; margin-top: 3px; }

.us-pie { border-top: 1px solid var(--color-borde-suave); padding-top: 12px; display: flex; flex-direction: column; gap: 7px; }
.us-linea { margin: 0; font-size: .85rem; color: var(--color-texto-suave); display: flex; align-items: center; gap: 8px; }
.us-linea i { color: var(--color-acento); flex: 0 0 auto; font-size: 1rem; }
.us-contacto { color: var(--color-texto); font-weight: 600; word-break: break-word; }

/* ===== Modal detalle de chip (itinerario) =================================== */
.cd-bloque { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--color-borde-suave); }
.cd-lbl { display: block; font-size: .75rem; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); font-weight: 800; margin-bottom: 6px; }
.cd-nota { margin: 8px 0 0; font-size: .88rem; background: var(--color-alerta-fondo, #FFF6E9); padding: 8px 10px; border-radius: var(--radio, 10px); }
.cd-datos { margin: 12px 0 0; }
.cd-datos > div { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; border-top: 1px solid var(--color-borde-suave); }
.cd-datos dt { color: var(--color-texto-suave); margin: 0; }
.cd-datos dd { margin: 0; font-weight: 700; text-align: right; }

/* ===== Modales de modelo / marca =========================================== */
.modal-ts-sub-titulo { font-weight: 800; font-size: var(--txt-sec, .78rem); text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); margin: 10px 0 6px; }
.mo-caract { display: grid; grid-template-columns: auto 1fr; gap: 5px 16px; margin: 0; }
.mo-caract dt { color: var(--color-texto-suave); margin: 0; }
.mo-caract dd { margin: 0; font-weight: 700; text-align: right; }
.mc-cab { display: flex; align-items: center; gap: 12px; }
.mc-logo { height: 40px; max-width: 120px; object-fit: contain; }

/* ===== Flooring: share de piso por marca dentro de categoría =============== */
.fl-share { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--esp-lg); margin-top: var(--esp-sm); }
.fl-cat-cab { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--esp-sm); padding-bottom: 5px; border-bottom: 2px solid var(--color-borde-suave); }
/* CUATRO columnas: marca · barra · cifra · variación. Eran tres, y al añadir el
   chip de variación el cuarto hijo caía a una segunda fila implícita: la fila
   pasaba de 28 a 56 px y la tarjeta entera doblaba de alto sin que nada lo
   pidiera. Con `auto` al final la variación va donde tiene que ir. */
.fl-marca { display: grid; grid-template-columns: 92px 1fr auto auto; align-items: center; gap: var(--esp-sm) 9px; margin-bottom: var(--esp-xs); font-size: var(--txt-peq); }
.fl-marca-nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; }
.fl-barra { height: 10px; border-radius: 999px; background: var(--color-superficie-alt, #EEE); overflow: hidden; }
.fl-barra span { display: block; height: 100%; background: var(--color-borde); border-radius: 999px; }
.fl-barra span.es-lider { background: var(--color-energia, #FF5A1F); }
.fl-marca-pct { font-variant-numeric: tabular-nums; font-weight: 700; white-space: nowrap; }

/* ── Flooring: mapa de peso de marcas por categoría (colores FIJOS por marca) ── */
.fl-hm { margin-top: var(--esp-lg); }
.fl-hm-cab { margin-bottom: var(--esp-md); }
.fl-hm-tabs { display: flex; flex-wrap: wrap; gap: var(--esp-sm); margin: 12px 0 4px; max-width: 100%; }
.fl-hm-tab {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px;
  border: 1.5px solid var(--color-borde-suave); border-radius: 999px;
  background: var(--color-superficie); color: var(--color-texto);
  font: inherit; font-weight: 700; font-size: var(--txt-sm, .82rem); cursor: pointer; white-space: nowrap;
  transition: background .12s, border-color .12s;
}
@media (hover: hover) { .fl-hm-tab:not(.es-activo):hover { background: var(--color-superficie-alt); } }
.fl-hm-tab.es-activo { background: var(--color-texto); color: var(--color-superficie); border-color: var(--color-texto); }
.fl-hm-tab-n { font-variant-numeric: tabular-nums; font-weight: 800; font-size: var(--txt-micro); opacity: .7; }
.fl-hm-panel { margin-top: var(--esp-lg); }
.fl-hm-panel[hidden] { display: none; }
.fl-hm-panel.es-activo { animation: fl-hm-fade .18s ease; }
@keyframes fl-hm-fade { from { opacity: 0; } to { opacity: 1; } }
.fl-hm-cat-cab { display: flex; justify-content: space-between; align-items: baseline; gap: var(--esp-md); margin-bottom: var(--esp-sm); }
.fl-hm-cat-cab strong { font-size: 1rem; }
/* La PISTA usa --color-borde: los gaps de 2px se ven como hairlines del color del
   tema (separan dos azules/oscuros contiguos y delinean el segmento casi-negro). */
.fl-hm-barra {
  display: flex; gap: 2px; width: 100%; height: 34px; overflow: hidden;
  border: 2px solid var(--color-borde); border-radius: var(--radio);
  box-shadow: var(--sombra-dura-sm); background: var(--color-borde);
}
.fl-hm-seg {
  flex: 1 1 0; min-width: 4px;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  cursor: pointer; white-space: nowrap; transition: filter .12s;
}
@media (hover: hover) { .fl-hm-seg:hover { filter: brightness(1.06) saturate(1.04); } }
.fl-hm-seg-pct { padding: 0 5px; font-weight: 800; font-size: var(--txt-sec); font-variant-numeric: tabular-nums; pointer-events: none; }
.fl-hm-leyenda {
  list-style: none; margin: 12px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 2px 16px;
}
.fl-hm-leg-btn {
  display: grid; grid-template-columns: 14px 1fr auto; align-items: center; gap: 9px; width: 100%;
  padding: 5px 2px; border: 0; background: none; font: inherit; color: var(--color-texto);
  cursor: pointer; text-align: left; border-bottom: 1px solid var(--color-borde-suave);
}
.fl-hm-leg-sw { width: 14px; height: 14px; border-radius: 4px; flex: none; box-shadow: 0 0 0 1px var(--color-borde-suave); }
.fl-hm-leg-nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; font-size: var(--txt-peq); }
.fl-hm-leg-nom.es-lider { font-weight: 800; }
.fl-hm-leg-pct { font-variant-numeric: tabular-nums; font-weight: 800; font-size: var(--txt-sec); white-space: nowrap; }
@media (max-width: 480px) {
  .fl-hm-barra { height: 28px; }
  .fl-hm-leyenda { grid-template-columns: 1fr 1fr; }
}

/* Historial de registro de flooring: un renglón por evento (promotor + fecha/hora),
   clicable a la ficha del promotor. */
.fl-historial { list-style: none; margin: 0; padding: 0; }
.fl-historial-item {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 6px 12px;
  padding: 10px 2px; border-bottom: 1px solid var(--color-borde-suave);
}
.fl-historial-item:last-child { border-bottom: 0; }
.fl-hist-promotor {
  display: inline-flex; align-items: center; gap: 9px; min-width: 0;
  border: 0; background: none; padding: 0; cursor: pointer; text-align: left;
  font-family: var(--fuente-cuerpo); font-weight: 700; font-size: var(--txt-md, .9rem); color: var(--color-texto);
}
.fl-hist-promotor--sin { cursor: default; }
.fl-hist-avatar {
  width: 32px; height: 32px; flex: 0 0 auto; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-superficie-alt); color: var(--color-acento);
  border: 1px solid var(--color-borde-suave); font-size: 1rem;
}
.fl-hist-nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (hover: hover) {
  button.fl-hist-promotor:hover .fl-hist-avatar { border-color: var(--color-acento); }
}
.fl-hist-meta { display: inline-flex; align-items: center; gap: var(--esp-md); flex-wrap: wrap; font-size: var(--txt-sec); color: var(--color-texto-suave); }
.fl-hist-fecha { display: inline-flex; align-items: center; gap: var(--esp-xs); font-variant-numeric: tabular-nums; }
.fl-hist-fecha i, .fl-hist-foto i { color: var(--color-acento); }
.fl-hist-frentes { font-weight: 700; }
.fl-hist-foto { display: inline-flex; align-items: center; }

/* --- Historial agrupado por promotor (acordeón) ---------------------------- */
/* El <details> cerrado oculta su contenido por hoja del navegador, y ese ocultado
   lo pisa cualquier `display` propio. Por eso .fl-hist-detalle NO lleva display
   suelto: sólo se le da cuando el padre está [open]. Es la misma trampa que hizo
   que los cuatro paneles del menú superior ocuparan sitio estando cerrados. */
/* display:block porque .fl-historial-item es flex y el <details> quedaría como un
   ítem flex encogido en vez de ocupar la fila entera. */
.fl-hist-grupo { display: block; padding: 0; }
.fl-hist-det > summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--esp-md);
  flex-wrap: wrap; padding: var(--esp-sm) 0; cursor: pointer; list-style: none;
}
.fl-hist-det > summary::-webkit-details-marker { display: none; }
@media (hover: hover) { .fl-hist-det > summary:hover .fl-hist-nom { text-decoration: underline; } }
/* El nombre ya no es un botón —el clic de la fila despliega— así que se le da el
   peso que tenía el enlace para que siga leyéndose como lo importante de la fila. */
.fl-hist-sum .fl-hist-nom { font-weight: 700; color: var(--color-texto); }
.fl-hist-veces {
  font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap;
  padding: 2px 9px; border-radius: 999px;
  background: var(--color-fondo-alt, rgba(127,127,140,.12)); color: var(--color-texto);
}
.fl-hist-caret { transition: transform .18s ease; color: var(--color-texto-suave); }
.fl-hist-det[open] > summary .fl-hist-caret { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .fl-hist-caret { transition: none; } }

/* Las DOS reglas, no sólo la de [open]: el ocultado nativo del <details> cerrado
   deja de pintar el CONTENIDO pero conserva la caja del elemento, así que el relleno
   y el borde superior punteado seguían dibujándose y quedaba una franja vacía de
   ~17 px bajo cada promotor. Medido: con el acordeón cerrado, altura > 0. */
.fl-hist-det:not([open]) > .fl-hist-detalle { display: none; }
.fl-hist-det[open] > .fl-hist-detalle { display: grid; }
.fl-hist-detalle {
  gap: var(--esp-xs); list-style: none; margin: 0 0 var(--esp-sm);
  padding: var(--esp-sm) 0 var(--esp-sm) 42px;   /* alineado bajo el nombre, no bajo el avatar */
  border-top: 1px dashed var(--color-borde-suave);
}
.fl-hist-detalle > li {
  display: flex; align-items: center; gap: var(--esp-md); flex-wrap: wrap;
  font-size: var(--txt-peq, .85rem); color: var(--color-texto-suave);
}
.fl-hist-detalle time { display: inline-flex; align-items: center; gap: var(--esp-xs);
                        font-variant-numeric: tabular-nums; }
.fl-hist-detalle time i, .fl-hist-detalle .fl-hist-foto i { color: var(--color-acento); }
.fl-hist-ficha { margin-top: var(--esp-2xs); }

/* Filtro por mes de la sección de registros (fotos + historial) del flooring. */
.fl-reg-filtro {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px;
  margin: 22px 0 10px;
}
.fl-reg-filtro-tit { font-family: var(--fuente-titulo); font-weight: 700; font-size: 1.05rem; display: inline-flex; align-items: center; gap: var(--esp-sm); }
.fl-reg-filtro-form { display: inline-flex; align-items: center; gap: var(--esp-sm); font-size: var(--txt-peq); color: var(--color-texto-suave); }
.fl-reg-filtro-form label { display: inline-flex; align-items: center; gap: var(--esp-xs); font-weight: 700; }
.fl-reg-filtro-form label i { color: var(--color-acento); }
.fl-reg-filtro-form input[type="month"] {
  margin: 0; padding: 7px 10px; border: 2px solid var(--color-borde); border-radius: var(--radio);
  background: var(--color-superficie); color: var(--color-texto); font-family: var(--fuente-cuerpo); font-weight: 700;
}

/* --- Métricas / dashboard ---------------------------------------------------- */
.metricas-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 16px;
}
@media (min-width: 640px) { .metricas-grid { grid-template-columns: repeat(4, 1fr); } }
.metricas-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid-fotos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 8px;
}
.foto-miniatura {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 4px;
  border: 2px solid var(--color-borde-suave);
}
.btn-foto-accion { width: 100%; margin-top: 4px; padding: 4px; }
.metrica {
  position: relative;
  border: 2px solid var(--color-borde); border-radius: var(--radio);
  background: var(--color-superficie); padding: 16px 14px 14px; box-shadow: var(--sombra-dura-sm);
  overflow: hidden;
  transition: transform .12s ease, box-shadow .15s ease;
}
.metrica::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  background: linear-gradient(90deg, var(--color-acento), var(--color-energia));
}
@media (hover: hover) {
  .metrica:hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0 var(--color-borde); }
}
.metrica { container-type: inline-size; }
.metrica .valor {
  font-family: var(--fuente-titulo);
  /* Tamaño fluido: en tarjetas estrechas el número se reduce solo para no
     desbordar (fallback fijo si el navegador no soporta unidades cq). */
  font-size: 1.5rem;
  font-size: clamp(1.3rem, 14cqw, 1.5rem);
  font-weight: 800;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.metrica .etiqueta { font-size: var(--txt-xs, .72rem); color: var(--color-texto-suave); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; margin-top: 3px; }
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px; border-radius: 20px;
  font-size: var(--txt-xs, .72rem); font-weight: 700; letter-spacing: .03em;
  border: 1px solid currentColor;
  /* Una pastilla NO se parte. Sin esto, "Hace 21 d" caía en dos lineas cuando la
     columna venia justa y esa fila crecia 18 px de mas, dejando la tabla con filas
     de tres alturas distintas. Son etiquetas cortas: o caben, o se ensancha la
     columna, pero nunca se parten. */
  white-space: nowrap;
}
.badge::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: 0 0 auto;
}
/* Tinta por el token de TEXTO: sobre este rosa fijo, --color-alerta-alto
   (#D7263D) daba 4,05:1 y "No realizada" es el badge que hay que leer;
   --color-alerta-alto-txt sube a 5,37:1. El fondo pastel sí sigue siendo fijo
   porque el @media oscuro reescribe la regla entera más abajo. */
.badge-alto { background: #FDE2E1; color: var(--color-alerta-alto-txt); }
/* Nivel intermedio: faltaba y admin/errores.php ya lo usaba, así que la etiqueta
   "Fallo" salía sin estilo. Ámbar, entre el rojo de "alto" y el verde de "bajo". */
.badge-medio { background: #FCEBCF; color: #8A5A00; }
.badge-bajo { background: #E1F3E7; color: var(--color-alerta-bajo); }
.badge-normal { background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); }
.badge-btn { cursor: pointer; font: inherit; transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1); }
.badge-btn:hover { transform: translateY(-1px); box-shadow: var(--sombra-suave); }
.badge-btn:active { transform: translateY(0); }

/* --- Resumen ejecutivo: componentes de los cuadros de inteligencia --------- */
.tarjeta-nota { font-size: var(--txt-sec, .78rem); font-weight: 600; }
.tarjeta h3 i { color: var(--color-acento); }
.chart-context { margin: -4px 0 4px; color: var(--color-texto-suave); font-size: var(--txt-sec); line-height: 1.35; }
.chart-insight { margin: 10px 0 0; padding: 8px 10px; border-left: 3px solid var(--color-acento); background: var(--color-superficie-alt); color: var(--color-texto); font-size: var(--txt-sec); line-height: 1.4; }
.chart-insight i { color: var(--color-acento-fuerte); margin-right: 4px; }
.filter-memory { display: flex; align-items: center; gap: var(--esp-sm); flex-wrap: wrap; margin-top: var(--esp-md); padding-top: var(--esp-md); border-top: 1px solid var(--color-borde-suave); }
.filter-memory-note { color: var(--color-texto-suave); font-size: var(--txt-sec); margin-right: auto; }
.filter-memory-note i { color: var(--color-acento-fuerte); margin-right: 4px; }
.ff-filtros > .filter-memory { grid-column: 1 / -1; }
@media (max-width: 560px) { .filter-memory-note { width: 100%; } }

/* Buscador global del admin: evita recorrer Reportes > grupo > pantalla cuando
   el usuario ya sabe qué quiere consultar. */
.admin-quick-trigger { width: auto; padding: 7px 10px; gap: 7px; margin-left: auto; color: var(--chrome-texto); }
.admin-quick-trigger span { font-size: var(--txt-sec); font-weight: 700; }
.admin-quick-trigger kbd { border: 1px solid rgba(255,255,255,.28); border-radius: 5px; padding: 2px 5px; color: inherit; font: 600 .66rem var(--fuente-cuerpo); }
.admin-quick { width: min(680px, calc(100vw - 24px)); max-height: min(760px, calc(100vh - 24px)); padding: 0; overflow: hidden; }
.admin-quick::backdrop { background: rgba(20,21,31,.62); backdrop-filter: blur(2px); }
.admin-quick-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 18px 18px 10px; }
.admin-quick-head h2 { margin: 2px 0 0; font-size: 1.3rem; }
.admin-quick-search { display: flex; align-items: center; gap: 8px; margin: 0 18px 12px; padding: 0 12px; border: 2px solid var(--color-borde); border-radius: var(--radio); background: var(--color-superficie); }
.admin-quick-search:focus-within { outline: 3px solid color-mix(in srgb, var(--color-acento) 35%, transparent); }
.admin-quick-search input { border: 0; box-shadow: none; outline: 0; width: 100%; padding: 12px 0; background: transparent; color: var(--color-texto); }
.admin-quick-list { max-height: min(510px, calc(100vh - 220px)); overflow-y: auto; padding: 0 10px 8px; }
.admin-quick-list a { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 10px; border-radius: var(--radio); color: var(--color-texto); text-decoration: none; }
.admin-quick-list a[hidden] { display: none; }
.admin-quick-list a:hover, .admin-quick-list a:focus-visible { background: var(--color-superficie-alt); }
.admin-quick-list a.es-actual { box-shadow: inset 3px 0 0 var(--color-acento); background: color-mix(in srgb, var(--color-acento) 9%, transparent); }
.admin-quick-list a span { display: flex; flex-direction: column; min-width: 0; }
.admin-quick-list a strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-quick-list a small { color: var(--color-texto-suave); }
.admin-quick-list a > i { color: var(--color-acento-fuerte); flex: 0 0 auto; }
.admin-quick-empty { padding: 24px 12px; text-align: center; color: var(--color-texto-suave); }
.admin-quick-help { margin: 0; padding: 9px 18px 12px; border-top: 1px solid var(--color-borde-suave); color: var(--color-texto-suave); font-size: var(--txt-micro); }
.admin-back-top { position: fixed; right: 18px; bottom: 18px; z-index: 28; width: 44px; height: 44px; display: grid; place-items: center; border: 2px solid var(--color-borde); border-radius: 50%; background: var(--color-superficie); color: var(--color-texto); box-shadow: var(--sombra-dura-sm); cursor: pointer; }
.admin-back-top[hidden] { display: none; }
@media (max-width: 700px) {
  .admin-quick-trigger { margin-left: auto; }
  .admin-quick-trigger span, .admin-quick-trigger kbd, .admin-topbar-ciudad { display: none; }
  .admin-back-top { right: 12px; bottom: 12px; }
}
/* A1: dos columnas (subieron / bajaron) */
.mov-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.mov-titulo { font-size: var(--txt-sm, .82rem); margin: 6px 0; display: flex; align-items: center; gap: 5px; }
.mov-titulo--sube { color: var(--color-alerta-bajo); }
.mov-titulo--baja { color: var(--color-alerta-alto); }
.mov-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.mov-lista li { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: .84rem; }
.mov-modelo { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mov-pct { font-weight: 800; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.mov-pct--sube { color: var(--color-alerta-bajo); }
.mov-pct--baja { color: var(--color-alerta-alto); }
/* B8 / C13: mosaico de números */
.cobertura-mosaico { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; }
.cobertura-item { text-align: center; padding: 10px 6px; border: 2px solid var(--color-borde-suave); border-radius: var(--radio); background: var(--color-superficie-alt); }
.cobertura-item strong { display: block; font-family: var(--fuente-titulo); font-size: 1.5rem; font-weight: 800; line-height: 1; }
.cobertura-item span { font-size: var(--txt-xs, .72rem); font-weight: 700; color: var(--color-texto-suave); }
/* B8: barra de progreso de cobertura */
.barra-progreso { height: 10px; border-radius: 999px; background: var(--color-superficie-alt); border: 1px solid var(--color-borde-suave); overflow: hidden; margin-top: 12px; }
.barra-progreso span { display: block; height: 100%; background: var(--color-acento); border-radius: 999px; }
/* C13: lista simple de novedades */
.lista-simple { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; font-size: var(--txt-peq, .86rem); }
.lista-simple i { color: var(--color-acento); }

table.tabla-admin { width: 100%; border-collapse: collapse; font-size: .88rem; }
table.tabla-admin th, table.tabla-admin td {
  padding: 11px 10px; border-bottom: 1px solid var(--color-borde-suave); text-align: left;
}
table.tabla-admin thead th {
  font-family: var(--fuente-titulo); font-size: .74rem; letter-spacing: .05em;
  text-transform: uppercase; color: var(--color-texto);
  background: var(--color-superficie-alt); border-bottom: 2px solid var(--color-borde);
}
table.tabla-admin tbody tr { transition: background-color .12s ease; }
table.tabla-admin tbody tr:nth-child(even) {
  background: color-mix(in srgb, var(--color-texto) 3%, transparent);
}
@media (hover: hover) {
  table.tabla-admin tbody tr:hover { background: var(--color-superficie-alt); }
}
table.tabla-admin td { font-variant-numeric: tabular-nums; }
.tabla-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.tabla-scroll table { min-width: 620px; }

@media (max-width: 900px) {
  .tabla-scroll.tabla-card-scroll {
    overflow: visible;
  }
  table.tabla-admin.tabla-admin--cards {
    display: block;
    min-width: 0;
    border-collapse: separate;
  }
  table.tabla-admin.tabla-admin--cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }
  table.tabla-admin.tabla-admin--cards tbody {
    display: grid;
    gap: 12px;
  }
  table.tabla-admin.tabla-admin--cards tr {
    display: block;
    border: 2px solid var(--color-borde);
    border-radius: var(--radio);
    background: var(--color-superficie);
    box-shadow: var(--sombra-dura-sm);
    overflow: hidden;
  }
  table.tabla-admin.tabla-admin--cards tbody tr:nth-child(even) {
    background: var(--color-superficie);
  }
  table.tabla-admin.tabla-admin--cards td {
    display: grid;
    grid-template-columns: minmax(92px, .42fr) minmax(0, 1fr);
    gap: 12px;
    align-items: start;
    border-bottom: 1px solid var(--color-borde-suave);
    padding: 10px 12px;
    text-align: right;
  }
  table.tabla-admin.tabla-admin--cards td:last-child { border-bottom: 0; }
  table.tabla-admin.tabla-admin--cards td::before {
    content: attr(data-label);
    color: var(--color-texto-suave);
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    text-align: left;
  }
  table.tabla-admin.tabla-admin--cards td[data-full-row="true"] {
    display: block;
    text-align: left;
  }
  table.tabla-admin.tabla-admin--cards td[data-full-row="true"]::before {
    content: none;
  }
  table.tabla-admin.tabla-admin--cards td form {
    justify-content: flex-end;
  }
}

/* En tablets (600–900px) las tarjetas de tabla admin van a 2 columnas para
   llenar el ancho y que el rango intermedio no quede 'a medias'. La matriz de
   comparativo no usa .tabla-admin, así que no se ve afectada. — ddchack */
@media (min-width: 600px) and (max-width: 900px) {
  table.tabla-admin.tabla-admin--cards tbody {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
  }
}

.dashboard-resumen {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 16px;
}
@media (min-width: 760px) { .dashboard-resumen { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.resumen-pill {
  position: relative;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  padding: 14px 12px 12px;
  box-shadow: var(--sombra-dura-sm);
  overflow: hidden;
  transition: transform .12s ease, box-shadow .15s ease;
}
.resumen-pill::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  background: linear-gradient(90deg, var(--color-acento), var(--color-energia));
}
@media (hover: hover) {
  .resumen-pill:hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0 var(--color-borde); }
}
.resumen-pill strong {
  display: block;
  font-family: var(--fuente-titulo);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.1;
}
.resumen-pill span {
  color: var(--color-texto-suave);
  font-size: var(--txt-xs, .72rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.empty-state {
  border: 2px dashed var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-superficie);
  box-shadow: none;
  padding: 20px 16px;
  text-align: center;
  margin-bottom: 16px;
}
.empty-state img {
  display: block;
  width: min(100%, 360px);
  margin: 0 auto 10px;
}
.empty-state .empty-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin: 0 auto 12px;
  border: 2px solid var(--color-borde);
  border-radius: 50%;
  background: var(--color-alerta-fondo);
  color: var(--color-acento-fuerte);
  font-size: 1.7rem;
  box-shadow: var(--sombra-dura-sm);
}
.empty-state h3 { margin-bottom: 4px; }
.empty-state p { margin: 0; }
.empty-state .btn { max-width: 320px; margin-left: auto; margin-right: auto; }

img[loading="lazy"].img-cargando {
  background: linear-gradient(100deg,
    var(--color-superficie) 40%,
    var(--color-superficie-alt) 50%,
    var(--color-superficie) 60%);
  background-size: 200% 100%;
  color: transparent;
}
img[loading="lazy"].img-error {
  object-fit: contain;
  padding: 8px;
  background: var(--color-superficie-alt);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-gris);
}
/* Placeholder Phosphor (ph-image) en vez del icono roto del navegador: el
   pseudo-elemento se genera sobre la <img> fallida. \e2ca = ph-bold ph-image. */
img[loading="lazy"].img-error::before {
  content: "\e2ca";
  font-family: "Phosphor-Bold";
  font-size: 1.6rem;
  line-height: 1;
}
@media (prefers-reduced-motion: no-preference) {
  img[loading="lazy"].img-cargando { animation: shimmer 1.3s linear infinite; }
  img[loading="lazy"].img-cargada { animation: imagen-reveal .22s ease; }
}
@keyframes imagen-reveal {
  from { opacity: .35; }
  to { opacity: 1; }
}

.login-visual {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-dura);
  margin-bottom: 18px;
}
.brand-mark {
  width: 64px;
  height: 64px;
  border-radius: 14px;
  border: 2px solid var(--color-borde);
  box-shadow: var(--sombra-dura);
}

/* --- Login: panel dividido (marca + formulario) -------------------------
   Refinado: franja diagonal + kicker en el panel de marca, chips visibles
   también en móvil, campos con icono guía y anillo de foco. Se conserva la
   foto y toda la paleta/tipografía. Carlos David Donoso Cordero (ddchack). */
.login-shell {
  display: flex;
  flex-direction: column;
  gap: 18px;
  margin: 4px 0 24px;
}
.login-brand {
  position: relative;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  box-shadow: 6px 6px 0 var(--color-borde), 0 24px 48px rgba(34,34,59,.16);
  overflow: hidden;
  padding: 24px 22px;
  color: #F7F3EB;
  /* Capa 1: glow marigold detrás del logo · Capa 2: velo tinta · Capa 3: foto */
  background-image:
    radial-gradient(420px 300px at 16% 30%, rgba(233,148,10,.30), transparent 62%),
    linear-gradient(160deg, rgba(34,34,59,.42) 0%, rgba(34,34,59,.82) 55%, rgba(34,34,59,.96) 100%),
    url('/assets/img/brand/login-splash.jpg');
  background-size: cover;
  background-position: center;
  min-height: 300px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 16px;
}
.login-brand::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 5px;
  background: linear-gradient(90deg, var(--color-acento), var(--color-energia));
}
.login-brand::before {
  content: "";
  position: absolute;
  right: -30px;
  top: -30px;
  width: 118px;
  height: 118px;
  border: 2px solid #F7F3EB;
  transform: rotate(9deg);
  background: repeating-linear-gradient(135deg, transparent 0 9px, color-mix(in srgb, var(--color-acento) 82%, transparent) 9px 17px);
  pointer-events: none;
}
.login-brand-kicker {
  position: relative;
  z-index: 1;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--txt-micro, .7rem);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #F7F3EB;
  background: rgba(247,243,235,.12);
  border: 1px solid rgba(247,243,235,.35);
  border-radius: 99px;
  padding: 6px 12px;
}
.login-brand-kicker i { color: var(--color-energia); }
.login-brand-body {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.login-brand-logo {
  width: auto;
  height: 72px;
  max-width: 100%;
  object-fit: contain;
  align-self: flex-start;
  margin: 4px 0 10px;
  filter: drop-shadow(0 8px 22px rgba(0, 0, 0, .4));
}
.login-brand-mark {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  border: 2px solid rgba(248,245,241,.7);
  box-shadow: var(--sombra-dura-sm);
  margin-bottom: 8px;
}
.login-brand h1 { color: #F7F3EB; margin-bottom: 2px; font-size: 1.8rem; letter-spacing: -0.02em; }
.login-brand p { color: rgba(247,243,235,.9); margin: 0; font-size: .94rem; max-width: 40ch; }
.login-brand-points {
  list-style: none;
  padding: 0;
  margin: 14px 0 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.login-brand-points li {
  display: flex;
  align-items: center;
  gap: 9px;
  width: fit-content;
  font-size: var(--txt-sm, .82rem);
  font-weight: 700;
  color: rgba(247,243,235,.96);
  background: rgba(247,243,235,.1);
  border: 1px solid rgba(247,243,235,.22);
  border-radius: 99px;
  padding: 8px 15px 8px 11px;
  backdrop-filter: blur(3px);
}
.login-brand-points i { font-size: 1.05rem; color: var(--color-energia); }

.login-form-inner {
  background: var(--color-superficie);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  box-shadow: 6px 6px 0 var(--color-borde), 0 24px 48px rgba(34,34,59,.16);
  padding: 26px 24px 28px;
}
.login-form-inner h2 { font-size: 1.7rem; font-weight: 800; }
.login-sub { margin-bottom: 18px; }

/* Entrada suave: la marca primero, el formulario un pelín después */
@keyframes loginSube {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.login-brand { animation: loginSube .5s var(--ease-out, ease) backwards; }
.login-form-inner { animation: loginSube .5s var(--ease-out, ease) .1s backwards; }
@media (prefers-reduced-motion: reduce) {
  .login-brand, .login-form-inner { animation: none; }
}

/* Campo con icono guía a la izquierda; el anillo de foco lo dibuja el
   contenedor (focus-within) para no duplicar contorno con el input. */
.login-field {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr);
  align-items: center;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.login-field--pw { grid-template-columns: 44px minmax(0, 1fr) 72px; }
.login-field > i {
  justify-self: center;
  color: var(--color-texto-suave);
  font-size: 1.15rem;
}
.login-field input {
  border: 0;
  border-radius: 0;
  min-height: 52px;
  padding: 10px 12px 10px 2px;
  background: transparent;
}
.login-field input:focus { border: 0; }
.login-field input:focus-visible { outline: none; }
.login-field:focus-within {
  border-color: var(--color-energia);
  box-shadow: inset 0 0 0 2px var(--color-energia);
}
.login-field .password-toggle {
  height: 100%;
  width: 100%;
  border: 0;
  border-left: 2px solid var(--color-borde-suave);
  border-radius: 0;
  box-shadow: none;
}
.login-field .password-toggle--texto {
  display: flex; align-items: center; justify-content: center; min-height: 52px;
  padding: 0 10px; background: var(--color-superficie-alt); color: var(--color-energia-txt);
  font: inherit; font-size: .8rem; font-weight: 800; cursor: pointer;
}
.login-fila-extra { display: flex; justify-content: flex-end; margin-top: 8px; }
.login-forgot { font-weight: 700; font-size: .84rem; }

/* Botón principal del login: ancho completo, alto cómodo y flecha que empuja */
.login-btn {
  width: 100%;
  justify-content: center;
  min-height: 52px;
  font-size: 1.02rem;
  gap: 8px;
}
.login-btn i { transition: transform .18s var(--ease-out, ease); }
@media (hover: hover) {
  .login-btn:hover i { transform: translateX(4px); }
}

.login-seguro {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 14px 0 0;
  font-size: .76rem;
  color: var(--color-texto-suave);
}
.login-seguro i { color: var(--color-alerta-bajo, #1B7A43); font-size: .95rem; }
.login-foot {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 2px solid var(--color-borde-suave);
  text-align: center;
  font-size: var(--txt-md, .9rem);
}

/* En teléfono el objetivo es entrar, no recorrer una landing antes del
   formulario. La fotografía de marca queda como cabecera compacta. */
@media (max-width: 899px) {
  .login-shell { gap: 10px; margin-top: 0; }
  .login-brand {
    min-height: 112px; padding: 16px 18px; justify-content: center;
    box-shadow: 3px 3px 0 var(--color-borde), 0 12px 26px rgba(34,34,59,.12);
    background-position: center 43%;
  }
  .login-brand-kicker, .login-brand p, .login-brand-points { display: none; }
  .login-brand-body { align-items: center; }
  .login-brand-logo { height: 58px; margin: 0; align-self: center; }
  .login-form-inner { padding: 21px 18px 22px; box-shadow: 3px 3px 0 var(--color-borde), 0 12px 26px rgba(34,34,59,.12); }
  .login-form-inner > .step-kicker { display: none; }
  .login-form-inner h2 { font-size: 1.5rem; margin-bottom: 4px; }
  .login-sub { margin-bottom: 14px; }
}

@media (min-width: 900px) {
  .login-shell {
    flex-direction: row;
    align-items: stretch;
    min-height: calc(100vh - 160px);
  }
  .login-brand {
    flex: 1 1 55%;
    padding: 44px;
    justify-content: space-between;
  }
  .login-brand-mark { width: 68px; height: 68px; }
  .login-brand-logo { height: 92px; }
  .login-brand h1 { font-size: 2.9rem; }
  .login-brand p { font-size: 1.08rem; }
  .login-form-panel {
    flex: 1 1 45%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .login-form-inner { width: 100%; max-width: 420px; padding: 34px 32px 32px; }
}

.terminos-contenido { white-space: pre-line; line-height: 1.6; }

.producto-summary {
  display: flex;
  gap: 10px;
  align-items: center;
}
.categoria-icon {
  width: 48px;
  height: 48px;
  object-fit: contain;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-fondo);
  flex: 0 0 auto;
}
.producto-summary-text { min-width: 0; }

/* --- Productos: acordeón de marca → modelos ----------------------------- */
.marca-acordeon > summary,
.modelo-item > summary { list-style: none; }
.marca-acordeon > summary::-webkit-details-marker,
.modelo-item > summary::-webkit-details-marker { display: none; }
.marca-acordeon > summary::marker,
.modelo-item > summary::marker { content: ""; }

.marca-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.marca-summary-main {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}
.marca-summary-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.marca-summary-text strong { font-family: var(--fuente-titulo); font-size: 1.1rem; }
.marca-summary-text .text-suave { font-family: var(--fuente-cuerpo); font-size: .76rem; font-weight: 700; }

.acordeon-chevron {
  flex: 0 0 auto;
  font-size: 1.1rem;
  color: var(--color-texto-suave);
  transition: transform .2s ease;
}
details[open] > summary .acordeon-chevron { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .acordeon-chevron { transition: none; } }

.marca-modelos {
  margin-top: 14px;
  border-top: 2px solid var(--color-borde);
}
.modelo-item { border-bottom: 1px solid var(--color-borde-suave); }
.modelo-item:last-child { border-bottom: 0; }
.modelo-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 2px;
}
.modelo-summary .producto-summary { min-width: 0; }
.modelo-summary .producto-summary-text strong { font-size: .98rem; }
.modelo-item[open] > .modelo-summary { border-bottom: 1px dashed var(--color-borde-suave); }
.modelo-item .mt-md { padding: 6px 2px 16px; }

/* --- Avatar de perfil -------------------------------------------------- */
.avatar-perfil-fila {
  display: flex;
  align-items: center;
  gap: 14px;
}
.avatar-circulo {
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--color-borde);
  box-shadow: var(--sombra-dura-sm);
  flex: 0 0 auto;
}
.avatar-grande { width: 72px; height: 72px; }
.avatar-inicial {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-texto);
  color: #F8F5F1;
  font-family: var(--fuente-titulo);
  font-weight: 700;
  font-size: 1.6rem;
}

/* --- Logotipo de marca: nunca se deforma (contain, no crop) ------------- */
.logo-marca {
  width: 64px;
  height: 64px;
  object-fit: contain;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  padding: 4px;
  flex: 0 0 auto;
}
.logo-marca-sm {
  width: 44px;
  height: 44px;
  object-fit: contain;
  border: 1px solid var(--color-borde-suave);
  border-radius: 4px;
  background: var(--color-superficie);
  padding: 3px;
  flex: 0 0 auto;
}

.registro-hero {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 16px;
}
.registro-hero img {
  width: 72px;
  height: 72px;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-dura-sm);
}
.registro-form {
  display: grid;
  gap: 16px;
}
.form-section {
  border-top: 2px solid var(--color-borde);
  padding-top: 14px;
}
.form-section:first-of-type {
  border-top: 0;
  padding-top: 0;
}
.section-title {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin-bottom: 4px;
}
.section-title > i {
  width: 42px;
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-acento);
  box-shadow: var(--sombra-dura-sm);
  font-size: 1.35rem;
  flex: 0 0 auto;
}
.section-title h2 {
  font-size: 1.18rem;
  margin-bottom: 0;
}
.section-title p {
  margin: 0;
  color: var(--color-texto-suave);
  font-size: var(--txt-peq, .86rem);
}
.password-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 52px;
  gap: 8px;
}
.password-toggle {
  border: 2px solid var(--color-borde);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
}
.password-meter {
  height: 10px;
  border: 2px solid var(--color-borde);
  border-radius: 99px;
  margin-top: 8px;
  background: var(--color-fondo);
  overflow: hidden;
}
.password-meter span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--color-alerta-alto);
}
.password-meter[data-score="2"] span { background: var(--color-acento); }
.password-meter[data-score="3"] span { background: var(--color-alerta-bajo); }
.password-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0;
  margin: 10px 0 0;
  list-style: none;
}
.password-checks li {
  border: 2px solid var(--color-borde-suave);
  border-radius: var(--radio);
  padding: 6px 9px;
  font-size: var(--txt-sm, .82rem);
  font-weight: 800;
  color: var(--color-texto-suave);
}
.password-checks li.ok {
  border-color: var(--color-alerta-bajo);
  background: #E1F3E7;
  color: var(--color-alerta-bajo);
}
.ayuda.ok { color: var(--color-alerta-bajo); font-weight: 800; }
/* Token de TINTA, no el de relleno: esto es texto ("la cédula debe tener 10
   dígitos", "las contraseñas no coinciden") y la clase NO es CSS muerto — se la
   pone registro.js a #cedulaAyuda y #passwordMatch en el alta pública. */
.ayuda.error { color: var(--color-alerta-alto-txt); font-weight: 800; }
.terminos-box {
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  padding: 12px;
  background: var(--color-alerta-fondo);
}
.terminos-box-scroll {
  max-height: 46px;
  overflow-y: auto;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-alerta-fondo);
  padding: 10px 12px;
  margin: 4px 0 12px;
  font-size: .85rem;
  line-height: 1.5;
  color: var(--color-texto);
}
.terminos-box-scroll a { color: var(--color-acento-fuerte); font-weight: 700; }
.terminos-box-scroll:focus-visible {
  outline: 3px solid var(--color-energia-txt);
  outline-offset: 2px;
}

/* --- Cropper wrapper --------------------------------------------------------- */
.cropper-wrapper { max-height: 55vh; overflow: hidden; border: 2px solid var(--color-borde); border-radius: var(--radio); background:#000; }
.cropper-wrapper img { display: block; max-width: 100%; }
/* Vista previa de foto SIN recorte (attachFoto): muestra la imagen COMPLETA con
   su forma real (object-fit: contain), tal como se guardará y se verá al
   ampliarla. En las listas/tarjetas la miniatura sigue siendo cuadrada (cover). */
.foto-preview {
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-superficie-alt);
  padding: 8px;
  text-align: center;
}
.foto-preview img {
  display: inline-block;
  max-width: 100%;
  max-height: 260px;
  object-fit: contain;
  border-radius: 6px;
}

/* --- Escaneo de código de barras: CTA del paso 1 + modal de cámara --------- */
.escaneo-cta {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  color: var(--color-texto);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura);
  padding: 14px 16px;
  transition: transform .1s ease, box-shadow .15s ease;
}
@media (hover: hover) {
  .escaneo-cta:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0 var(--color-borde); }
}
.escaneo-cta > .ph-barcode { font-size: 2rem; color: var(--color-acento); flex: 0 0 auto; }
.escaneo-cta-txt { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.escaneo-cta-txt strong { font-size: 1rem; }
.escaneo-cta-txt small { color: var(--color-texto-suave); font-size: .8rem; }
.escaneo-cta-flecha { color: var(--color-texto-suave); flex: 0 0 auto; }
.separador-o { display: flex; align-items: center; gap: 10px; margin: 14px 0; color: var(--color-texto-suave); font-size: var(--txt-sec, .78rem); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.separador-o::before, .separador-o::after { content: ''; flex: 1; height: 1px; background: var(--color-borde-suave); }

.escaner-modal {
  border: none; padding: 0;
  border-radius: var(--radio-lg);
  width: min(460px, calc(100vw - 24px));
  background: var(--color-superficie);
  color: var(--color-texto);
}
.escaner-modal::backdrop { background: rgba(20, 21, 31, .92); }
.escaner-inner { padding: 16px; }
.escaner-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.escaner-video-wrap {
  position: relative; width: 100%; aspect-ratio: 4 / 3;
  background: #000; border-radius: var(--radio); overflow: hidden;
}
#escanerVideo { width: 100%; height: 100%; object-fit: cover; display: block; }
.escaner-marco {
  position: absolute; inset: 20% 10%;
  border: 3px solid rgba(255, 255, 255, .92); border-radius: 12px;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .28);
}
.escaner-estado { text-align: center; font-weight: 600; font-size: .88rem; margin: 12px 0 6px; }
.escaner-manual { margin-top: 8px; }
.escaner-manual-row { display: flex; gap: 8px; margin-top: 4px; }
.escaner-manual-row input { flex: 1; min-width: 0; }
.escaner-acciones { display: flex; justify-content: center; margin-top: 10px; }

/* --- Avatar de perfil: foto redonda en el topbar y la barra inferior -------- */
.nav-avatar { padding: 5px; }
.nav-avatar-img {
  width: 30px; height: 30px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  border: 1px solid rgba(255, 255, 255, .5);
}
.nav-avatar-img--sm { width: 24px; height: 24px; border-width: 0; }

/* Descripción del producto en el modal "Producto reconocido". */
.pr-descripcion { margin: 10px 0 0; font-size: var(--txt-md, .9rem); line-height: 1.4; }

/* --- "Cerca de ti": almacenes cercanos por GPS en el paso de almacén ------- */
.cercanos-seccion {
  border: 1px dashed var(--color-acento);
  border-radius: var(--radio);
  padding: 10px;
  background: var(--color-alerta-fondo);
}
.cercanos-cabecera { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cercanos-cabecera .selected-note { margin: 0; }
.dist-chip {
  flex: 0 0 auto;
  align-self: center;
  font-size: var(--txt-xs, .72rem);
  font-weight: 800;
  color: var(--color-acento-fuerte);
  background: var(--color-alerta-fondo);
  border: 1px solid var(--color-borde-suave);
  border-radius: 999px;
  padding: 3px 9px;
  white-space: nowrap;
}

/* --- Zona de subida de foto: reemplaza el <input type=file> nativo, que se
   mimetiza con el fondo. El input real queda oculto (.sr-only) y este label
   actua como boton visible. --- */
.upload-dropzone {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 52px;
  border: 2px dashed var(--color-acento);
  border-radius: var(--radio);
  padding: 12px 16px;
  margin-top: 6px;
  color: var(--color-acento-fuerte);
  font-weight: 700;
  font-size: .92rem;
  text-align: center;
  cursor: pointer;
  background: var(--color-alerta-fondo);
  transition: border-color .15s ease, background-color .15s ease, transform .1s ease;
}
.upload-dropzone i { font-size: 1.3rem; }
@media (hover: hover) {
  .upload-dropzone:hover { border-color: var(--color-acento-fuerte); background: #FCE8C7; transform: translateY(-1px); }
}
.upload-dropzone.en-foco {
  outline: 3px solid var(--color-energia);
  outline-offset: 3px;
}
.upload-dropzone.is-loading {
  border-style: solid;
  background: var(--color-superficie-alt);
}
.upload-dropzone.tiene-foto,
.upload-dropzone.foto-lista {
  border-color: var(--color-alerta-bajo);
  color: var(--color-alerta-bajo);
  background: #E1F3E7;
}

/* --- Selector de foto con doble opción: "Tomar foto" (camara directa) y
   "Elegir archivo" (galeria). Reemplaza al dropzone unico para que el promotor
   no pierda el acceso directo a la camara en el movil. Dos botones lado a lado
   que se apilan en pantallas muy estrechas. --- */
.foto-chooser {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
}
.foto-chooser-btn {
  flex: 1 1 140px;
  min-height: 48px;
  justify-content: center;
  cursor: pointer;
}
/* El campo de archivo va dentro de la etiqueta y oculto a la vista, así que el
   anillo de foco caería sobre algo invisible. Se traslada a la etiqueta, que es
   lo que el usuario ve: un foco que no se ve es igual de inútil que no tenerlo. */
.foto-chooser-btn:focus-within {
  outline: 2px solid var(--color-acento);
  outline-offset: 2px;
}
.foto-chooser-estado {
  margin: 6px 0 0;
  font-size: .88rem;
  font-weight: 600;
  color: var(--color-texto-suave);
}
.foto-chooser-estado.ok { color: var(--color-alerta-bajo); }

.oculto { display: none !important; }
.text-suave { color: var(--color-texto-suave); }
.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.mb-0 { margin-bottom: 0; }
.mt-neg-sm { margin-top: -6px; }
.mt-xs { margin-top: 8px; }
.mt-sm { margin-top: 10px; }
.mt-md { margin-top: 14px; }
/* .mt-14, .mb-xs y .mb-md no existian, pero ocho paginas las usan: quien tiene
   .mt-md y .mb-14 delante da por hecho que existen sus espejos, y una clase que no
   existe no avisa. Se completan las parejas con los mismos valores. */
.mt-14 { margin-top: 14px; }
.mb-xs { margin-bottom: 8px; }
.mb-md { margin-bottom: 14px; }
/* Fila de botones de descarga de plantillas (admin/sell-importar.php). Tenia la
   clase en el marcado y ninguna regla: los botones salian pegados en linea. */
.imp-plantillas-btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.mt-lg { margin-top: 18px; }
.my-sm { margin: 10px 0; }
.mb-14 { margin-bottom: 14px; }
.mb-20 { margin-bottom: 20px; }
.pt-16 { padding-top: 16px; }
.pt-24 { padding-top: 24px; }
.flex-fin { display:flex; justify-content:flex-end; gap:10px; }
.fila-entre { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; }
.fila-acciones { display: flex; gap: 8px; flex-wrap: wrap; }
.fila-inline { display: flex; gap: 6px; align-items: center; }
.select-compacto { min-height: 40px; width: auto; }

.paginacion { gap: 10px; }
.btn-deshabilitado { opacity: .4; pointer-events: none; }

/* --- Cobertura: mapa de Ecuador (asesores + visitas a almacenes) --------- */
.mapa-cobertura-tarjeta {
  border: 2px solid var(--color-borde);
  box-shadow: var(--sombra-dura);
}
.mapa-cobertura {
  height: 460px;
  width: 100%;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-fondo);
  /* Leaflet reparte z-index altos entre sus capas y controles (400 los paneles,
     hasta 1000 los controles de zoom y la atribución). Sin un contexto de
     apilamiento propio esos valores compiten con el resto de la página y el
     mapa acaba dibujándose ENCIMA de la barra superior, que está en z-index 20,
     tapando el título. Con position:relative + z-index:0 todos esos valores
     quedan encerrados dentro del contenedor y el mapa vuelve a su sitio, por
     debajo de la marquesina. No se sube el z-index de .topbar a propósito: eso
     la pondría por delante de modales y avisos, cambiando un problema por otro. */
  position: relative;
  z-index: 0;
}
@media (max-width: 720px) {
  .mapa-cobertura { height: min(72vh, 420px); }
}
.mapa-leyenda-toggle { display: flex; gap: 8px; flex-wrap: wrap; }
.chip-capa {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  border: 2px solid var(--color-borde-suave);
  border-radius: 20px;
  padding: 8px 14px 8px 10px;
  background: var(--color-superficie);
  color: var(--color-texto-suave);
  font-weight: 700;
  font-size: var(--txt-sec, .78rem);
  cursor: pointer;
  transition: border-color .15s ease, color .15s ease, background-color .15s ease;
}
.chip-capa .punto-leyenda {
  width: 10px; height: 10px; border-radius: 50%; display: inline-block;
  background: var(--color-gris);
}
.chip-capa-asesores .punto-leyenda { background: var(--color-texto); }
.chip-capa-visitas .punto-leyenda { background: var(--color-energia); }
.chip-capa.activo { border-color: var(--color-borde); color: var(--color-texto); background: var(--color-alerta-fondo); }

.mapa-leyenda {
  background: var(--color-superficie);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-dura-sm);
  padding: 8px 10px;
  font-family: var(--fuente-cuerpo);
  font-size: var(--txt-sec, .78rem);
  color: var(--color-texto);
  line-height: 1.6;
}
.mapa-leyenda-fila { display: flex; align-items: center; gap: 6px; font-weight: 700; }
.mapa-leyenda-fila .punto-leyenda { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
.mapa-leyenda-nota { color: var(--color-texto-suave); font-weight: 400; font-size: var(--txt-micro, .7rem); margin-top: 2px; }

/* Popup de Leaflet totalmente re-estilizado (sin flecha ni chrome default) */
.leaflet-popup.popup-cobertura-wrapper .leaflet-popup-content-wrapper {
  background: var(--color-superficie);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-dura-sm);
  padding: 2px;
}
.leaflet-popup.popup-cobertura-wrapper .leaflet-popup-tip { display: none; }
.leaflet-popup.popup-cobertura-wrapper .leaflet-popup-content { margin: 8px 10px; }
.popup-cobertura strong { font-family: var(--fuente-titulo); font-size: .95rem; display: block; margin-bottom: 4px; }
.popup-cobertura-fila { display: flex; align-items: center; gap: 6px; font-size: var(--txt-sm, .82rem); color: var(--color-texto-suave); }
.popup-cobertura-fila i { color: var(--color-acento-fuerte); }

/* El basemap Carto siempre es claro; el "chrome" del mapa (leyenda y popups) se
   mantiene en tinta clara fija aunque la app esté en modo oscuro, para no dejar
   cajas oscuras flotando sobre un mapa claro. Desarrollado por Carlos David
   Donoso Cordero (ddchack). */
.mapa-leyenda,
.leaflet-popup.popup-cobertura-wrapper .leaflet-popup-content-wrapper {
  background: #FFFFFF;
  border-color: #22223B;
  color: #22223B;
}
.mapa-leyenda,
.mapa-leyenda-fila { color: #22223B; }
.mapa-leyenda-nota { color: #565670; }
.popup-cobertura strong { color: #22223B; }
.popup-cobertura-fila { color: #565670; }
.popup-cobertura-fila i { color: #9A6103; }

/* --------------------------------------------------------------------------
   PARTICIPACIÓN EN PERCHA (admin) — lista/mapa por almacén + pantalla de
   detalle con gráficos. Reutiliza .mapa-cobertura/.popup-cobertura del mapa
   de Cobertura y .tabla-admin/.tarjeta de siempre; solo lo específico va acá.
   -------------------------------------------------------------------------- */
.percha-tabs { display: flex; gap: var(--esp-sm); margin-bottom: var(--esp-lg); overflow-x: auto; -webkit-overflow-scrolling: touch; }
.percha-tab {
  flex: 0 0 auto; border: 1.5px solid var(--color-borde); background: var(--color-superficie);
  color: var(--color-texto); border-radius: 999px; padding: 9px 16px; font-weight: 700; font-size: .84rem;
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px; min-height: 44px;
  transition: background-color .12s ease, color .12s ease;
}
@media (hover: hover) { .percha-tab:hover { background: var(--color-superficie-alt); } }
/* El fondo baja a --color-energia-txt (#C24418), el tono OSCURO de la misma
   familia que el proyecto ya usa cuando la energía tiene que ser legible. Con
   #FF5A1F el blanco encima daba 3,12:1 y el mínimo para texto normal es 4,5. Es
   el mismo naranja, sólo que uno que deja leer lo que lleva dentro. */
.percha-tab.activo {
  background: var(--color-energia-txt, #C24418); color: #fff; border-color: var(--color-energia-txt, #C24418);
  box-shadow: 0 2px 10px rgba(194, 68, 24, .30);
}
.percha-tab.activo i { color: #fff; }

/* Popup del mapa: secciones extra de categorías/marcas + enlace al detalle */
.popup-percha-seccion { font-size: var(--txt-sec, .78rem); color: #565670; margin-top: 4px; line-height: 1.35; }
.popup-percha-seccion i { color: #9A6103; margin-right: 3px; }
.popup-percha-link {
  display: inline-block; margin-top: 8px; font-size: .8rem; font-weight: 800;
  color: #9A6103; text-decoration: none;
}
.popup-percha-link:hover { text-decoration: underline; }

/* Pantalla de detalle: 2 cards (mapa + datos) lado a lado en desktop */
.percha-detalle-cards { display: grid; gap: var(--esp-lg); grid-template-columns: 1fr; margin-bottom: var(--esp-lg); }
/* align-items: start — por defecto el grid ESTIRA las dos tarjetas a la altura
   de la más alta, y eso aquí sale caro: medido en una tienda sin GPS, la tarjeta
   de "Ubicación" tiene 193 px de contenido (título, el aviso de que no hay
   coordenadas y los botones) pero ocupaba 486 porque la vecina lleva una foto de
   329. Casi 300 px de vacío, en la mejor posición de la pantalla, para decir que
   no hay nada que enseñar.
   Con `start` cada tarjeta mide lo suyo. Cuando SÍ hay mapa las dos crecen de
   forma natural y se ven igual que antes. */
@media (min-width: 720px) { .percha-detalle-cards { grid-template-columns: 1fr 1fr; align-items: start; } }
/* --- Configuración v2 --------------------------------------------------- */

/* Índice de secciones. Son anclas puras: funcionan sin una línea de JS. En
   móvil se deslizan en horizontal en vez de apilarse y comerse la pantalla. */
.config-indice {
  display: flex; gap: 8px; margin: 0 0 18px; padding-bottom: 4px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.config-indice a {
  flex: 0 0 auto; padding: 7px 14px; border: 2px solid var(--color-borde);
  border-radius: var(--radio-pill); background: var(--color-superficie);
  color: inherit; text-decoration: none; font-weight: 700; font-size: var(--txt-sm, .82rem);
  white-space: nowrap;
}
.config-indice a:hover { background: var(--color-superficie-alt); }
/* Ahora son pestañas de verdad (se ve una sección y sólo una), así que la activa
   tiene que cantar: invertida, el mismo lenguaje que .tab-tamanio.activo en el
   resto de la app. Sin marcarla, el usuario no sabe en qué sección está. */
.config-indice a.activo {
  background: var(--color-texto);
  color: var(--color-superficie);
}

.config-bloque { margin-bottom: 26px; scroll-margin-top: 16px; }
.config-bloque-tit {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 10px; font-size: 1.05rem;
}

/* Tarjetas de estado. Responden "¿está funcionando?", que es la pregunta con la
   que se entra a esta pantalla. El color del borde lleva el semáforo. */
.config-diag {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.config-diag-item {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 14px 16px; background: var(--color-superficie);
  border: 2px solid var(--color-borde); border-radius: var(--radio);
  box-shadow: var(--sombra-dura); color: inherit; text-decoration: none;
}
a.config-diag-item { transition: transform var(--dur-1) var(--ease-out); }
a.config-diag-item:hover { transform: translateY(-2px); }
.config-diag-item--problema { border-color: var(--color-alerta-alto); }
.config-diag-item--aviso    { border-color: var(--color-acento); }
.config-diag-icono {
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: 0 0 40px; margin-top: 2px;
  border-radius: var(--radio-sm); background: var(--color-superficie-alt);
  font-size: 1.2rem;
}
.config-diag-item--problema .config-diag-icono { color: var(--color-alerta-alto); }
.config-diag-cuerpo { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.config-diag-cab { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.config-diag-valor {
  font-family: var(--fuente-titulo); font-weight: 800; font-size: 1.5rem;
  line-height: 1.1; font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}
.config-diag-cuerpo .text-suave { font-size: .8rem; line-height: 1.4; }
.config-diag-flecha { margin-left: auto; flex: 0 0 auto; opacity: .5; align-self: center; }

/* Quién tocó por última vez cada bloque. */
.config-sello {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 12px; font-size: .76rem; color: var(--color-texto-suave);
}

/* Campos numéricos en fila; se apilan solos sin media query. */
.config-campos-fila { display: flex; gap: 16px; flex-wrap: wrap; }
.config-campos-fila .config-campo { flex: 1 1 220px; min-width: 200px; }

/* Interruptor dependiente de otro: se indenta para que se lea la jerarquía. */
.config-switch--hijo { margin-left: 22px; border-left: 3px solid var(--color-borde-suave); padding-left: 14px; }
.config-nota-inactiva { display: block; margin-top: 4px; color: var(--color-acento-fuerte); }

/* Vista del Analista: lista de valores, NUNCA un formulario con campos
   deshabilitados (un campo deshabilitado no se envía, así que su envío
   apagaría todos los interruptores). */
.config-lectura { display: grid; grid-template-columns: 1fr auto; gap: 8px 16px; margin: 0; }
.config-lectura dt { color: var(--color-texto-suave); font-size: .88rem; }
.config-lectura dd { margin: 0; font-weight: 700; text-align: right; }

/* Fila de acción (select + botón) sin estilos en línea. */
.config-linea { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.config-linea-select { flex: 1 1 240px; min-width: 220px; min-height: 44px; width: auto; margin: 0; }

/* --- Errores del sistema ----------------------------------------------- */
.err-filtros { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.err-filtro {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border: 2px solid var(--color-borde);
  border-radius: var(--radio-pill); background: var(--color-superficie);
  color: inherit; text-decoration: none; font-weight: 700; font-size: .85rem;
}
.err-filtro--activo { background: var(--color-texto); color: var(--color-superficie); border-color: var(--color-texto); }
.err-filtro-n { font-variant-numeric: tabular-nums; opacity: .7; }

.err-item { margin-bottom: 10px; }
/* Lo resuelto se apaga en vez de esconderse: sigue siendo consultable. */
.err-item--resuelto { opacity: .6; }
.err-cab { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.err-clase { font-family: var(--fuente-titulo); overflow-wrap: anywhere; }
.err-veces { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.err-mensaje {
  margin: 10px 0 8px; padding: 10px 12px;
  background: var(--color-superficie-alt); border-radius: var(--radio-sm);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--txt-sm, .82rem); line-height: 1.45; overflow-wrap: anywhere;
}
.err-meta {
  display: flex; flex-wrap: wrap; gap: 4px 16px;
  font-size: var(--txt-sec, .78rem); color: var(--color-texto-suave);
}
.err-meta span { display: inline-flex; align-items: center; gap: 5px; overflow-wrap: anywhere; }
.err-acciones { margin-top: 12px; }

/* --- Tarjeta de versión en el perfil ----------------------------------- */
/* inline-flex y no flex: el marcador del <summary> es un list-item, así que un
   hijo de bloque cae debajo de la flechita en vez de ponerse a su lado. */
.version-info { display: inline-flex; align-items: center; gap: 10px; vertical-align: middle; min-width: 0; }
.version-info > span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.version-info .text-suave { font-size: var(--txt-sec, .78rem); font-weight: 400; }

.version-item { padding: 12px 0; border-bottom: 1px solid var(--color-borde-suave); }
.version-item:last-child { border-bottom: 0; padding-bottom: 0; }
.version-item-cab { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.version-item-cab strong { font-variant-numeric: tabular-nums; }
.version-fecha { font-size: .76rem; margin-left: auto; }
@media (max-width: 480px) { .version-fecha { margin-left: 0; width: 100%; } }
.version-titulo { margin: 4px 0 6px; font-weight: 700; font-size: .92rem; }
.version-cambios { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.version-cambios li { font-size: .85rem; line-height: 1.45; color: var(--color-texto-suave); }
/* La versión vigente se destaca; las anteriores quedan como contexto. */
.version-item--actual .version-cambios li { color: var(--color-texto); }

/* --- Visitas que el sistema cerró solo (pantalla del promotor) --------- */
/* En móvil se apila: el nombre del almacén puede ser largo y comprimirlo para
   meter las horas en la misma línea lo dejaría ilegible. */
.vsc-item {
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  align-items: baseline; justify-content: space-between;
  padding: 12px 0; border-bottom: 1px solid var(--color-borde-suave);
}
.vsc-item:last-child { border-bottom: 0; }
.vsc-almacen { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.vsc-almacen strong { overflow-wrap: anywhere; }
.vsc-almacen .text-suave { font-size: .8rem; }
.vsc-horas { display: flex; flex-direction: column; gap: 3px; align-items: flex-end; }
@media (max-width: 520px) { .vsc-horas { align-items: flex-start; } }
.vsc-fecha { font-size: var(--txt-sec, .78rem); color: var(--color-texto-suave); }
.vsc-rango {
  display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap;
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: var(--txt-md, .9rem);
}

/* --- Cerrar visitas olvidadas ------------------------------------------ */
/* La fila lleva tres controles (hora, motivo y botón) dentro de una celda de
   tabla. Sin anchos explícitos, el campo de motivo se estira y empuja el botón
   fuera de la columna, que es lo que descuadraba la pantalla. */
.cierre-form { margin: 0; }
.cierre-fila { display: flex; gap: 8px; align-items: center; flex-wrap: nowrap; }
/* Los tres controles a la MISMA altura: un input[type=time] se dibuja más bajo
   que un input de texto, y esos 8 px de diferencia son justo lo que hace que la
   fila se vea descuadrada aunque esté centrada. */
.cierre-hora { width: 116px; flex: 0 0 116px; height: 48px; margin: 0; }
.cierre-nota { flex: 1 1 160px; min-width: 120px; height: 48px; margin: 0; }
.cierre-btn  { flex: 0 0 auto; white-space: nowrap; }
.cierre-rango { display: block; margin-top: 5px; font-size: .75rem; }
.cierre-entrada { white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 860px) {
  /* En pantalla estrecha se apila: antes que encoger los campos hasta que el
     botón salga de la celda, es preferible ocupar dos líneas. */
  .cierre-fila { flex-wrap: wrap; }
  .cierre-nota { flex: 1 1 100%; }
}

/* --- Marcas: logotipo pulsable y modal de monitoreo rápido ------------- */
/* El logotipo de la tabla es un <button> (para que funcione con teclado y lo
   anuncien los lectores de pantalla), así que hay que quitarle toda la
   apariencia de botón y dejar sólo la afordancia de que se puede pulsar. */
.logo-marca-btn {
  background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer; line-height: 0; border-radius: var(--radio-sm);
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.logo-marca-btn:hover { transform: scale(1.06); }
.logo-marca-btn:focus-visible { outline: 2px solid var(--color-acento); outline-offset: 3px; }

.marca-modal-logo {
  display: flex; align-items: center; justify-content: center;
  min-height: 140px; padding: 18px;
  background: var(--color-superficie-alt);
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  margin-bottom: 12px;
}
/* El logotipo se muestra COMPLETO: los hay cuadrados y horizontales, y
   recortarlos para cuadrar la caja mutilaría la mitad. */
.marca-modal-logo img { max-width: 100%; max-height: 180px; width: auto; height: auto; object-fit: contain; }
.marca-modal-inicial {
  font-family: var(--fuente-titulo); font-weight: 800; font-size: 3rem;
  color: var(--color-texto-suave);
}
.marca-modal-sub { text-align: center; }

.marca-modal-kpis {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
}
@media (max-width: 520px) { .marca-modal-kpis { grid-template-columns: repeat(2, 1fr); } }
.marca-modal-kpi {
  background: var(--color-superficie-alt);
  border-radius: var(--radio-sm);
  padding: 10px 8px; text-align: center;
}
.marca-modal-kpi strong {
  display: block; font-family: var(--fuente-titulo); font-weight: 800;
  font-size: 1.15rem; color: var(--color-texto); font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.marca-modal-kpi span {
  display: block; margin-top: 2px; font-size: var(--txt-micro, .7rem); letter-spacing: .03em;
  text-transform: uppercase; color: var(--color-texto-suave);
}
.marca-modal-acciones {
  margin-top: 16px; padding-top: 14px;
  border-top: 1px solid var(--color-borde-suave);
  text-align: center;
}

.percha-mini-mapa {
  height: 220px; width: 100%; border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio); background: var(--color-fondo);
  /* Mismo motivo que en .mapa-cobertura: encierra los z-index de Leaflet para
     que el mini-mapa no se dibuje por encima de la barra superior. */
  position: relative;
  z-index: 0;
}
.percha-almacen-foto {
  /* Marco más cuadrado (3:2) que crece con el ancho, con tope para no dominar en
     pantallas anchas. object-fit:cover + object-position:center recorta sin
     deformar y centra la foto. */
  width: 100%; aspect-ratio: 3 / 2; max-height: 380px;
  object-fit: cover; object-position: center; border-radius: var(--radio);
  border: 1px solid var(--color-borde-suave); margin-bottom: 10px; display: block;
}
.percha-almacen-foto--vacia {
  display: flex; align-items: center; justify-content: center;
  color: var(--color-texto-suave); font-size: 2.2rem; background: var(--color-superficie-alt);
}
/* Foto del almacén con botón para verla en grande (visor/lightbox). */
.percha-almacen-foto-wrap { position: relative; margin-bottom: var(--esp-md); }
.percha-almacen-foto-wrap .percha-almacen-foto { margin-bottom: 0; cursor: zoom-in; }
.percha-foto-zoom {
  position: absolute; right: 8px; bottom: 8px;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px; border-radius: 999px;
  border: 2px solid var(--color-borde);
  background: var(--color-superficie); color: var(--color-texto);
  font-family: var(--fuente-cuerpo); font-weight: 800; font-size: .76rem; cursor: pointer;
  box-shadow: var(--sombra-dura-sm);
  transition: transform .1s ease, box-shadow .15s ease, background-color .15s ease;
}
.percha-foto-zoom i { font-size: var(--txt-md, .9rem); color: var(--color-acento); }
@media (hover: hover) {
  .percha-foto-zoom:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--color-borde); background: var(--color-superficie-alt); }
}
.percha-datos-lista { margin: 0; display: grid; gap: var(--esp-sm); }
.percha-datos-lista div { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--esp-md); align-items: baseline; }
.percha-datos-lista dt { font-size: .74rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); }
.percha-datos-lista dd { margin: 0; font-weight: 600; }

/* Gráficos: 2 columnas en desktop, 1 en móvil */
.percha-chart-grid { display: grid; gap: var(--esp-lg); grid-template-columns: 1fr; }
@media (min-width: 900px) { .percha-chart-grid { grid-template-columns: 1fr 1fr; } }
.percha-chart-grid-full { grid-column: 1 / -1; }
.percha-chart-wrap { position: relative; height: 260px; margin-top: var(--esp-md); }
.percha-chart-wrap canvas { width: 100% !important; height: 100% !important; }

.summary-toggle {
  cursor: pointer;
  font-weight: 700;
  font-family: var(--fuente-titulo);
}
.summary-toggle:focus-visible {
  outline: 3px solid var(--color-energia-txt);
  outline-offset: 3px;
}

/* ============================================================================
   Capa de motion — micro-interacciones y transiciones. Todo queda desactivado
   automáticamente si el usuario prefiere menos movimiento.
   ========================================================================= */
@media (prefers-reduced-motion: no-preference) {
  /* Transición suave entre páginas (MPA view transitions, progresivo). */
  @view-transition { navigation: auto; }
  html { scroll-behavior: smooth; }

  /* Entrada escalonada de tarjetas y métricas al cargar la página. */
  .tarjeta, .tarjeta-plana, .metrica, .resumen-pill, .empty-state,
  .lista-grid .almacen-card, .login-brand, .login-form-inner {
    animation: entrada .5s var(--ease-out) backwards;
  }
  .paso-wizard.animando-entrada {
    animation: paso-entrada .26s var(--ease-out) backwards;
  }
  .metricas-grid > :nth-child(2), .dashboard-resumen > :nth-child(2),
  .lista-grid > :nth-child(2) { animation-delay: .06s; }
  .metricas-grid > :nth-child(3), .dashboard-resumen > :nth-child(3),
  .lista-grid > :nth-child(3) { animation-delay: .12s; }
  .metricas-grid > :nth-child(4), .dashboard-resumen > :nth-child(4),
  .lista-grid > :nth-child(4) { animation-delay: .18s; }
  .login-form-inner { animation-delay: .1s; }

  /* Despliegue animado de <details> (acordeones) en navegadores modernos.

     ANIMACIÓN, NO TRANSICIÓN, y a propósito. La versión anterior ponía
     `block-size: 0` como estado BASE de ::details-content y confiaba en que
     `details[open]` lo devolviera a `auto`. En Chrome 148 esa vuelta no ocurre:
     el pseudo-elemento se queda en block-size 0 con content-visibility hidden
     aunque el <details> esté abierto, así que TODOS los acordeones de la
     aplicación abrían sin mostrar nada — el de "Agregar almacén", el picker de
     check-in, el de la foto de fachada. El usuario toca, la flecha gira y no
     aparece nada.

     Con @keyframes el estado base es el normal (auto) y el 0 vive sólo dentro
     del fotograma inicial. Si la animación falla, no se ejecuta o el navegador
     no la entiende, el contenido se ve igual: falla ABIERTO. */
  /* SIN animación de ::details-content. Se probó de las dos formas —transición
     desde block-size 0, y @keyframes— y en Chrome 148 el pseudo-elemento se
     queda colapsado igual: el acordeón abre, la flecha gira y no aparece nada.
     Afectaba a TODOS los <details> de la aplicación, no a uno: "Agregar
     almacén", el picker de check-in, la foto de fachada.

     Un acordeón que no se abre no es un problema de estética, es una función
     perdida. La animación era un adorno y se retira; el despliegue instantáneo
     del navegador funciona en todas partes. Si algún día se quiere recuperar,
     hay que verificarla en Chrome estable ANTES, no asumir que va. */
}
@keyframes entrada {
  from { opacity: 0; transform: translateY(10px); }
}
@keyframes paso-entrada {
  from { opacity: 0; transform: translateX(10px); }
}

/* ============================================================================
   Matriz comparativa (admin/comparativo.php) — la vista "Excel en vivo".
   ========================================================================= */
.filtros-matriz-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0 14px;
}
.tabs-tamanio {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}
.tab-tamanio {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 8px 18px;
  border: 2px solid var(--color-borde);
  border-radius: 99px;
  background: var(--color-superficie);
  color: var(--color-texto);
  font-weight: 800;
  font-size: .88rem;
  text-decoration: none;
  box-shadow: var(--sombra-dura-sm);
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) ease, background-color var(--dur-1) ease;
}
@media (hover: hover) {
  .tab-tamanio:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--color-borde); }
}
.tab-tamanio.activo {
  background: var(--color-texto);
  color: var(--color-superficie);
}

/* Pie del panel de filtros: la descarga completa y su explicación. */
.filtros-matriz-pie {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 2px dashed var(--color-borde-suave);
}
.filtros-matriz-pie-nota { font-size: var(--txt-sm, .82rem); flex: 1 1 240px; }

/* --- Tarjeta de MARCA (pantalla del promotor) ---------------------------
   Lleva clases propias en vez de reutilizar .fila-entre: esa clase es un
   space-between compartido con Almacenes e Inicio, y era justo lo que hacía que
   la insignia se colocara al lado del nombre unas veces y debajo otras, dejando
   la rejilla con tarjetas de dos tamaños. */
.marca-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  height: 100%;
}
.marca-card-cab {
  display: flex;
  gap: 12px;
  align-items: center;
  min-width: 0;          /* deja que el texto se recorte en vez de desbordar */
}
.marca-card-txt {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.marca-card-nombre {
  font-size: 1.05rem;
  line-height: 1.25;
  overflow-wrap: anywhere;   /* un nombre largo parte, no desborda */
}
.marca-card-dist {
  font-size: .84rem;
  color: var(--color-texto-suave);
  overflow-wrap: anywhere;
}
/* La ausencia se lee como ausencia: la distingue la CURSIVA, no un color más
   claro. Un `opacity: .75` aquí la dejaba en 3,85:1 sobre el fondo crema — por
   debajo del mínimo legible — porque la opacidad mezcla el texto con su fondo.
   La cursiva diferencia igual de bien y no toca el contraste en ningún tema. */
.marca-card-dist--vacio {
  font-style: italic;
}
/* margin-top:auto la empuja al fondo: con las filas ya igualadas, el hueco que
   sobra queda ARRIBA y todas las insignias de la fila se alinean entre sí. */
.marca-card-badge {
  align-self: flex-start;
  margin-top: auto;
}
/* Marca sin catálogo: gris, no ámbar. Conserva el punto y la forma de insignia
   (sigue siendo el mismo dato) pero deja de reclamar atención como si tuviera
   194 modelos. */
.badge-vacio {
  background: var(--color-fondo);
  color: var(--color-texto-suave);
  border-color: var(--color-borde-suave);
}

.matriz-leyenda { display: flex; flex-wrap: wrap; gap: 6px; }
.leyenda-chip {
  font-size: var(--txt-xs, .72rem);
  font-weight: 800;
  padding: 4px 9px;
  border-radius: 99px;
  letter-spacing: .02em;
}

.matriz-scroll {
  overflow: auto;
  max-height: min(70vh, 680px);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
  -webkit-overflow-scrolling: touch;
}

/* Pantalla completa de la matriz: un solo overlay que hace scroll en ambos ejes.
   La cabecera (título, leyenda, filtro y orden) se desplaza hacia arriba al bajar;
   los encabezados de la tabla (modelos arriba, almacén a la izquierda) quedan fijos
   con position:sticky. Así primero se hace scroll hasta la tabla y luego se recorre
   la tabla con sus encabezados siempre visibles. */
body.matriz-full-open { overflow: hidden; }
.matriz-tarjeta.matriz-full {
  position: fixed; inset: 0; z-index: 200; margin: 0; max-width: none; border-radius: 0;
  background: var(--color-superficie);
  padding: 0 0 env(safe-area-inset-bottom);
  display: block;
  overflow: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.matriz-full .matriz-cabecera {
  position: sticky; left: 0;
  width: 100vw;
  box-sizing: border-box;
  margin: 0;
  padding: 12px 16px 10px;
  background: var(--color-superficie);
  border-bottom: 2px solid var(--color-borde);
  z-index: 6;
}
/* Deja sitio arriba a la derecha para el botón flotante "Salir". */
.matriz-full .matriz-cabecera > .fila-entre { padding-right: 92px; }
.matriz-full .matriz-scroll {
  overflow: visible;
  max-height: none;
  border: 0;
  border-radius: 0;
}
/* Botón de salida siempre visible aunque la cabecera ya se haya desplazado. */
.matriz-full #btnMaximizarMatriz {
  position: fixed;
  top: calc(8px + env(safe-area-inset-top));
  right: calc(10px + env(safe-area-inset-right));
  z-index: 210;
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
}
/* Cabecera compacta en pantalla completa: se recorre rápido y deja el máximo de
   alto para la tabla. */
.matriz-full .matriz-cabecera > .fila-entre { margin-bottom: 8px; padding-right: 88px; }
.matriz-full .matriz-cabecera h3 { font-size: 1rem; line-height: 1.15; }
.matriz-full .matriz-cabecera > #buscarAlmacenMatriz { margin-bottom: 8px; }
.matriz-full .matriz-cabecera .matriz-leyenda { gap: 4px; }
.matriz-full .matriz-cabecera .leyenda-chip { padding: 3px 7px; font-size: var(--txt-xs, .72rem); }
/* Pestañas de orden en una sola fila desplazable (no ocupan 4 renglones). */
.matriz-full .matriz-orden {
  flex-wrap: nowrap;
  overflow-x: auto;
  margin-bottom: 0;
  padding-bottom: 2px;
  -webkit-overflow-scrolling: touch;
}
.matriz-full .matriz-orden > * { flex: 0 0 auto; }
.matriz-full .matriz-orden .tab-tamanio { white-space: nowrap; }
.matriz {
  border-collapse: separate;
  border-spacing: 0;
  width: max-content;
  min-width: 100%;
  font-size: .8rem;
}
.matriz thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--color-superficie);
  border-bottom: 2px solid var(--color-borde);
  vertical-align: bottom;
  text-align: left;
}
.matriz .matriz-esquina {
  left: 0;
  z-index: 5;
  min-width: 128px;
  padding: 7px 10px;
  font-family: var(--fuente-titulo);
  font-size: var(--txt-xs, .72rem);
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-texto-suave);
  border-right: 2px solid var(--color-borde);
}
.matriz tbody th[scope="row"] {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--color-superficie);
  border-right: 2px solid var(--color-borde);
  border-bottom: 1px solid var(--color-borde-suave);
  padding: 5px 10px;
  text-align: left;
  min-width: 128px;
  max-width: 180px;
}
.matriz tbody th[scope="row"] strong,
.matriz tbody th[scope="row"] .celda-enlace {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: var(--txt-sec, .78rem); line-height: 1.16; font-weight: 700; text-align: left; padding: 0;
}
.matriz tbody th[scope="row"] span { display: block; font-size: .64rem; color: var(--color-texto-suave); font-weight: 400; margin-top: 1px; }
.matriz tbody th[scope="row"] .matriz-indice { line-height: 1.1; }
.matriz td {
  border-bottom: 1px solid var(--color-borde-suave);
  padding: 2px 4px;
  text-align: right;
  min-width: 94px;
}

.matriz-col {
  position: relative;
  min-width: 122px;
  max-width: 150px;
  padding: 7px 8px 6px;
}
.matriz-col-marca { display: flex; align-items: center; gap: 5px; margin-bottom: 1px; }
.matriz-col-marca img { height: 13px; width: auto; max-width: 64px; object-fit: contain; }
.matriz-col-marca span {
  font-size: .58rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-texto-suave);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.matriz-col-inicial {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--color-texto); color: var(--color-superficie);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .54rem; font-weight: 800; flex: 0 0 auto;
}
.matriz-col-modelo {
  display: block;
  font-family: var(--fuente-titulo);
  font-size: .76rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.matriz-col-prom {
  display: block;
  font-family: var(--fuente-titulo);
  font-weight: 800;
  font-size: .92rem;
  font-variant-numeric: tabular-nums;
  margin-top: 1px;
}
.matriz-col-extra {
  display: block;
  font-size: .58rem;
  font-weight: 400;
  color: var(--color-texto-suave);
  margin-top: 1px;
  white-space: nowrap;
}
.matriz-col-link {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px;
  color: var(--color-texto-suave);
}
@media (hover: hover) {
  .matriz-col-link:hover { background: var(--color-superficie-alt); color: var(--color-acento-fuerte); }
}
.var-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: .6rem;
  font-weight: 800;
  border-radius: 99px;
  padding: 2px 7px;
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}
.var-chip.pos { background: #DDF2E1; color: #14652F; }
.var-chip.neg { background: #F9D2CF; color: #8F1020; }

/* Celdas heatmap: escala verde (barato) → amarillo (promedio) → rojo (caro). */
.celda-precio {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  min-height: 30px;
  padding: 4px 7px;
  border: 0;
  border-radius: 7px;
  font-family: var(--fuente-cuerpo);
  font-weight: 800;
  font-size: .77rem;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) ease;
}
@media (hover: hover) {
  .celda-precio:hover { transform: translateY(-1px); box-shadow: inset 0 0 0 2px var(--color-borde); }
}
.celda-precio:active { transform: scale(.96); }
.nv--2 { background: #B9E7C9; color: #0C4A26; }
.nv--1 { background: #E0F3E2; color: #196F3D; }
.nv-0  { background: #FCEFC7; color: #7A5A00; }
.nv-1  { background: #FFD9BD; color: #9A3A12; }
.nv-2  { background: #F6C1BD; color: #8F1020; }
.celda-vacia { color: var(--color-gris); text-align: center; font-size: .8rem; }

.col-hl { box-shadow: inset 0 0 0 999px rgba(34, 34, 59, 0.055); }
@media (prefers-color-scheme: dark) {
  .col-hl { box-shadow: inset 0 0 0 999px rgba(243, 240, 231, 0.07); }
}

/* --- Modal de detalle (dialog nativo) ----------------------------------- */
.modal-ts {
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  box-shadow: var(--sombra-dura), 0 24px 48px rgba(34, 34, 59, 0.25);
  padding: 0;
  width: min(440px, calc(100vw - 28px));
  /* Sin esto un detalle largo (respuestas con muchas preguntas/fotos) desbordaba
     la pantalla sin poder hacer scroll. Beneficia a TODOS los modales .modal-ts. */
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--color-superficie);
  color: var(--color-texto);
}
.modal-ts::backdrop {
  background: rgba(34, 34, 59, 0.55);
  backdrop-filter: blur(3px);
}
/* Variante ancha para el detalle de visita (check-in/out + tareas + fotos). */
.modal-ts--ancha { width: min(620px, calc(100vw - 28px)); }
.vis-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 12px; }
.vis-col { background: var(--color-superficie-alt, #F0ECE6); border-radius: var(--radio, 12px); padding: 10px 12px; }
.vis-col-tit { margin: 0 0 6px; font-size: var(--txt-sm, .82rem); font-weight: 800; text-transform: uppercase; letter-spacing: .03em; color: var(--color-texto-suave); }
.vis-seccion { margin-top: 14px; }
.vis-foto { display: block; width: 100%; margin-top: 8px; border-radius: 10px; overflow: hidden; background: var(--color-superficie-alt); }
.vis-foto img { width: 100%; display: block; object-fit: cover; max-height: 220px; }
.vis-foto--sm { display: inline-block; width: 60px; height: 60px; margin: 0; }
.vis-foto--sm img { width: 100%; height: 100%; max-height: none; }
.vis-foto--vacia { display: inline-flex; align-items: center; justify-content: center; min-height: 60px; color: var(--color-texto-suave); font-size: 1.4rem; }
.vis-tareas { display: flex; flex-direction: column; gap: 8px; }
.vis-tarea { padding: 8px 10px; border: 1px solid var(--color-borde); border-radius: 10px; }
.vis-tarea .vis-ok { color: var(--color-alerta-bajo, #1F7A45); }
.vis-tarea .vis-pend { color: var(--color-texto-suave); }
.vis-tarea-fotos { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.modal-ts-inner { padding: 18px 18px 20px; }
.modal-ts-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 4px;
}
.modal-ts-cerrar { width: 40px; height: 40px; min-height: 40px; font-size: 1.15rem; }

/* ---- Editar/crear almacén: cabecera y acciones fijas ----------------------
   Es el formulario más largo del panel: a 1280x800 el modal esconde 269 px, así
   que al abrirlo no se veía ninguna acción y al bajar desaparecía la X, que era
   la única salida visible. Las dos reglas van acotadas por #id porque
   .modal-ts-head la comparten los demás modales, que sí caben.

   El scrollport es el propio <dialog> (.modal-ts trae max-height + overflow-y),
   no el .modal-ts-inner: por eso los sticky de dentro del inner se pegan contra
   el borde del diálogo.

   Los márgenes negativos NO son adorno: .modal-ts-inner tiene padding lateral de
   18 px y sin ellos la barra mide 390 px dentro de un scrollport de 426 y deja
   18 px de canalón a cada lado por los que se ve pasar el contenido por detrás.
   Fondo opaco con --color-superficie (token que SÍ se invierte en oscuro), nunca
   un claro literal. */
#modalEditarAlmacen .modal-ts-head {
  position: sticky; top: 0; z-index: 3;
  background: var(--color-superficie);
  margin: 0 -18px; padding: 8px 18px;
}
#modalEditarAlmacen .ed-acciones {
  position: sticky; bottom: 0; z-index: 2;
  margin: 14px -18px 0; padding: 10px 18px;
  background: var(--color-superficie);
  border-top: 1px solid var(--color-borde-suave);
}
.modal-ts-foto {
  width: 100%;
  max-height: 220px;
  object-fit: cover;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  margin: 8px 0 4px;
}
.modal-ts-precio {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 6px 0 14px;
}
.modal-ts-precio strong {
  font-family: var(--fuente-titulo);
  font-size: 2rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.modal-ts-datos { margin: 0; display: grid; gap: 8px; }
.modal-ts-datos > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  border-bottom: 1px solid var(--color-borde-suave);
  padding-bottom: 7px;
}
.modal-ts-datos dt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--color-texto-suave);
  font-weight: 700;
  font-size: .8rem;
  white-space: nowrap;
}
.modal-ts-datos dd { margin: 0; font-weight: 700; text-align: right; font-size: .88rem; }
.modal-ts-acciones {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 16px;
}
@media (prefers-reduced-motion: no-preference) {
  .modal-ts[open] { animation: entrada .25s var(--ease-out); }
}

/* --- Menú del FAB central: elegir Registrar precio / Registrar venta ------
   Hoja simple con <div hidden> (no <dialog>/showModal — ver nota en app.js),
   mismo patrón visual que .gamif-overlay/.gamif-sheet: hoja desde abajo en
   móvil, centrada en desktop. */
.fab-sheet-ov {
  position: fixed; inset: 0; z-index: 9500; padding: 0;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(34, 34, 59, .55); backdrop-filter: blur(3px);
  opacity: 0; transition: opacity var(--dur-2, .3s) var(--ease-out, ease);
}
.fab-sheet-ov[hidden] { display: none; }
.fab-sheet-ov.visible { opacity: 1; }
@media (min-width: 640px) { .fab-sheet-ov { align-items: center; padding: 20px; } }
.fab-sheet {
  position: relative; width: 100%; max-width: 400px; margin: 0 auto;
  background: var(--color-superficie); color: var(--color-texto);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg) var(--radio-lg) 0 0;
  padding: 18px 18px 24px;
  transform: translateY(16px); transition: transform var(--dur-2, .3s) var(--ease-out, cubic-bezier(.2,.8,.2,1));
}
.fab-sheet-ov.visible .fab-sheet { transform: translateY(0); }
@media (min-width: 640px) {
  .fab-sheet { border-radius: var(--radio-lg); padding-bottom: 18px; transform: translateY(12px) scale(.97); }
  .fab-sheet-ov.visible .fab-sheet { transform: translateY(0) scale(1); }
}
.fab-sheet-cerrar { position: absolute; top: 10px; right: 10px; }
.fab-menu-opciones { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.fab-menu-op {
  display: flex; align-items: center; gap: 12px;
  padding: 14px; min-height: 64px;
  border: 2px solid var(--color-borde); border-radius: var(--radio-lg);
  background: var(--color-superficie); color: var(--color-texto); text-decoration: none;
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1), border-color var(--dur-1);
}
.fab-menu-op:active { transform: scale(0.98); }
@media (hover: hover) {
  .fab-menu-op:hover { border-color: var(--color-acento); box-shadow: var(--sombra-suave); }
}
.fab-menu-op-ic {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); font-size: 1.25rem;
}
.fab-menu-op-ic--venta { background: #E1F3E7; color: var(--color-alerta-bajo); }
.fab-menu-op-ic--checkin { background: color-mix(in srgb, var(--color-energia, #FF5A1F) 16%, transparent); color: var(--color-energia, #FF5A1F); }
.fab-menu-op-ic--form { background: color-mix(in srgb, var(--color-texto) 12%, transparent); color: var(--color-texto); }
.fab-menu-op-txt { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.fab-menu-op-txt strong { font-size: .94rem; }
.fab-menu-op-txt .text-suave { font-size: var(--txt-sec, .78rem); }
.fab-menu-op-caret { flex: 0 0 auto; color: var(--color-gris); }

/* --- Matriz: export, ficha de modelo y evolución ------------------------- */
.matriz-header-derecha {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}
@media (max-width: 720px) {
  .matriz-header-derecha { align-items: flex-start; }
}
.matriz-col-btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  border-radius: 6px;
}
@media (hover: hover) {
  .matriz-col-btn:hover .matriz-col-modelo {
    color: var(--color-acento-fuerte);
    text-decoration: underline;
    text-underline-offset: 3px;
  }
}
.modal-ts--ancho { width: min(640px, calc(100vw - 28px)); }
.chart-evo-wrap {
  position: relative;
  height: 280px;
  margin-top: 10px;
}
/* Contenedor de altura fija para mini-gráficos (evita el crecimiento infinito
   de Chart.js cuando responsive:true + maintainAspectRatio:false no tienen una
   altura acotada por el padre). */
.chart-mini-wrap {
  position: relative;
  height: 170px;
  margin-top: 8px;
}
/* Controles de zoom del gráfico de evolución: segmentado Año/Mes + navegador
   de mes (aparece solo en vista mensual). */
.evo-controles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
}
.seg-control {
  display: inline-flex;
  border: 2px solid var(--color-borde);
  border-radius: 999px;
  padding: 3px;
  gap: 2px;
  background: var(--color-superficie);
}
.seg-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: none;
  background: transparent;
  color: var(--color-texto-suave);
  font-family: inherit;
  font-weight: 700;
  font-size: var(--txt-sm, .82rem);
  padding: 6px 14px;
  min-height: 36px;
  border-radius: 999px;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.seg-btn.is-activo {
  background: var(--color-texto);
  color: var(--color-superficie);
}
.evo-mes-nav {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.evo-mes-actual {
  min-width: 78px;
  text-align: center;
  font-weight: 700;
  font-size: .88rem;
}
.evo-mes-nav .icon-btn:disabled { opacity: .35; cursor: not-allowed; }
.modal-ts-foto--contain {
  object-fit: contain;
  background: var(--color-fondo);
}
.modal-ts-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin: 12px 0 14px;
}
.modal-ts-stats > div {
  border: 1px solid var(--color-borde-suave);
  border-radius: 8px;
  padding: 8px 10px;
  background: var(--color-superficie);
}
.modal-ts-stats span {
  display: block;
  font-size: .64rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--color-texto-suave);
}
.modal-ts-stats strong {
  font-family: var(--fuente-titulo);
  font-size: 1.02rem;
  font-variant-numeric: tabular-nums;
}
.modal-ts-stats--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.modal-ts-stats small {
  display: block;
  font-size: .68rem;
  color: var(--color-texto-suave);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* --- Mín/máx por modelo con rollover del almacén ------------------------- */
.matriz-col-minmax {
  display: flex;
  gap: 3px;
  margin-top: 3px;
}
.mm {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-size: .6rem;
  font-weight: 800;
  border-radius: 6px;
  padding: 2px 5px;
  font-variant-numeric: tabular-nums;
  cursor: help;
}
.mm i { font-size: .64rem; }
.mm-min { background: #E0F3E2; color: #196F3D; }
.mm-max { background: #F9D2CF; color: #8F1020; }

/* Tooltip ligero (rollover) — se muestra DEBAJO para no recortarse contra el
   borde superior del contenedor con scroll de la matriz. */
[data-tip] { position: relative; }
[data-tip]:hover::after,
[data-tip]:focus-visible::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 40;
  white-space: nowrap;
  background: var(--color-texto);
  color: var(--color-superficie);
  font-size: var(--txt-micro, .7rem);
  font-weight: 700;
  letter-spacing: .01em;
  padding: 6px 10px;
  border-radius: 8px;
  box-shadow: var(--sombra-dura-sm);
  pointer-events: none;
}

/* ============================================================================
   Catálogo del promotor, detalle de producto y foto-por-defecto.
   ========================================================================= */
/* Cuando la "categoria-icon" muestra una foto real (no el ícono ilustrado),
   se recorta a cubrir el cuadro en vez de contener con margen. */
.categoria-icon--foto {
  object-fit: cover;
  background: var(--color-superficie);
  padding: 0;
}

.volver-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 700;
  font-size: var(--txt-md, .9rem);
  text-decoration: none;
  color: var(--color-texto-suave);
  margin-bottom: 12px;
}
.volver-link:hover { color: var(--color-acento-fuerte); }

/* --- Lista del catálogo (modelos como filas enlazadas) ------------------ */
.catalogo-modelos { margin-top: 14px; border-top: 2px solid var(--color-borde); }
.catalogo-modelo {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 2px;
  border-bottom: 1px solid var(--color-borde-suave);
  color: var(--color-texto);
  text-decoration: none;
  content-visibility: auto;
  contain-intrinsic-size: auto 66px;
  transition: background-color var(--dur-1) ease;
}
.catalogo-modelo:last-child { border-bottom: 0; }
@media (hover: hover) { .catalogo-modelo:hover { background: var(--color-superficie-alt); } }
.catalogo-modelo-foto {
  width: 52px; height: 52px; flex: 0 0 auto;
  object-fit: cover;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
}
.catalogo-modelo-foto.es-icono { object-fit: contain; background: var(--color-fondo); padding: 4px; }
.catalogo-modelo-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.catalogo-modelo-info strong { font-size: .95rem; }
.catalogo-modelo-info .text-suave { font-size: var(--txt-sec, .78rem); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.catalogo-modelo-meta { display: inline-flex; align-items: center; gap: 8px; color: var(--color-texto-suave); flex: 0 0 auto; }
.catalogo-modelo-meta .badge { padding: 3px 7px; }

/* --- Detalle de producto (hero + ficha) --------------------------------- */
.producto-hero {
  display: flex;
  gap: 14px;
  align-items: center;
  background: var(--color-superficie);
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  box-shadow: var(--sombra-dura-sm);
  padding: 14px;
  margin-bottom: 16px;
}
.producto-hero-foto {
  width: 104px; height: 104px; flex: 0 0 auto;
  object-fit: cover;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio);
  background: var(--color-superficie);
}
.producto-hero-foto.es-icono { object-fit: contain; background: var(--color-fondo); padding: 8px; }
.producto-hero-info { min-width: 0; }
.producto-hero-info h1 { font-size: 1.5rem; }
.producto-hero-meta { margin: 4px 0 0; font-size: var(--txt-sm, .82rem); font-weight: 700; color: var(--color-texto-suave); }
.producto-hero-stats {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; font-size: .84rem;
}
.producto-hero-stats strong { font-family: var(--fuente-titulo); font-variant-numeric: tabular-nums; }

.ficha-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 4px 16px;
}
.ficha-campo { min-width: 0; }
.ficha-campo label { margin-top: 10px; }

.foto-item { position: relative; }
.foto-miniatura.foto-principal { border-color: var(--color-acento); border-width: 3px; }

/* --- Cobertura: acción de filtro y rangos rápidos ----------------------- */
/* Envuelve porque desde v215 esta celda puede llevar DOS acciones —"Aplicar" y el
   "Limpiar" que sólo sale cuando hay filtros puestos—. La celda mide 196 px y los
   dos botones piden 238: sin wrap, el segundo se salía de la tarjeta (el mismo
   fallo que ya apareció en sell-analitica). Como cada .btn va a width:100%, al
   envolver quedan uno sobre otro a ancho completo, que se lee como una decisión y
   no como un accidente. Con una sola acción, nada cambia. */
.filtro-accion { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; }
.filtro-accion .btn { width: 100%; }
.rango-rapido { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

/* Los círculos de precisión del mapa no capturan el clic (deja pasar al punto). */
.leaflet-interactive.precision-anillo { pointer-events: none; }

/* ============================================================================
   Lightbox: visor de fotos a pantalla completa (X clara + flechas + contador).
   ========================================================================= */
.lightbox {
  border: 0; padding: 0; margin: 0;
  width: 100vw; height: 100dvh; max-width: 100vw; max-height: 100dvh;
  background: transparent; overflow: hidden;
}
.lightbox::backdrop { background: rgba(20, 21, 31, 0.93); }
.lightbox-img {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  max-width: 92vw; max-height: 84dvh;
  object-fit: contain;
  border-radius: 10px;
  box-shadow: 0 24px 70px rgba(0, 0, 0, .55);
}
.lightbox-cerrar {
  position: fixed;
  top: calc(12px + env(safe-area-inset-top)); right: 14px;
  z-index: 2;
  width: 54px; height: 54px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid #F7F3EB; border-radius: 50%;
  background: rgba(34, 34, 59, .72); color: #F7F3EB;
  font-size: 1.55rem; cursor: pointer;
  backdrop-filter: blur(4px);
  transition: transform var(--dur-1) var(--ease-out), background-color var(--dur-1) ease;
}
.lightbox-cerrar:active { transform: scale(.9); }
.lightbox-nav {
  position: fixed; top: 50%; transform: translateY(-50%);
  width: 50px; height: 50px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid rgba(247, 243, 235, .55); border-radius: 50%;
  background: rgba(34, 34, 59, .5); color: #F7F3EB;
  font-size: 1.45rem; cursor: pointer;
  backdrop-filter: blur(3px);
}
.lightbox-prev { left: 12px; }
.lightbox-next { right: 12px; }
.lightbox-nav[hidden], .lightbox-contador[hidden] { display: none; }
.lightbox-contador {
  position: fixed;
  bottom: calc(18px + env(safe-area-inset-bottom)); left: 50%;
  transform: translateX(-50%);
  padding: 6px 15px; border-radius: 99px;
  background: rgba(34, 34, 59, .72); color: #F7F3EB;
  font-weight: 800; font-size: .85rem; font-variant-numeric: tabular-nums;
}
@media (hover: hover) {
  .lightbox-cerrar:hover, .lightbox-nav:hover { background: var(--color-energia); color: #22223B; border-color: var(--color-energia); }
}
@media (prefers-reduced-motion: no-preference) {
  .lightbox[open] { animation: entrada .2s var(--ease-out); }
}
/* Pista visual de que una foto se puede ampliar. */
img[data-lightbox] { cursor: zoom-in; }

/* --- Herramienta de estandarización de valores -------------------------- */
.estandarizar-tabla td { vertical-align: middle; }
.estandarizar-tabla code {
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  background: var(--color-superficie-alt);
  padding: 3px 8px; border-radius: 6px; font-size: .85rem;
  border: 1px solid var(--color-borde-suave);
}
.estandarizar-tabla input { min-height: 42px; }
.estandarizar-tabla th:nth-child(2), .estandarizar-tabla td:nth-child(2) { text-align: center; width: 70px; }

/* --- Nombres clicables en tablas admin + modal de almacén --------------- */
.celda-enlace {
  border: 0; background: none; padding: 0; margin: 0;
  font: inherit; font-weight: 700; color: var(--color-texto);
  text-align: left; cursor: pointer;
}
@media (hover: hover) {
  .celda-enlace:hover { color: var(--color-acento-fuerte); text-decoration: underline; text-underline-offset: 3px; }
}
.celda-enlace:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: 2px; border-radius: 4px; }

/* ÁREA TÁCTIL DE 24 px EN LOS ENLACES DE FILA.
   Medido en producción, no en local: .celda-enlace daba 21,8 px de alto en las 278
   filas de Almacenes, y el mismo patrón aparecía en comparativo, flooring y Status
   Day. Con .alm-editar (21 px) y el enlace del nombre en el Termómetro (20 px) suman
   ~916 objetivos por debajo del mínimo de 24 px de WCAG 2.2.

   Relleno con margen negativo del mismo tamaño: la fila no cambia de alto —el margen
   devuelve exactamente lo que el relleno añade— y sólo crece la superficie que
   responde al dedo. Se prefiere a un ::after absoluto porque varios de estos viven
   dentro de contenedores con overflow:hidden que lo recortarían.

   NO se tocó .matriz-col-btn (28 objetivos a 19 px): vive en la cabecera de la matriz
   de precios, que es densa y de ancho calculado, y ahí el relleno sí puede mover la
   maqueta. Queda medido y sin arreglar a propósito.
   Y las casillas del comparador de sell-analítica NO estaban mal: el <input> mide
   17 px pero su <label> .sa-check mide 30x30 y es lo que recibe el clic. Las 100 que
   marcó la sonda eran un falso positivo por medir el input y no su envoltorio. */
.celda-enlace,
.termo-td-nombre > a { padding-top: 3px; padding-bottom: 3px; margin-top: -3px; margin-bottom: -3px; }
/* .alm-editar NO va en esta lista aunque también estaba por debajo de 24 px: su
   padding se declara en el <style> de admin/almacenes.php, que gana por orden de
   cascada, así que la regla de aquí no le añadía nada. Se arregló allí. */

.almacen-sin-foto {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  min-height: 130px; margin: 8px 0 4px;
  border: 2px dashed var(--color-borde-suave); border-radius: var(--radio);
  background: var(--color-fondo); color: var(--color-texto-suave);
}
.almacen-sin-foto i { font-size: 2rem; }
.almacen-sin-foto span { font-weight: 700; font-size: .85rem; }

.hist-lista { display: grid; gap: 8px; margin-top: 8px; max-height: 340px; overflow-y: auto; }
.hist-fila {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio);
  background: var(--color-superficie); padding: 10px 12px;
}
.hist-fila strong { font-family: var(--fuente-titulo); font-variant-numeric: tabular-nums; }
.hist-fila .text-suave { font-size: var(--txt-sm, .82rem); }
.hist-fecha { font-size: var(--txt-sec, .78rem); font-weight: 700; color: var(--color-texto-suave); white-space: nowrap; text-align: right; }

/* --- Filtro de ciudad (listado de almacenes del promotor) --------------- */
.ciudad-filtro {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  margin-bottom: 14px;
}
.ciudad-filtro label { margin: 0; display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.ciudad-filtro select { width: auto; min-width: 200px; min-height: 44px; }
.ciudad-filtro-nota { font-size: .8rem; display: inline-flex; align-items: center; gap: 5px; }

/* ============================================================================
   Duplicados y marcas (admin/duplicados.php) — fusionar modelos repetidos.
   Desarrollado por Carlos David Donoso Cordero (ddchack).
   ========================================================================= */
.dup-grupo { border: 1px solid var(--color-borde-suave); margin-top: 14px; }
.dup-grupo:first-of-type { margin-top: 4px; }
.dup-lista { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.dup-item {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px; border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-lg); background: var(--color-superficie-alt);
}
.dup-keep {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  min-width: 58px; margin: 0; cursor: pointer;
  font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-texto-suave);
}
.dup-keep input { width: 22px; height: 22px; accent-color: var(--color-acento); cursor: pointer; }
.dup-info { flex: 1; min-width: 0; }
.dup-nombre { font-family: var(--fuente-titulo); font-weight: 700; color: var(--color-texto); line-height: 1.2; word-break: break-word; }
.dup-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 5px; font-size: var(--txt-sm, .82rem); }
.dup-meta code, .dup-reasignar-et code {
  font-size: var(--txt-sec, .78rem); padding: 1px 6px; border-radius: 6px;
  background: var(--color-superficie); border: 1px solid var(--color-borde-suave);
}
.chip-marca {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 9px; font-size: .66rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-texto); background: var(--color-superficie);
  border: 1px solid var(--color-borde-suave); border-radius: 999px;
}
.dup-stats { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; font-size: .76rem; color: var(--color-texto-suave); }
.dup-stats i { color: var(--color-acento); }
.dup-reasignar { margin-top: 12px; border-top: 1px dashed var(--color-borde-suave); padding-top: 10px; }
.dup-reasignar summary { cursor: pointer; font-size: .85rem; font-weight: 600; color: var(--color-texto-suave); }
.dup-reasignar-form { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.dup-reasignar-et { font-size: .84rem; }
.dup-reasignar-form select { flex: 1; min-width: 150px; min-height: 44px; width: auto; }

/* ============================================================================
   Edición Avanzada (hub) + Código de barras drag & drop (admin)
   Desarrollado por Carlos David Donoso Cordero (ddchack).
   ========================================================================= */
/* grid-auto-rows:1fr iguala TODAS las filas a la más alta (el desnivel era entre
   filas, no dentro de una fila). Con align-items:flex-start el icono/texto se
   alinean arriba y min-height da un piso común aunque la descripción sea corta. */
.edicion-hub { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; grid-auto-rows: 1fr; }
.edicion-tool {
  display: flex; align-items: flex-start; gap: 14px; text-decoration: none; min-height: 96px;
  padding: 18px; border: 2px solid var(--color-borde); border-radius: var(--radio-lg);
  background: var(--color-superficie); box-shadow: var(--sombra-dura-sm); color: var(--color-texto);
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.edicion-tool:hover { transform: translate(-2px, -2px); box-shadow: var(--sombra-dura); }
.edicion-tool-icono {
  flex: 0 0 auto; width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;
  background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); font-size: 1.5rem;
}
.edicion-tool-cuerpo { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.edicion-tool-cuerpo strong { font-family: var(--fuente-titulo); font-size: 1.02rem; }
.edicion-tool-cuerpo .text-suave {
  font-size: var(--txt-sm, .82rem); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* El aviso "no se puede deshacer" se conserva como chip rojo legible (no se trunca). */
.edicion-tool-aviso { display: inline-flex; align-items: center; gap: 4px; margin-top: 2px; font-size: var(--txt-xs, .72rem); font-weight: 700; color: var(--color-alerta-alto, #C0392B); }
.edicion-tool-flecha { margin-left: auto; align-self: center; color: var(--color-acento); font-size: 1.1rem; }

.bc-lista { display: flex; flex-direction: column; gap: 14px; }
.bc-card {
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio-lg);
  background: var(--color-superficie); padding: 14px;
}
.bc-card.bc-changed { border-color: var(--color-acento); box-shadow: inset 3px 0 0 var(--color-acento); }
.bc-cabecera { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 10px; }
.bc-numero {
  font-family: var(--fuente-titulo); font-weight: 800; font-size: 1.05rem; letter-spacing: .02em;
  font-variant-numeric: tabular-nums; display: inline-flex; align-items: center; gap: 6px;
}
.bc-numero i { color: var(--color-acento); }
.bc-chip-marca {
  font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 9px; border-radius: 999px; color: var(--color-texto);
  background: var(--color-superficie-alt); border: 1px solid var(--color-borde-suave);
}
.bc-cambio {
  display: none; margin-left: auto; font-size: .66rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .04em; color: #fff; background: var(--color-acento); padding: 2px 9px; border-radius: 999px;
}
.bc-card.bc-changed .bc-cambio { display: inline-block; }
.bc-drop {
  border: 2px dashed var(--color-borde-suave); border-radius: 12px; padding: 12px 14px;
  background: var(--color-superficie-alt); display: flex; flex-direction: column; gap: 2px;
  transition: border-color var(--dur-1), background var(--dur-1);
}
.bc-drop.bc-over { border-color: var(--color-acento); background: var(--color-alerta-fondo); }
.bc-drop-hint { font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); }
.bc-drop-modelo { font-weight: 700; color: var(--color-texto); word-break: break-word; }
.bc-cands-titulo { font-size: var(--txt-sec, .78rem); margin: 12px 0 8px; display: flex; align-items: center; gap: 6px; }
.bc-cands { display: flex; flex-wrap: wrap; gap: 8px; }
.bc-cand {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; min-height: 40px;
  border: 1px solid var(--color-borde-suave); border-radius: 999px; background: var(--color-superficie);
  color: var(--color-texto); font-size: var(--txt-sm, .82rem); font-weight: 600; cursor: grab;
  touch-action: none; user-select: none; -webkit-user-select: none;
}
.bc-cand:hover { border-color: var(--color-acento); }
.bc-cand:active { cursor: grabbing; }
.bc-cand i { color: var(--color-texto-suave); }
.bc-cand--actual { background: var(--color-alerta-fondo); border-color: var(--color-acento); }
.bc-cand--actual i { color: var(--color-alerta-bajo); }
.bc-cand-tag { font-size: .6rem; text-transform: uppercase; letter-spacing: .04em; color: var(--color-acento-fuerte); font-weight: 800; }
.bc-cand--fantasma { opacity: .35; }
.bc-drag-clone {
  position: fixed; z-index: 1000; pointer-events: none; transform: translate(-50%, -50%);
  box-shadow: var(--sombra-suave); opacity: .96; cursor: grabbing;
}
.bc-revertir {
  margin-top: 10px; background: none; border: none; color: var(--color-texto-suave); cursor: pointer;
  font-size: var(--txt-sec, .78rem); font-weight: 600; display: inline-flex; align-items: center; gap: 5px; padding: 4px 0;
}
.bc-revertir:hover { color: var(--color-acento); }
.bc-barra-guardar {
  position: sticky; bottom: 0; margin-top: 16px; padding: 12px 0;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: linear-gradient(to top, var(--color-superficie) 70%, transparent);
}
.bc-contador { font-size: .84rem; font-weight: 700; color: var(--color-texto-suave); }
.bc-contador--activo { color: var(--color-acento); }
.bc-drop-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.bc-editar {
  background: none; border: 1px solid var(--color-borde-suave); border-radius: 999px;
  padding: 3px 11px; font-size: var(--txt-xs, .72rem); font-weight: 700; color: var(--color-texto-suave);
  cursor: pointer; display: inline-flex; align-items: center; gap: 4px;
}
.bc-editar:hover { color: var(--color-acento); border-color: var(--color-acento); }
.bc-card.bc-changed .bc-editar { display: none; } /* al mover el código, primero guarda eso */
.bc-editor { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 6px; }
.bc-editar-input { flex: 1; min-width: 200px; }
.bc-editar-no {
  background: none; border: none; color: var(--color-texto-suave); cursor: pointer;
  font-size: .8rem; font-weight: 600; padding: 4px 6px;
}
.bc-editar-no:hover { color: var(--color-alerta-alto); }
.bc-merge { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 8px; }
.bc-merge-vacio { font-size: .76rem; color: var(--color-texto-suave); display: inline-flex; align-items: center; gap: 6px; font-style: italic; }
.bc-merge-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 6px 6px 11px;
  border-radius: 999px; font-size: .8rem; font-weight: 700;
  color: var(--color-alerta-bajo); background: var(--color-superficie);
  border: 1px solid var(--color-alerta-bajo);
}
.bc-merge-chip i { color: var(--color-alerta-bajo); }
.bc-merge-x {
  border: none; background: var(--color-superficie-alt); color: var(--color-texto-suave);
  width: 22px; height: 22px; min-width: 22px; border-radius: 999px; cursor: pointer;
  font-size: var(--txt-micro, .7rem); line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.bc-merge-x:hover { background: var(--color-alerta-alto); color: #fff; }
.bc-cand--marcado { opacity: .4; text-decoration: line-through; pointer-events: none; }

/* ============================================================================
   Constructor de reportes drag-and-drop (admin/reporte.php)
   Desarrollado por Carlos David Donoso Cordero (ddchack).
   ========================================================================= */
.rep-flujo {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px;
  list-style: none; margin: 0 0 12px; padding: 0;
}
.rep-flujo li { min-width: 0; display: flex; align-items: center; gap: 9px; padding: 9px 11px; border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md); background: var(--color-superficie); }
.rep-flujo li > span { width: 28px; height: 28px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; border-radius: 9px; background: var(--color-texto); color: var(--color-superficie); font-size: .75rem; font-weight: 800; }
.rep-flujo li > div { min-width: 0; display: flex; flex-direction: column; }
.rep-flujo strong { font-size: .76rem; line-height: 1.2; }
.rep-flujo small { color: var(--color-texto-suave); font-size: .65rem; line-height: 1.2; }
.rep-barra { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 14px; padding: 10px 12px; border-top: 4px solid var(--color-texto); }
.rep-barra-acciones { display: flex; flex-wrap: wrap; gap: 8px; }
.rep-fecha {
  display: inline-flex; align-items: center; gap: 7px; min-height: 42px; padding: 8px 14px;
  border: 2px solid var(--color-borde); border-radius: 999px; background: var(--color-superficie);
  color: var(--color-texto); font-weight: 700; font-size: var(--txt-peq, .86rem); cursor: pointer;
}
.rep-fecha:hover { border-color: var(--color-acento); }
.rep-buscar-campo { display: flex; align-items: center; gap: 7px; margin: 10px 10px 0; padding: 0 9px; border: 1px solid var(--color-borde-suave); border-radius: 9px; background: var(--color-superficie); }
.rep-buscar-campo:focus-within { border-color: var(--color-acento); box-shadow: 0 0 0 2px var(--color-alerta-fondo); }
.rep-buscar-campo i { color: var(--color-texto-suave); }
.rep-buscar-campo input { width: 100%; min-width: 0; min-height: 38px; padding: 7px 0; border: 0; background: transparent; box-shadow: none; font-size: .8rem; }
.rep-buscar-campo input:focus-visible { outline: 0; }
.rep-buscar-vacio { margin: 8px 12px 0; color: var(--color-texto-suave); font-size: .75rem; }
.rep-buscar-vacio[hidden] { display: none; }

.rep-shell {
  display: grid; gap: 14px; grid-template-columns: 210px 1fr;
  grid-template-areas: "panel estantes" "panel preview"; align-items: start;
}
@media (max-width: 860px) { .rep-shell { grid-template-columns: 1fr; grid-template-areas: "panel" "estantes" "preview"; } }
.rep-panel { grid-area: panel; display: flex; flex-direction: column; gap: 14px; }
/* Bloque unificado: la fuente (encabezado) abarca sus dimensiones y medidas.
   Un solo borde/fondo envuelve todo para que se lea como una unidad, en vez de
   tres tarjetas sueltas. */
.rep-panel-grupo { border: 1px solid var(--color-borde-suave); border-radius: var(--radio-lg); background: var(--color-superficie); overflow: hidden; }
/* Selector de FUENTE de datos (Precios / Flooring / Status Day), ahora encabezado
   del bloque. Vertical para caber en el panel angosto; la fuente activa reusa la
   paleta del chip de medida (alerta-fondo + acento), legible en claro y oscuro. */
.rep-fuente { display: flex; flex-direction: column; gap: 4px; padding: 8px; background: var(--color-superficie-alt); border-bottom: 1px solid var(--color-borde-suave); }
.rep-fuente-op { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; padding: 9px 12px; border-radius: var(--radio-md); border: 1.5px solid transparent; background: transparent; color: var(--color-texto-suave); font-weight: 700; font-size: .84rem; cursor: pointer; transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1); }
.rep-fuente-op i { font-size: 1.1rem; }
.rep-fuente-op:hover { background: var(--color-superficie); color: var(--color-texto); }
.rep-fuente-op.activo { background: var(--color-superficie); border-color: var(--color-acento); color: var(--color-acento-fuerte); box-shadow: var(--sombra-suave); }
.rep-fuente-op.activo i { color: var(--color-acento); }
/* Dentro del grupo las secciones ya no son tarjetas: sólo padding + un divisor
   punteado entre Dimensiones y Medidas. */
.rep-paleta { display: flex; flex-direction: column; padding: 12px; }
.rep-paleta[hidden] { display: none; }
.rep-paleta .rep-panel-sec { border: 0; background: transparent; padding: 0; border-radius: 0; }
.rep-paleta .rep-panel-sec + .rep-panel-sec { margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--color-borde-suave); }
@media (max-width: 860px) {
  .rep-fuente { flex-direction: row; flex-wrap: wrap; }
  .rep-fuente-op { flex: 1 1 30%; justify-content: center; text-align: center; }
}
.rep-panel-sec { border: 1px solid var(--color-borde-suave); border-radius: var(--radio-lg); background: var(--color-superficie); padding: 12px; }
.rep-panel-tit { font-size: .74rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }
.rep-campos { display: flex; flex-wrap: wrap; gap: 8px; }

.rep-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 11px; min-height: 40px;
  border-radius: 999px; font-size: var(--txt-sm, .82rem); font-weight: 700; cursor: grab;
  touch-action: none; user-select: none; -webkit-user-select: none; white-space: nowrap;
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1);
}
.rep-chip--dim { background: var(--color-superficie-alt); border: 1.5px solid var(--color-borde); color: var(--color-texto); }
.rep-chip--dim i { color: var(--color-texto); }
.rep-chip--measure { background: var(--color-alerta-fondo); border: 1.5px solid var(--color-acento); color: var(--color-acento-fuerte); }
.rep-chip--measure i { color: var(--color-acento); }
.rep-chip[hidden] { display: none !important; }
.rep-chip:active { cursor: grabbing; }
.rep-chip--placed { padding-right: 6px; }
.rep-chip--src { opacity: .35; }
.rep-chip--ghost { position: fixed; top: 0; left: 0; z-index: 9999; pointer-events: none; transform: scale(1.05); box-shadow: var(--sombra-suave); opacity: .95; will-change: transform; }
.rep-chip-x, .rep-chip-agg, .rep-chip-bucket { border: none; background: rgba(0,0,0,.06); color: inherit; border-radius: 999px; cursor: pointer; font-weight: 800; font-size: .66rem; line-height: 1; padding: 4px 7px; }
.rep-chip-x { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; }
.rep-chip-x:hover { background: var(--color-alerta-alto); color: #fff; }
.rep-chip-agg:hover, .rep-chip-bucket:hover { background: var(--color-acento); color: #fff; }
@keyframes rep-chip-in { from { transform: scale(.6); opacity: 0; } }
.rep-chip--placed { animation: rep-chip-in .16s var(--ease-out); }

.rep-estantes { grid-area: estantes; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.rep-zone { border: 2px dashed var(--color-borde-suave); border-radius: var(--radio-lg); background: var(--color-superficie); padding: 10px; min-height: 84px; transition: border-color var(--dur-1), background var(--dur-1), opacity var(--dur-1); }
.rep-zone:has(.rep-chip) { border-style: solid; }
.rep-zone-tit { font-size: var(--txt-xs, .72rem); font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); display: flex; align-items: center; gap: 5px; margin-bottom: 8px; }
.rep-zone-drop { display: flex; flex-wrap: wrap; gap: 8px; min-height: 40px; align-content: flex-start; }
.rep-zone--vacia .rep-zone-drop::before { content: 'Arrastra o toca aquí'; font-size: .74rem; font-style: italic; color: var(--color-gris); }
.rep-dragging .rep-zone--invalid { opacity: .4; }
.rep-zone--valid { border-color: var(--color-acento); }
.rep-zone--hot { border-color: var(--color-acento); background: var(--color-alerta-fondo); }
/* ---- Constructor con TECLADO -------------------------------------------
   El chip cogido y la zona candidata necesitan verse, porque el usuario de
   teclado no tiene un cursor que le diga donde va a caer la cosa. Se reutiliza
   el mismo lenguaje del arrastre (borde de acento) para que las dos formas de
   usar el constructor se lean igual. */
.rep-zone--teclado {
  border-style: solid;
  border-color: var(--color-acento);
  background: var(--color-alerta-fondo);
  box-shadow: 0 0 0 3px var(--color-alerta-fondo);
}
/* Mientras hay un campo cogido, las zonas que NO lo admiten se apagan: es la
   misma pista que da el arrastre, y evita que alguien navegue a ciegas hacia una
   zona que va a rechazarlo. */
.rep-cogiendo .rep-zone:not(.rep-zone--teclado) { opacity: .45; }
/* Los chips del panel son <span> con tabindex: sin esto el foco no se ve, y un
   foco invisible es exactamente igual de inutil que no poder enfocar. */
.rep-chip[tabindex]:focus-visible {
  outline: 2px solid var(--color-acento);
  outline-offset: 2px;
}
.rep-ph { width: 3px; align-self: stretch; min-height: 34px; background: var(--color-acento); border-radius: 3px; }

.rep-preview { grid-area: preview; border: 1px solid var(--color-borde-suave); border-radius: var(--radio-lg); background: var(--color-superficie); overflow: hidden; }
.rep-preview { box-shadow: var(--sombra-suave); }
.rep-vista { display: flex; gap: 4px; padding: 8px; border-bottom: 1px solid var(--color-borde-suave); flex-wrap: wrap; }
.rep-vista button { border: 1px solid var(--color-borde-suave); background: var(--color-superficie); color: var(--color-texto-suave); border-radius: 999px; padding: 6px 12px; font-size: var(--txt-sec, .78rem); font-weight: 700; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; }
.rep-vista button.activo { background: var(--color-tinta, #22223B); background: var(--chrome-bg); color: var(--chrome-texto); border-color: transparent; }
.rep-lienzo { padding: 12px; min-height: 260px; position: relative; }
.rep-vacio { text-align: center; padding: 32px 16px; }
.rep-vacio h3 { margin: 8px 0 4px; }
/* --color-acento-fuerte y no --color-acento: el ámbar #E9940A funciona como
   FONDO, pero como texto sobre claro da 2,02:1 —medido en "Todas"/"Ninguna" de
   los desplegables de flooring— cuando el mínimo es 4,5. El proyecto ya tiene el
   tono para esto: es el mismo que usan los enlaces normales (app.css:151) y en
   oscuro se voltea solo a #F0B24A.
   El padding vertical no es cosmético: sin él el objetivo táctil medía 16 px de
   alto, por debajo de los 24 que pide WCAG 2.2. */
.rep-link { background: none; border: none; color: #8A5602; font-weight: 700; cursor: pointer; padding: 5px 3px; min-height: 24px; text-decoration: underline; }
/* Un tono propio y no el token global: --color-acento-fuerte (#9A6103) pasa sobre
   blanco (5,14) pero se queda en 4,30 sobre el panel crema de los desplegables de
   flooring, que es justo donde vive este enlace. #8A5602 pasa en los dos (5,14 y
   6,15). No se toca el token porque lo usan 71 reglas.
   En OSCURO manda el token, que ya se voltea a un ámbar claro; dejar el oscuro
   fijo lo haría ilegible sobre fondo oscuro. */
@media (prefers-color-scheme: dark) { .rep-link { color: var(--color-acento-fuerte); } }
:root[data-theme="dark"] .rep-link { color: var(--color-acento-fuerte); }
/* El contenedor tiene que tener ALTURA para que la cabecera pegajosa de abajo
   funcione. Con sólo `overflow-x: auto` su alto era el del contenido, así que
   quien scrolleaba era la página y el `position: sticky` no llegaba a activarse
   nunca: a las ~20 filas la cabecera se iba y las columnas de cifras se quedaban
   sin rótulo. Con el tope por defecto de 1.000 filas eso son cientos de números
   sin saber de qué columna son. Mismo patrón que .tabla-scroll--sticky. */
.rep-tabla-wrap { overflow: auto; max-height: min(70vh, calc(100vh - 260px)); }
.rep-chart-wrap { position: relative; }
/* El alto vive en el LIENZO, no en el contenedor. Con height:340px en el
   contenedor, la cabecera (título + Ampliar) le robaba altura al gráfico, y al
   ampliar esa altura fija ganaba sobre inset:0 y la caja no se estiraba. */
.rep-chart-lienzo { position: relative; height: 340px; }
.rep-tabla { width: 100%; border-collapse: collapse; font-size: var(--txt-sm, .82rem); }
.rep-tabla th, .rep-tabla td { padding: 7px 10px; border-bottom: 1px solid var(--color-borde-suave); text-align: left; white-space: nowrap; }
.rep-tabla thead th { background: var(--color-superficie-alt); font-weight: 800; position: sticky; top: 0; z-index: 3; }
/* PIVOTE: la cabecera son dos filas. La segunda se pega justo debajo de la
   primera, y su desplazamiento se MIDE al pintar (--rep-th1) en vez de clavar un
   número: ese alto es padding 7+7 + la caja de línea de .82rem + el borde de
   2px de .rep-th-col, y cambia con el navegador y con el zoom. */
.rep-tabla thead tr:nth-child(2) th { top: var(--rep-th1, 32px); z-index: 2; }
.rep-th-col { text-align: center; border-bottom: 2px solid var(--color-acento); }
.rep-th-m { color: var(--color-acento-fuerte); }
/* Cabecera ordenable. El th ya no lleva padding propio: lo pone el botón, para
   que toda la celda sea zona de clic y no sólo el texto. */
.rep-tabla th:has(> .rep-th-orden) { padding: 0; }
.rep-th-orden {
  font: inherit; color: inherit; background: none; border: 0; cursor: pointer;
  width: 100%; padding: 7px 10px; text-align: right;
  display: inline-flex; align-items: center; justify-content: flex-end; gap: 6px;
  white-space: nowrap;
}
.rep-th-orden i { font-size: .8em; opacity: .45; }
.rep-th-orden:hover { background: var(--color-alerta-fondo); }
.rep-th-orden:hover i { opacity: .8; }
/* El orden activo no se transmite SÓLO por la flecha: también subraya. */
.rep-th-orden.es-activo { text-decoration: underline; text-underline-offset: 3px; }
.rep-th-orden.es-activo i { opacity: 1; }
.rep-th-orden:focus-visible { outline: 2px solid var(--color-texto); outline-offset: -2px; }
.rep-num { text-align: right; font-variant-numeric: tabular-nums; }
/* ── Banda de totales del constructor ──────────────────────────────────────────
   Responde a "¿comparado con qué?": el agregado REAL del periodo (no la suma de
   lo que se ve) y su variación contra el periodo anterior del mismo largo. Vive
   entre el selector de vista y el resultado porque es contexto de LO DE ABAJO,
   no una vista más. */
/* UNA SOLA FILA, a propósito. En dos filas (rótulo arriba, tarjetas abajo) medía
   123 px, y ese alto sale del hueco de la tabla —que ya se había quedado corta dos
   veces—. Así son 60 px: el rótulo va a la izquierda como una etiqueta más y cada
   medida pone su cifra y su variación en la misma línea. */
.rep-totales { display: flex; align-items: center; gap: 10px; padding: 7px 12px; border-bottom: 1px solid var(--color-borde-suave); background: var(--color-fondo-alt, var(--color-superficie)); }
.rep-tot-cab { display: flex; flex-direction: column; flex: 0 0 auto; max-width: 150px; }
.rep-tot-tit { font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); display: inline-flex; align-items: center; gap: 4px; }
.rep-tot-vs { font-size: .66rem; color: var(--color-texto-suave); line-height: 1.25; }
.rep-tot-grid { display: flex; gap: 8px; flex: 1 1 auto; min-width: 0; overflow-x: auto; }
.rep-tot-item { flex: 1 1 0; min-width: 128px; display: flex; flex-direction: column; gap: 0; padding: 5px 10px; border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px); background: var(--color-superficie); }
.rep-tot-l { font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--color-texto-suave); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rep-tot-cifra { display: flex; align-items: baseline; gap: 6px; }
.rep-tot-v { font-family: var(--fuente-titulo); font-size: 1.02rem; font-weight: 800; color: var(--color-texto); font-variant-numeric: tabular-nums; line-height: 1.2; white-space: nowrap; }
/* El aviso rompe la fila: si compitiera por el ancho con las tarjetas, en cuanto
   hubiera tres medidas quedaría en dos palabras por línea. */
.rep-totales { flex-wrap: wrap; }
.rep-tot-aviso { flex: 1 0 100%; margin: 0; font-size: var(--txt-xs, .72rem); line-height: 1.35; color: var(--color-alerta-medio); display: flex; align-items: center; gap: 5px; }
.rep-tot-aviso i { flex: 0 0 auto; }
/* CUARTA vez que este proyecto pisa lo mismo: el atributo `hidden` vale
   `display: none`, pero cualquier regla de clase con `display` lo gana por
   especificidad. Sin esto el aviso se "ocultaba" y seguía ocupando 16 px, que
   además se le restaban a la tabla porque --rep-banda mide la banda entera.
   Se cubre TODA la banda, no sólo el aviso, para no repetirlo a la quinta. */
.rep-totales[hidden],
.rep-tot-aviso[hidden] { display: none; }
/* --color-alerta-medio es un ámbar oscuro (#A25C00) pensado para fondo claro; en
   oscuro se cae a 2,4:1. Se aclara, igual que hacen los demás avisos. */
@media (prefers-color-scheme: dark) { .rep-tot-aviso { color: #E8A33D; } }
:root[data-theme="dark"] .rep-tot-aviso { color: #E8A33D; }
.rep-tot-d { font-size: var(--txt-micro, .7rem); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* SÓLO se colorea lo que tiene una dirección buena o mala clara: más unidades o
   más dólares es mejor. Un precio promedio que se mueve no es bueno ni malo —de
   hecho en la tabla .rep-pct--sube es ROJO, porque ahí subir es el precio— así que
   esas variaciones van en --neutro y la flecha sola dice la dirección. Pintarlas
   de verde sería afirmar algo que no sabemos.
   Los tonos salen de los tokens del proyecto; en oscuro el rojo y el verde
   saturados dejan de leerse y hay que pasar a la variante clara, por las DOS vías
   (preferencia del sistema e interruptor manual) porque la app tiene ambas. */
.rep-tot-d--sube  { color: var(--color-alerta-bajo); }
.rep-tot-d--baja  { color: var(--color-alerta-alto); }
.rep-tot-d--igual,
.rep-tot-d--neutro,
.rep-tot-d--nuevo { color: var(--color-texto-suave); font-weight: 600; }
@media (prefers-color-scheme: dark) {
  .rep-tot-d--sube { color: var(--color-alerta-bajo-txt); }
  .rep-tot-d--baja { color: var(--color-alerta-alto-txt); }
}
:root[data-theme="dark"] .rep-tot-d--sube { color: var(--color-alerta-bajo-txt); }
:root[data-theme="dark"] .rep-tot-d--baja { color: var(--color-alerta-alto-txt); }
/* En estrecho el rótulo pasa arriba: con 150 px reservados a la izquierda las
   tarjetas quedaban sin sitio para su cifra. */
@media (max-width: 760px) {
  .rep-totales { flex-direction: column; align-items: stretch; gap: 6px; }
  .rep-tot-cab { flex-direction: row; max-width: none; justify-content: space-between; align-items: baseline; gap: 8px; }
  .rep-tot-item { min-width: 116px; }
  .rep-tot-v { font-size: .95rem; }
}

/* El mensaje de "no hay nada" con su porqué y su arreglo. Centrado y con aire:
   ocupa el hueco de la tabla, así que un párrafo pegado a la esquina se lee como
   un error del sistema en vez de como una explicación. */
.rep-vacio-msg { padding: 30px 20px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; color: var(--color-texto-suave); }
.rep-vacio-msg p { margin: 0; max-width: 42ch; font-size: var(--txt-md, .9rem); }
.rep-vacio-ic { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 50%; background: var(--color-fondo-alt, rgba(127,127,140,.12)); font-size: 1.15rem; }

.rep-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; padding: 12px; }
.rep-kpi { border: 2px solid var(--color-borde); border-radius: var(--radio-lg); box-shadow: var(--sombra-dura-sm); padding: 18px; text-align: center; }
.rep-kpi-n { font-family: var(--fuente-titulo); font-size: 1.5rem; font-weight: 800; color: var(--color-texto); font-variant-numeric: tabular-nums; }
.rep-kpi-l { font-size: var(--txt-xs, .72rem); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); margin-top: 3px; }
@keyframes rep-skel { 0%,100% { opacity: .5; } 50% { opacity: 1; } }
.rep-skel { height: 26px; border-radius: 8px; background: var(--color-superficie-alt); margin: 8px 0; animation: rep-skel 1.1s ease-in-out infinite; }

.rep-toast { position: fixed; left: 50%; bottom: 22px; transform: translate(-50%, 20px); background: var(--chrome-bg); color: var(--chrome-texto); padding: 11px 18px; border-radius: 999px; font-size: .84rem; font-weight: 700; box-shadow: var(--sombra-suave); z-index: 10001; opacity: 0; transition: transform var(--dur-2) var(--ease-out), opacity var(--dur-2); max-width: 90vw; text-align: center; }
.rep-toast--on { transform: translate(-50%, 0); opacity: 1; }

@media (max-width: 760px) {
  .rep-flujo { display: flex; overflow-x: auto; padding-bottom: 5px; scroll-snap-type: x proximity; }
  .rep-flujo li { flex: 0 0 220px; scroll-snap-align: start; }
  .rep-barra { align-items: stretch; }
  .rep-fecha { width: 100%; justify-content: center; }
  .rep-barra-acciones { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; }
  .rep-barra-acciones .btn { width: 100%; justify-content: center; }
  .rep-estantes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .rep-zone { min-height: 96px; }
}

.rep-pop { position: absolute; z-index: 10000; background: var(--color-superficie); border: 1px solid var(--color-borde); border-radius: 14px; box-shadow: var(--sombra-suave); padding: 12px; min-width: 220px; max-width: 320px; }
.rep-pop-tit { font-weight: 800; font-size: var(--txt-sm, .82rem); margin-bottom: 8px; }
.rep-pop-list { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.rep-pop-item { display: flex; align-items: center; gap: 8px; padding: 5px 4px; font-size: .84rem; border-radius: 8px; cursor: pointer; }
.rep-pop-item:hover { background: var(--color-superficie-alt); }
.rep-pop-acc { display: flex; align-items: center; gap: 10px; margin-top: 10px; }

/* Popover de filtro rediseñado */
.rep-pop--filtro { min-width: 264px; max-width: 340px; padding: 0; overflow: hidden; animation: rep-chip-in .14s var(--ease-out); }
.rep-pop--filtro .rep-pop-tit { display: flex; align-items: center; gap: 7px; margin: 0; padding: 12px 14px 10px; border-bottom: 1px solid var(--color-borde-suave); }
.rep-pop--filtro .rep-pop-tit i { color: var(--color-acento); }
.rep-pop--filtro .rep-pop-body { padding: 10px 12px 12px; }
.rep-pop-cargando { margin: 4px 2px; font-size: var(--txt-sm, .82rem); }
.rep-pop-buscar { width: 100%; border: 1.5px solid var(--color-borde-suave); border-radius: 10px; padding: 8px 11px; font-size: .84rem; font-family: inherit; background: var(--color-superficie-alt); color: var(--color-texto); margin-bottom: 8px; }
.rep-pop-buscar:focus { outline: none; border-color: var(--color-acento); background: var(--color-superficie); }
.rep-pop-herr { display: flex; align-items: center; gap: 7px; font-size: .74rem; color: var(--color-texto-suave); margin-bottom: 6px; padding: 0 2px; }
.rep-pop-herr .rep-link { font-size: .74rem; text-decoration: none; }
.rep-pop-herr .rep-link:hover { text-decoration: underline; }
.rep-pop-n { margin-left: auto; font-size: var(--txt-micro, .7rem); font-weight: 700; color: var(--color-gris); }
.rep-pop-item input[type="checkbox"] { accent-color: var(--color-acento); width: 16px; height: 16px; flex: none; }
.rep-pop-val { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rep-pop-cnt { font-size: var(--txt-micro, .7rem); color: var(--color-gris); font-variant-numeric: tabular-nums; flex: none; }
.rep-pop--filtro .rep-pop-acc { position: sticky; bottom: 0; background: var(--color-superficie); border-top: 1px solid var(--color-borde-suave); padding-top: 10px; }

/* Chips de filtro: estado activo + edición */
.rep-chip--editable { cursor: pointer; }
.rep-chip--editable:hover { border-color: var(--color-acento); }
.rep-chip-caret { font-size: .58rem; opacity: .55; }
.rep-chip--filtro-activo { background: var(--color-alerta-fondo); border-color: var(--color-acento); color: var(--color-acento-fuerte); }
.rep-chip--filtro-activo > i:first-child { color: var(--color-acento); }
.rep-chip-n { background: var(--color-acento); color: #fff; border-radius: 999px; min-width: 19px; height: 19px; padding: 0 5px; display: inline-flex; align-items: center; justify-content: center; font-size: .66rem; font-weight: 800; line-height: 1; }
.rep-pop-presets { display: flex; flex-direction: column; gap: 4px; }
.rep-preset { text-align: left; border: 1px solid var(--color-borde-suave); background: var(--color-superficie); border-radius: 8px; padding: 8px 11px; font-size: var(--txt-sm, .82rem); font-weight: 600; cursor: pointer; }
.rep-preset.activo, .rep-preset:hover { border-color: var(--color-acento); color: var(--color-acento-fuerte); }

.rep-sheet-ov { position: fixed; inset: 0; background: rgba(34,34,59,.5); z-index: 10000; display: flex; align-items: flex-end; justify-content: center; padding: 0; }
.rep-sheet { background: var(--color-fondo); width: 100%; max-width: 720px; max-height: 82vh; overflow-y: auto; border-radius: 18px 18px 0 0; padding: 18px; animation: rep-chip-in .2s var(--ease-out); }
@media (min-width: 720px) { .rep-sheet-ov { align-items: center; } .rep-sheet { border-radius: 18px; } }
.rep-sheet-tit { display: flex; align-items: center; justify-content: space-between; }
.rep-sheet-tit h3 { margin: 0; }
/* Subtítulo de grupo de plantillas por fuente (Precios / Flooring / Status Day). */
.rep-sheet-sub { margin-top: 16px; }
.rep-sheet-sub h3 { font-size: .8rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); display: flex; align-items: center; gap: 7px; }
.rep-sheet-sub h3 i { color: var(--color-acento); font-size: 1rem; }
.rep-sheet-x { border: none; background: var(--color-superficie-alt); width: 34px; height: 34px; border-radius: 999px; cursor: pointer; font-weight: 800; }
.rep-sheet-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; margin-top: 12px; }
.rep-tpl { text-align: left; display: flex; flex-direction: column; gap: 3px; padding: 13px; border: 1.5px solid var(--color-borde-suave); border-radius: var(--radio-lg); background: var(--color-superficie); cursor: pointer; transition: transform var(--dur-1), border-color var(--dur-1); }
.rep-tpl:hover { transform: translateY(-2px); border-color: var(--color-acento); }
.rep-tpl-ic { width: 36px; height: 36px; border-radius: 10px; background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); display: grid; place-items: center; font-size: 1.2rem; margin-bottom: 4px; }
.rep-tpl strong { font-size: var(--txt-md, .9rem); }
.rep-tpl span.text-suave { font-size: .76rem; line-height: 1.3; }
.rep-tpl--guardado .rep-tpl-ic { background: var(--color-superficie-alt); color: var(--color-acento); }

/* ===========================================================================
   REPORTES ADMIN — mejoras de la auditoría (jul 2026)
   Estilos de: filtros de ciudad, avisos de truncamiento, exportación,
   badges de frescura, índice por almacén, enlaces cruzados y hojas modales
   de la sección Reportes. Ver memoria: trade-software-reportes-admin.
   ========================================================================== */
/* ===== dashboard ===== */
/* ------------------------------------------------------------------
   Dashboard por modelo (admin/dashboard.php)
   ------------------------------------------------------------------ */

/* Subtítulo que aclara la BASE DE CÁLCULO de cada KPI: en la misma pantalla
   convivían dos "promedios" distintos con etiquetas casi iguales y el gerente
   veía dos cifras sin explicación. */
.resumen-pill-nota {
  display: block;
  margin-top: 4px;
  font-size: .68rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-texto-suave);
}

/* Outliers desglosados en altos/bajos (sustituyen al número único del KPI):
   un competidor 15% más caro es una oportunidad y uno 15% más barato una
   amenaza, sumarlos escondía la lectura. Las dos últimas reglas ganan en
   especificidad a `.resumen-pill span`, que si no les impondría el color
   suave y el uppercase de las etiquetas del pill. */
.resumen-pill-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 2px;
}
.resumen-pill-badges .badge {
  font-size: .74rem;
  text-transform: none;
  letter-spacing: .02em;
}
.resumen-pill-badges .badge-alto { color: var(--color-alerta-alto-txt); }   /* -txt, no el de relleno: sobre el rosa fijo #FDE2E1 daba 4,05:1 y el comentario de .badge-alto (app.css:3002) ya lo advertía */
.resumen-pill-badges .badge-bajo { color: var(--color-alerta-bajo); }

/* Ranking del estado vacío: marca y nombre comercial bajo el código de modelo. */
.db-rank-sub {
  display: block;
  font-size: var(--txt-sec, .78rem);
  line-height: 1.3;
  margin-top: 2px;
}

/* Enlace cruzado a la percha del almacén, junto al nombre en la tabla
   (el botón que abre el modal de almacén se conserva intacto). */
.db-link-percha {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 6px;
  padding: 6px;
  border-radius: 8px;
  line-height: 1;
  color: var(--color-texto-suave);
}
@media (hover: hover) {
  .db-link-percha:hover {
    color: var(--color-acento-fuerte);
    background: var(--color-borde-suave);
  }
}
.db-link-percha:focus-visible {
  outline: 3px solid var(--color-energia-txt);
  outline-offset: 2px;
}

/* ===== resumen ===== */
/* Resumen ejecutivo: modelos enlazables (mismo look que .celda-enlace, que es un
   <button> para el modal; esto es un <a> de navegación al detalle del modelo). */
.enlace-modelo {
  color: var(--color-texto);
  font-weight: 700;
  text-decoration: none;
}
@media (hover: hover) {
  .enlace-modelo:hover { color: var(--color-acento-fuerte); text-decoration: underline; text-underline-offset: 3px; }
}
.enlace-modelo:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: 2px; border-radius: 4px; }

/* A1: la fila pasó de 2 a 3 elementos (modelo · n · %). El modelo se lleva el
   espacio sobrante y el tamaño de muestra queda discreto entre ambos. */
.mov-lista li .mov-modelo { flex: 1 1 auto; }
.mov-n {
  flex: 0 0 auto;
  font-size: var(--txt-xs, .72rem);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* D14: tarjeta "Pendientes y pulso" */
.pendientes-lista { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.pendiente-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 12px;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  background: var(--color-superficie-alt);
}
.pendiente-item > div { min-width: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
.pendiente-cifra {
  font-family: var(--fuente-titulo);
  font-size: 1.5rem;
  font-weight: 800; /* va en un <span>, no hereda el bold de <strong> como .hero-kpi */
  line-height: 1;
}
.pendiente-cifra--alerta { color: var(--color-alerta-alto); }
.pendiente-nota { flex-basis: 100%; font-size: .74rem; }

/* ===== cobertura ===== */
/* --------------------------------------------------------------------------
   COBERTURA — añadidos de la ronda de auditoría.
   IMPORTANTE: estas reglas deben ir DESPUÉS del bloque .mapa-cobertura que ya
   existe (~línea 2725) y después de su media query de 720px: comparten
   especificidad (una clase) y ganan por orden de aparición.
   -------------------------------------------------------------------------- */

/* Encabezado del mapa: botón de exportar + chips de capa en la misma fila */
.cobertura-acciones {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 10px;
}

/* Chip de la capa "Sin visita": punto hueco, igual que su marcador en el mapa */
.chip-capa-sinvisita .punto-leyenda {
  background: transparent;
  border: 2px solid var(--color-texto-suave);
  box-sizing: border-box;
}

/* Punto hueco de la leyenda (sin GPS / sin visita). El color concreto se pasa
   inline desde el JS con border-color, para que leyenda y marcador no se
   desincronicen nunca. */
.mapa-leyenda-fila .punto-leyenda--hueco {
  background: transparent;
  border: 2px solid currentColor;
  box-sizing: border-box;
}

/* Si el CDN de Leaflet no cargó, el contenedor deja de reservar los 460 px y
   se comporta como un estado vacío normal (antes quedaba un hueco en blanco). */
.mapa-cobertura--sin-libreria {
  height: auto;
  min-height: 0;
  border: none;
  background: transparent;
}
@media (max-width: 720px) {
  .mapa-cobertura--sin-libreria { height: auto; }
}

/* ===== comparativo ===== */
/* ---------------------------------------------------------------------------
   Comparativo · orden de filas, índice del almacén y frescura del dato.
   (clases nuevas usadas por admin/comparativo.php)
   ------------------------------------------------------------------------- */

/* Barra "Ordenar almacenes": reutiliza .tabs-tamanio/.tab-tamanio y sólo añade
   la etiqueta que las precede. */
.matriz-orden { align-items: center; margin-bottom: 14px; }
.matriz-orden-etq {
  font-size: var(--txt-xs, .72rem);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-texto-suave);
  margin-right: 2px;
}

/* Aclaración en la esquina de la matriz ("Almacén e índice"). */
.matriz-esquina-nota {
  display: block;
  font-size: .6rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--color-texto-suave);
}

/* Índice de precio del almacén: promedio de sus deltas frente al mercado.
   El color lo aportan las clases nv--2 … nv-2 del heatmap (no se tocan aquí).
   Va en un <small> a propósito: la regla existente
   ".matriz tbody th[scope=row] span" pisaría color y display si fuera un span. */
.matriz-indice {
  display: inline-flex;
  align-items: center;
  margin-top: 2px;
  padding: 1px 6px;
  border: 1px solid var(--color-borde);
  border-radius: 99px;
  font-size: .6rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* El bloque duplicado que había aquí se quitó: definía .celda-precio--antigua
   por segunda vez con `left: 5px`, y como el bloque bueno (más abajo) pide
   `right: 4px` sin resetear `left`, con un ancho fijo el navegador se quedaba
   con `left` y el punto de "dato viejo" salía en la esquina IZQUIERDA, no en la
   derecha que describe su comentario. También arrastraba opacity .62 y un color
   distintos de los del bloque que sí manda. */

/* Precio EN PROMOCIÓN: no se esconde (es un precio real de la percha) pero sí
   se distingue. Sin esto, una celda barata por descuento temporal se leía como
   "esta tienda está barata", que es la conclusión contraria a la buena.
   La etiqueta va con ICONO y no sólo con color: en el comparativo el color de
   fondo de la celda ya significa otra cosa (el nivel de precio). */
.celda-precio--promo { position: relative; }
.celda-precio-promo-ic {
  font-size: .72em;
  margin-right: 3px;
  vertical-align: baseline;
  opacity: .85;
}

/* ===== reporte ===== */
/* ---- Reporte a medida: variación %, aviso de tope y gestión de guardados ---- */
.rep-pct { font-weight: 800; font-variant-numeric: tabular-nums; }
.rep-pct--sube { color: var(--color-alerta-alto); }
.rep-pct--baja { color: var(--color-alerta-bajo); }

/* Aviso dentro del popover de filtros cuando se alcanza el tope de valores */
.rep-pop-aviso { display: flex; align-items: flex-start; gap: 6px; font-size: var(--txt-xs, .72rem); line-height: 1.3; color: var(--color-texto-suave); background: var(--color-alerta-fondo); border-radius: 8px; padding: 7px 9px; margin: 0 0 8px; }
.rep-pop-aviso i { color: var(--color-acento); flex: none; margin-top: 1px; }

/* Tarjeta de reporte guardado: contenedor + botón de cargar + acciones */
.rep-tpl--guardado { padding: 0; cursor: default; }
.rep-tpl--guardado:hover { transform: none; border-color: var(--color-borde-suave); }
.rep-tpl-cargar { display: flex; flex-direction: column; gap: 3px; width: 100%; text-align: left; border: none; background: none; color: inherit; font: inherit; padding: 13px 13px 8px; cursor: pointer; border-radius: var(--radio-lg) var(--radio-lg) 0 0; }
.rep-tpl-cargar:hover { background: var(--color-superficie-alt); }
.rep-tpl-cargar strong { font-size: var(--txt-md, .9rem); }
.rep-tpl-cargar span.text-suave { font-size: .76rem; line-height: 1.3; }
.rep-tpl-acc { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 0 13px 11px; }
.rep-tpl-acc .rep-link { font-size: .74rem; text-decoration: none; }
.rep-tpl-acc .rep-link:hover { text-decoration: underline; }
.rep-link--peligro { color: var(--color-alerta-alto); }

/* Diálogos de Enviar / Guardar (reemplazan prompt()+confirm()) */
.rep-dlg { border: 2px solid var(--color-borde); border-radius: var(--radio-lg); background: var(--color-superficie); color: var(--color-texto); box-shadow: var(--sombra-dura-sm); padding: 0; width: min(430px, calc(100vw - 28px)); }
.rep-dlg::backdrop { background: rgba(34, 34, 59, .55); backdrop-filter: blur(2px); }
.rep-dlg[open] { animation: rep-chip-in .16s var(--ease-out); }
.rep-dlg-form { padding: 18px; display: flex; flex-direction: column; gap: 7px; }
.rep-dlg-tit { margin: 0; display: flex; align-items: center; gap: 8px; font-size: 1.05rem; }
.rep-dlg-tit i { color: var(--color-acento); }
.rep-dlg-sub { margin: 0 0 4px; font-size: var(--txt-sm, .82rem); line-height: 1.35; }
.rep-dlg-lbl { font-size: var(--txt-xs, .72rem); text-transform: uppercase; letter-spacing: .04em; font-weight: 800; color: var(--color-texto-suave); }
.rep-dlg-input { width: 100%; border: 1.5px solid var(--color-borde-suave); border-radius: 10px; padding: 10px 12px; font: inherit; font-size: var(--txt-md, .9rem); background: var(--color-superficie-alt); color: var(--color-texto); }
.rep-dlg-input:focus { outline: none; border-color: var(--color-acento); background: var(--color-superficie); }
.rep-dlg-check { display: flex; align-items: flex-start; gap: 9px; font-size: .84rem; line-height: 1.35; margin-top: 8px; cursor: pointer; }
.rep-dlg-check input { accent-color: var(--color-acento); width: 18px; height: 18px; flex: none; margin-top: 1px; }
.rep-dlg-acc { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

/* ===== verificador ===== */
/* ==== Dashboard por modelo: piezas nuevas (v-audit) ==== */

/* Nota al pie de cada KPI: dice de qué promedio sale la cifra. Es un <small>,
   así que NO lo alcanza la regla `.resumen-pill span` (uppercase + .72rem). */
.resumen-pill-nota {
  display: block;
  margin-top: 4px;
  font-size: .64rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-texto-suave);
}

/* El KPI de outliers cambió su <strong> por dos badges. Ocupa el hueco del
   <strong> (display:block, 1.25rem/1.1) para que las 4 tarjetas del grid sigan
   alineadas, y envuelve en la rejilla de 2 columnas del móvil. */
.resumen-pill-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  min-height: 1.4rem;
}

/* IMPORTANTE: `.resumen-pill span` (0,1,1) le gana a `.badge-alto` (0,1,0) y
   pintaba los badges de gris, incluido el punto ::before (currentColor). Estas
   reglas (0,2,0) le devuelven su color y su caja. */
.resumen-pill .badge {
  padding: 2px 9px;
  font-size: .66rem;
  font-weight: 800;
  letter-spacing: .02em;
  text-transform: none;
}
.resumen-pill .badge-alto { color: var(--color-alerta-alto-txt); }   /* -txt, no el de relleno: sobre el rosa fijo #FDE2E1 daba 4,05:1 y el comentario de .badge-alto (app.css:3002) ya lo advertía */
.resumen-pill .badge-bajo { color: var(--color-alerta-bajo); }

/* Atajo a la percha del almacén, junto al botón que abre el modal. Caja de 28px
   para que sea tocable sin separarse del nombre. */
.db-link-percha {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin-left: 2px;
  vertical-align: middle;
  border-radius: 8px;
  color: var(--color-texto-suave);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease-out), background var(--dur-1);
}
@media (hover: hover) {
  .db-link-percha:hover {
    color: var(--color-acento-fuerte);
    background: var(--color-fondo);
  }
}
.db-link-percha:focus-visible {
  outline: 3px solid var(--color-energia-txt);
  outline-offset: 2px;
}

/* Segunda línea (marca · nombre comercial) del ranking del estado vacío. */
.db-rank-sub {
  display: block;
  margin-top: 2px;
  font-size: var(--txt-xs, .72rem);
  line-height: 1.3;
}

/* ===== verificador ===== */
/* === Matriz comparativa: índice por almacén, barra de orden y frescura ===
   Clases nuevas usadas por admin/comparativo.php. Sin ellas la página no se
   rompe, pero el chip de índice queda sin píldora y el dato viejo no se
   distingue del reciente (que es justo lo que la mejora pretende evitar). */

/* Nota "e índice" en la cabecera de esquina: avisa de que la columna congelada
   lleva dos datos (nombre del almacén + su índice de precio). */
.matriz-esquina-nota {
  display: block;
  font-size: .62rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: none;
  color: var(--color-texto-suave);
}

/* Índice de precio del almacén. Vive DENTRO del th[scope=row] (la única celda
   sticky de la fila) para seguir visible al desplazar la matriz en horizontal.
   El color lo pone la clase nv-* que ya existe para el heatmap. */
.matriz tbody th[scope="row"] .matriz-indice {
  display: inline-block;
  margin-top: 4px;
  padding: 2px 8px;
  border: 1.5px solid var(--color-borde);
  border-radius: 99px;
  font-size: .66rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  cursor: help;
}

/* Barra de orden de almacenes: mismas píldoras que las tabs de tamaño (no se
   toca su min-height de 44px para no regresar el trabajo de touch targets),
   con una etiqueta delante que las distingue de las tabs de tamaño. */
.matriz-orden {
  align-items: center;
  gap: 8px;
  margin-top: 0;
  margin-bottom: 14px;
}
.matriz-orden-etq {
  font-size: var(--txt-xs, .72rem);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-texto-suave);
  margin-right: 2px;
}

/* Dato viejo: dentro del mes en curso, un precio de hace más de 10 días se
   atenúa y lleva un punto en la esquina, para no decidir sobre datos viejos
   creyendo que son de ayer. Al pasar el cursor recupera opacidad plena. */
.celda-precio--antigua {
  position: relative;
  opacity: .58;
}
.celda-precio--antigua::after {
  content: '';
  position: absolute;
  top: 4px;
  right: 4px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: .8;
}
@media (hover: hover) {
  .celda-precio--antigua:hover { opacity: 1; }
}

/* ===== verificador ===== */
/* --- Cobertura: acciones del encabezado del mapa (exportar + chips de capas) --- */
.cobertura-acciones {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* Chip de la capa "Sin visita": punto hueco gris, para no confundirlo con los
   puntos llenos de las capas que SÍ representan actividad. */
.chip-capa-sinvisita .punto-leyenda {
  background: transparent;
  border: 2px solid var(--color-gris);
}

/* Punto hueco de la leyenda del mapa: el color va inline (lo pinta el JS con
   COLOR_SIN_GPS / COLOR_SIN_VISITA), aquí sólo se define la forma. */
.punto-leyenda--hueco {
  background: transparent;
  border: 2px solid var(--color-gris);
}

/* Si el CDN de Leaflet no cargó, el contenedor suelta la altura fija de 460px
   para no dejar un hueco vacío enorme: sólo ocupa lo que mide el aviso. */
.mapa-cobertura--sin-libreria {
  height: auto;
  min-height: 0;
  border-style: dashed;
  background: var(--color-superficie);
}
.mapa-cobertura--sin-libreria .empty-state {
  border: 0;
  box-shadow: none;
  background: transparent;
}

/* ===== verificador ===== */
/* ==========================================================================
   Reporte a medida — piezas nuevas (diálogos, guardados, variación %, avisos)
   Se incluye TAMBIÉN el CSS de la ronda anterior porque app.css todavía no lo
   tiene: verificado con grep, sólo existe .rep-tpl--guardado .rep-tpl-ic.
   Si el orquestador ya aplicó el CSS del otro agente, las reglas duplicadas
   son idénticas en intención y la última gana; no rompen nada.
   ========================================================================== */

/* --- Variación %: se lee de un vistazo (sube = rojo, baja = verde) --- */
.rep-pct { font-weight: 800; font-variant-numeric: tabular-nums; }
.rep-pct--sube { color: var(--color-alerta-alto); }
.rep-pct--baja { color: var(--color-alerta-bajo); }

/* --- Aviso de tope de filas bajo la tabla (truncamiento nunca silencioso) --- */
.rep-aviso-tope {
  display: flex; align-items: center; gap: 7px;
  margin: 10px 2px 2px; padding: 9px 12px;
  background: var(--color-alerta-fondo);
  border: 1.5px solid var(--color-borde-suave);
  border-radius: 10px;
  /* --color-alerta-fondo es crema fijo (no cambia en modo oscuro), así que el
     texto va con --color-acento-fuerte como el resto de avisos del proyecto:
     con --color-texto-suave quedaba gris claro sobre crema e ilegible. */
  font-size: .76rem; line-height: 1.35; color: var(--color-acento-fuerte);
}
.rep-aviso-tope i { color: var(--color-acento); flex: none; font-size: .95rem; }

/* --- Aviso dentro del popover de filtros (tope de 500 valores) --- */
.rep-pop-aviso {
  display: flex; align-items: flex-start; gap: 6px;
  margin: 0 0 8px; padding: 7px 9px;
  background: var(--color-alerta-fondo);
  border-radius: 8px;
  /* Mismo motivo que .rep-aviso-tope: fondo crema fijo → texto oscuro. */
  font-size: var(--txt-xs, .72rem); line-height: 1.3; color: var(--color-acento-fuerte);
}
.rep-pop-aviso i { color: var(--color-acento); flex: none; margin-top: 1px; }

/* --- Diálogos de Enviar / Guardar (sustituyen prompt()+confirm()) --- */
.rep-dlg {
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  box-shadow: var(--sombra-dura);
  background: var(--color-superficie);
  color: var(--color-texto);
  padding: 0;
  width: min(94vw, 420px);
}
.rep-dlg::backdrop { background: rgba(34, 34, 59, .5); }
.rep-dlg-form { display: flex; flex-direction: column; gap: 9px; padding: 18px; }
.rep-dlg-tit { display: flex; align-items: center; gap: 8px; margin: 0 0 2px; font-size: 1.02rem; }
.rep-dlg-tit i { color: var(--color-acento); }
.rep-dlg-sub { margin: 0; font-size: .8rem; line-height: 1.35; }
.rep-dlg-lbl { font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); }
.rep-dlg-input {
  width: 100%; box-sizing: border-box;
  border: 1.5px solid var(--color-borde-suave); border-radius: 10px;
  padding: 11px 12px; font-size: var(--txt-md, .9rem); font-family: inherit;
  background: var(--color-superficie-alt); color: var(--color-texto);
}
.rep-dlg-input:focus { outline: none; border-color: var(--color-acento); background: var(--color-superficie); }
.rep-dlg-check { display: flex; align-items: flex-start; gap: 8px; font-size: var(--txt-sm, .82rem); line-height: 1.3; cursor: pointer; margin-top: 2px; }
.rep-dlg-check input[type="checkbox"] { accent-color: var(--color-acento); width: 18px; height: 18px; flex: none; margin-top: 1px; }
.rep-dlg-acc { display: flex; gap: 8px; margin-top: 6px; }
.rep-dlg-acc .btn { flex: 1; justify-content: center; }

/* --- Tarjeta de reporte guardado: cargar + acciones de mantenimiento --- */
.rep-tpl--guardado { padding: 0; cursor: default; overflow: hidden; }
.rep-tpl--guardado:hover { transform: none; }
.rep-tpl-cargar {
  text-align: left; display: flex; flex-direction: column; gap: 3px;
  width: 100%; border: none; background: none; color: inherit;
  padding: 13px 13px 9px; cursor: pointer; font: inherit;
}
.rep-tpl-cargar strong { font-size: var(--txt-md, .9rem); }
.rep-tpl-cargar span.text-suave { font-size: .76rem; line-height: 1.3; }
.rep-tpl-cargar:hover strong { color: var(--color-acento-fuerte); }
.rep-tpl-acc {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 13px; min-height: 40px;
  border-top: 1px solid var(--color-borde-suave);
  background: var(--color-superficie-alt);
}
.rep-tpl-acc .rep-link { font-size: .74rem; }
.rep-link--peligro { color: var(--color-alerta-alto); margin-left: auto; }
.rep-link--peligro:hover { text-decoration: underline; }
/* ===== Percha: filtro de ciudad, buscador, frescura y enlaces cruzados ===== */
/* Buscador en cliente de la tabla de almacenes. El input hereda el estilo
   global de input[type=search]; el wrapper sólo resuelve el ancho dentro del
   .fila-entre (flex) que comparte con el botón de exportar. */
.percha-buscador { flex: 1 1 260px; min-width: 0; max-width: 380px; }
.percha-buscador input[type=search] { width: 100%; margin: 0; }

/* El select de ciudad: sin mínimo queda ridículo con nombres cortos, sin
   máximo se come la fila en escritorio. */
.percha-filtro-ciudad { min-width: 200px; max-width: 280px; }

/* Semáforo de frescura del snapshot, junto a la fecha de "Actualizado". El
   color lo ponen las clases badge-bajo/normal/alto que ya existen; aquí sólo
   la separación, el tamaño y que no parta la línea. */
.percha-frescura {
  margin-left: 6px;
  padding: 2px 8px;
  font-size: .66rem;
  vertical-align: middle;
  white-space: nowrap;
}

/* Botonera de enlaces cruzados de la tarjeta de ubicación del detalle. */
.percha-card-acciones { display: flex; flex-wrap: wrap; gap: var(--esp-sm); margin-top: var(--esp-md); }

/* Si el CDN de Leaflet no cargó, el mini-mapa suelta su altura fija para que
   el aviso no quede flotando en un hueco vacío. Mismo criterio que
   .mapa-cobertura--sin-libreria, que ya existe para el mapa grande. */
.percha-mini-mapa--sin-libreria {
  height: auto;
  min-height: 0;
  border-style: dashed;
  background: var(--color-superficie);
}
.percha-mini-mapa--sin-libreria .empty-state-mini { border: 0; box-shadow: none; background: transparent; }

/* ===== Impulso de venta: dato secundario dentro de un KPI del hero =====
   El KPI principal pasó a ser el mes en curso; el acumulado histórico se
   conserva como línea de apoyo, deliberadamente más pequeña para que
   acompañe al dato principal sin competir con él. */
.hero-kpi-nota {
  display: block;
  margin-top: 4px;
  color: var(--color-texto-suave);
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .03em;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ===== Percha: galería de fotos panorámicas del almacén ===== */
.percha-fotos-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 12px;
}
.percha-foto {
  margin: 0;
  border: 2px solid var(--color-borde);
  border-radius: var(--radio-lg);
  overflow: hidden;
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
}
.percha-foto img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  cursor: zoom-in;
  background: var(--color-fondo);
}
.percha-foto-pie {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 8px 10px 10px;
  font-size: var(--txt-xs, .72rem);
  line-height: 1.25;
}
.percha-foto-pie i { font-size: .8rem; }
.percha-foto-cat { font-weight: 800; color: var(--color-texto); }
/* La fecha es el dato que da valor legal a la evidencia: se resalta. */
.percha-foto-fecha { font-weight: 700; color: var(--color-acento-fuerte); font-variant-numeric: tabular-nums; }
.percha-foto-autor { color: var(--color-texto-suave); font-weight: 700; }

/* ===== Paginación del editor de producto ===== */
.pe-paginacion {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 14px;
}
.pe-paginacion .btn { min-width: 40px; justify-content: center; }

/* Al guardar desde el modal la lista NO se recarga: sólo se repinta la fila.
   Sin esta señal el admin cierra el modal y no sabe si el cambio se aplicó,
   porque la tabla se ve igual que antes salvo por una palabra. */
.pe-fila-actualizada > td {
  background: color-mix(in srgb, var(--color-acento) 16%, transparent);
  animation: peFilaDestello 1.6s ease-out;
}
@keyframes peFilaDestello {
  from { background: color-mix(in srgb, var(--color-acento) 42%, transparent); }
  to   { background: color-mix(in srgb, var(--color-acento) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  /* Sin animación el destello se queda fijo hasta que el JS quita la clase:
     el aviso se sigue viendo, simplemente no parpadea. */
  .pe-fila-actualizada > td { animation: none; }
}

/* ============================================================================
   MODO OSCURO — arreglos de fondos CLAROS FIJOS (sin variable) que dejaban el
   texto ilegible. VA AL FINAL A PROPÓSITO: estas reglas tienen la misma
   especificidad que las reglas base (p. ej. .wizard-step.completo de la línea
   ~939), así que deben ir DESPUÉS en el archivo para ganar. No mover hacia
   arriba. Sólo afecta a modo oscuro; el modo claro queda igual.
   ========================================================================= */
@media (prefers-color-scheme: dark) {
  /* Excepción del volteo de --color-acento-fuerte (ahora ámbar claro): este badge
     lo usaba como FONDO con texto blanco; se le da fondo energía sólido. */
  .almacen-ficha-foto-hint { background: var(--color-energia, #FF5A1F); }

  /* Pasos completados del wizard: verde claro fijo → verde oscuro + ícono claro. */
  .wizard-step.completo { background: rgba(27, 122, 67, .32); border-color: var(--color-alerta-bajo); }
  .wizard-step.completo i { color: #79D9A2; }
  /* Paso activo (fondo ámbar sólido): ícono oscuro para contraste. */
  .wizard-step.activo i { color: #22223B; }

  /* Checklist de confirmación: verde claro fijo → verde oscuro + texto claro. */
  .confirm-checklist span { background: rgba(27, 122, 67, .26); border-color: var(--color-alerta-bajo); color: var(--color-texto); }
  .confirm-checklist span i { color: #79D9A2; }

  /* Referencia de precio (alto/bajo vs mercado): tintes claros → tintes oscuros. */
  .referencia-precio.alto { background: rgba(215, 38, 61, .18); }
  .referencia-precio.bajo { background: rgba(27, 122, 67, .20); }

  /* Alertas de éxito/error (fondo claro fijo con texto oscuro fijo): versión
     oscura coherente con .alerta-info (que ya se arregla por las variables). */
  .alerta-exito { background: rgba(27, 122, 67, .22); border-color: var(--color-alerta-bajo); color: #A7E9C0; }
  .alerta-error { background: rgba(215, 38, 61, .20); border-color: var(--color-alerta-alto); color: #F3B0B7; }

  /* Guía "código nuevo": el paso "hecho" usa verde oscuro fijo como texto; en
     oscuro necesita un verde claro para leerse sobre la superficie oscura. */
  .ncg-paso.ok { color: #6FD79B; }

  /* ---- (1) Semánticos como TEXTO ----------------------------------------
     El rojo/verde base (#D7263D / #1B7A43) no alcanza 4.5:1 como TEXTO sobre
     superficies oscuras (rojo ≈ 3.3:1). Se definen variantes CLARAS aparte
     (-txt) SÓLO para texto; los tokens base NO se tocan para no aclarar los
     FONDOS sólidos que los usan (.btn-peligro, .sd-kpi-ic, password-meter…). */
  :root {
    --color-alerta-alto-txt: #FF6B78;
    --color-alerta-bajo-txt: #57C98A;
  }
  /* Variación % de reportes (sube = rojo, baja = verde) */
  .rep-pct--sube { color: var(--color-alerta-alto-txt); }
  .rep-pct--baja { color: var(--color-alerta-bajo-txt); }
  /* Movimientos de precio (sube = verde, baja = rojo) */
  .mov-titulo--sube,
  .mov-pct--sube { color: var(--color-alerta-bajo-txt); }
  .mov-titulo--baja,
  .mov-pct--baja { color: var(--color-alerta-alto-txt); }
  /* Badges como texto en los pills (ganan por especificidad a la base) */
  .resumen-pill .badge-alto,
  .resumen-pill-badges .badge-alto { color: var(--color-alerta-alto-txt); }
  .resumen-pill .badge-bajo,
  .resumen-pill-badges .badge-bajo { color: var(--color-alerta-bajo-txt); }
  /* .ff-error, .ff-req y .ayuda.error NO necesitan regla aquí: desde que usan
     --color-alerta-alto-txt (declarado también en el :root de arriba con su
     valor claro), el relevo de estas dos líneas ya los invierte. Antes usaban
     --color-alerta-alto, que no se redefine, y se quedaban en 3,66:1 sobre el
     fondo oscuro en texto de 13 px. */
  /* La pastilla Sí/No/N-A marcada pinta background: var(--color-texto) con
     color: #fff. En oscuro --color-texto invierte a crema y el blanco es fijo:
     blanco sobre crema, 1,14:1 — la respuesta elegida DESAPARECE. Es la misma
     trampa que ya se arregló para .admin-subtab.activo; se copia el remedio de
     .chip-filtro.is-activo: naranja de marca con tinta navy fija (4,95:1). */
  .ff-seg-op:has(input:checked) {
    background: var(--color-energia, #FF5A1F);
    border-color: var(--color-energia, #FF5A1F);
    color: #22223B;
  }

  /* ---- (2) Fondos pastel hardcodeados → tintes rgba del color semántico ---
     Mismo patrón que .referencia-precio: fondo tenue + texto claro legible.
     El borde y el punto ::before de los badges usan currentColor, así que
     siguen al texto claro automáticamente. */
  .badge-alto { background: rgba(215, 38, 61, .20); color: var(--color-alerta-alto-txt); }
  /* Mismo tratamiento que sus hermanos: sin esto el ámbar claro fijo quedaría
     como un bloque crema sobre superficie oscura, con el texto marrón ilegible. */
  .badge-medio { background: rgba(233, 148, 10, .22); color: #F0B45C; }
  .badge-bajo { background: rgba(27, 122, 67, .22); color: var(--color-alerta-bajo-txt); }
  .var-chip.pos { background: rgba(27, 122, 67, .20); color: var(--color-alerta-bajo-txt); }
  .var-chip.neg { background: rgba(215, 38, 61, .18); color: var(--color-alerta-alto-txt); }
  .gps-badge.si { background: rgba(27, 122, 67, .18); color: var(--color-alerta-bajo-txt); border-color: var(--color-alerta-bajo-txt); }
  .mm-min { background: rgba(27, 122, 67, .20); color: var(--color-alerta-bajo-txt); }
  .mm-max { background: rgba(215, 38, 61, .20); color: var(--color-alerta-alto-txt); }
  /* Heatmap nv-*: escala barato(verde) → promedio(ámbar) → caro(rojo). */
  .nv--2 { background: rgba(27, 122, 67, .32); color: #79D9A2; }
  .nv--1 { background: rgba(27, 122, 67, .18); color: #79D9A2; }
  .nv-0  { background: rgba(233, 148, 10, .22); color: #F0B24A; }
  .nv-1  { background: rgba(255, 90, 31, .24); color: #FFB088; }
  .nv-2  { background: rgba(215, 38, 61, .26); color: var(--color-alerta-alto-txt); }

  /* ---- (3) Hover del botón fantasma -------------------------------------
     El tinte navy fijo rgba(34,34,59,.06) es casi invisible sobre superficies
     oscuras. Overlay adaptable con --color-texto (claro en oscuro). Se conserva
     el gate @media (hover: hover) de la regla base. */
  @media (hover: hover) {
    .btn-fantasma:not(:disabled):hover { background: color-mix(in srgb, var(--color-texto) 8%, transparent); }
  }

  /* ---- (4) Sobrecarga de negrita: en superficies oscuras los pesos 700/800
     "bloomean" y recargan la jerarquía. Se afinan SÓLO rótulos secundarios/meta
     (se conservan uppercase + letter-spacing + color-texto-suave para el efecto
     etiqueta). NO se tocan cifras hero, H1/H2 ni valores primarios. El modo
     claro queda idéntico: estos overrides viven sólo en el bloque oscuro. */
  /* Meta/apoyo: 700 → 600 */
  .notif-reto-fechas { font-weight: 600; }
  .hist-fecha { font-weight: 600; }
  .marca-summary-text .text-suave { font-weight: 600; }
  .separador-o { font-weight: 600; }
  /* Etiquetas de KPI (anclan una cifra): 800 → 700 */
  .us-stat-lbl { font-weight: 700; }
  .metrica .etiqueta { font-weight: 700; }
  /* Micro-rótulos meta uppercase: 800 → 600 */
  .cd-lbl { font-weight: 600; }
  .modal-ts-sub-titulo { font-weight: 600; }
  .percha-datos-lista dt { font-weight: 600; }
  .vis-col-tit { font-weight: 600; }
}

/* ============================================================================
   Inicio del promotor · pestañas de actividad + actividad POR UBICACIÓN (v118)
   Dos vistas del feed: "Reciente" (últimos registros en cualquier tienda) y
   "Cerca de ti" (lo que registró en la tienda donde está, con actualización de
   precio en un toque conservando el historial).
   ============================================================================ */
.feed-section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--e4); flex-wrap: wrap; }

.feed-tabs { display: inline-flex; background: var(--color-fondo); border: 1px solid var(--color-borde); border-radius: var(--radio-pill); padding: 4px; gap: 2px; }
/* 8 px de padding y no 10: la píldora crece en la misma pantalla donde el hero
   está adelgazando, así que se llega a los 44 con lo justo. */
.feed-tab { display: inline-flex; align-items: center; justify-content: center; gap: 6px; border: 0; background: transparent; color: var(--color-texto-suave); font: inherit; font-weight: 600; font-size: var(--txt-md, .9rem); min-height: 44px; padding: 8px 16px; border-radius: var(--radio-pill); cursor: pointer; transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out); }
.feed-tab i { font-size: 1.05em; }
/* El :hover va dentro del gate, como sus vecinos: sin él, en táctil el color se
   quedaba pegado tras el toque hasta tocar en otro sitio. */
@media (hover: hover) { .feed-tab:hover { color: var(--color-texto); } }
.feed-tab.is-active { background: var(--color-superficie); color: var(--color-acento-fuerte); box-shadow: var(--sombra-dura-sm); }
/* En móvil las pestañas ya caen en su propia línea (.feed-section-head envuelve),
   pero quedaban pegadas a la derecha. A todo el ancho reparten el blanco táctil
   en dos mitades iguales y arreglan de paso esa alineación. */
@media (max-width: 419px) {
  .feed-tabs { display: flex; width: 100%; }
  .feed-tab { flex: 1; }
}

.feed-ubi { margin-top: var(--e3); }
.feed-ubi-estado { text-align: center; padding: var(--e6) var(--e4); display: flex; flex-direction: column; align-items: center; gap: var(--e2); }
.feed-ubi-ic { width: 56px; height: 56px; border-radius: 50%; display: grid; place-items: center; background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); font-size: 1.6rem; margin-bottom: 4px; }
.feed-ubi-estado h4 { margin: 0; font-family: var(--fuente-titulo); font-size: 1.15rem; }
.feed-ubi-estado p { max-width: 44ch; margin: 0; }
.feed-ubi-spin { font-size: 2rem; color: var(--color-acento); animation: ubiSpin .9s linear infinite; }
@keyframes ubiSpin { to { transform: rotate(360deg); } }

.feed-ubi-tienda { display: flex; align-items: center; gap: var(--e3); padding: var(--e3); background: var(--color-superficie); border: 1px solid var(--color-borde); border-radius: var(--radio-md); box-shadow: var(--sombra-dura-sm); margin-bottom: var(--e3); }
.feed-ubi-tienda-ic { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 10px; display: grid; place-items: center; background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); font-size: 1.2rem; }
.feed-ubi-tienda-txt { display: flex; flex-direction: column; min-width: 0; margin-right: auto; }
.feed-ubi-tienda-txt strong { font-size: 1rem; line-height: 1.2; }
.feed-ubi-tienda-txt .text-suave { font-size: .8rem; }

.ubi-lista { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--e3); }
.ubi-card { display: flex; flex-direction: column; gap: var(--e3); padding: var(--e3); border: 1px solid var(--color-borde); border-radius: var(--radio-md); background: var(--color-superficie); box-shadow: var(--sombra-dura-sm); }
/* min-height igual al de la foto: las tarjetas sin imagen usan el marcador
   .ubi-card-foto--vacia, que ya mide 64, pero fijarlo aqui evita que un dia un
   cambio en el marcador vuelva a desalinear la fila. */
.ubi-card-top { display: flex; gap: var(--e3); align-items: center; min-height: 64px; }
.ubi-card-foto { flex: 0 0 auto; width: 64px; height: 64px; border-radius: 10px; object-fit: cover; background: var(--color-fondo); }
.ubi-card-foto--vacia { display: grid; place-items: center; color: var(--color-texto-suave); font-size: 1.5rem; }
.ubi-card-info { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* PUESTOS FIJOS DENTRO DE LA TARJETA.
   La rejilla ya igualaba el alto de las tarjetas de una misma fila, pero dentro cada
   pieza caia donde le tocaba: el nombre ocupa una, dos o tres lineas segun el
   producto —"BATIDORA DE MANO" frente a "EXPRIMIDOR DE JUGOS 700ML - INOX./NEGRO
   GIRO MULTI-DIRECCION"— y todo lo de abajo se desplazaba con el. El resultado es
   una parrilla donde el precio y el boton estan a tres alturas distintas en la misma
   fila, y el ojo no puede compararlos de un vistazo ni el dedo aprender donde tocar.

   Dos cambios: el nombre se acota a DOS lineas reservando siempre ese alto (aunque
   solo ocupe una), y el boton se empuja al fondo. Asi el bloque de arriba mide igual
   en todas y el boton esta siempre en el mismo sitio. */
.ubi-card-nombre {
  font-size: .98rem; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: calc(.98rem * 1.25 * 2);
}
.ubi-card-marca { font-size: .8rem; color: var(--color-texto-suave); display: inline-flex; align-items: center; gap: 4px; }
.ubi-card-precio { font-family: var(--fuente-titulo); font-size: 1.3rem; font-weight: 700; color: var(--color-acento-fuerte); }
.ubi-card-hace { font-size: var(--txt-sec, .78rem); }
.ubi-card-var { align-self: flex-start; margin-top: 3px; font-size: .76rem; font-weight: 700; display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: var(--radio-pill); }
/* Al fondo de la tarjeta, pase lo que pase con el contenido de arriba. Es lo que
   convierte una parrilla irregular en una parrilla con puestos: el dedo aprende
   donde esta el boton y deja de buscarlo tarjeta por tarjeta. */
/* El renglon del codigo se reserva por CONSTRUCCION, no con un min-height a ojo.
   Primero le puse min-height: 1.05rem y no cuadraba: medido, un codigo con texto
   ocupa 19,34 px —el icono manda en la caja de linea— y el hueco vacio se quedaba en
   16,80. Esos 2,5 px de diferencia arrastraban marca, precio y "hace" hacia arriba en
   los productos sin codigo, y la fila volvia a quedar descuadrada.
   Asi que el hueco lleva el MISMO icono y solo se le quita la tinta con
   visibility:hidden, que conserva la caja intacta. Dos cosas iguales miden igual;
   dos cosas parecidas, no. */
.ubi-card-codigo--vacia { visibility: hidden; }
/* Codigo, marca y "hace" son de UN dato cada uno: si no caben, se recortan con
   puntos suspensivos, no se parten en dos lineas. Medido a 900 px de ventana, donde
   la parrilla va a tres columnas de 179 px: el codigo "IN-EXPRIMIDOR JUGOS INOX -018"
   pasaba a dos lineas y empujaba precio y boton 19,4 px hacia abajo SOLO en esa
   tarjeta, con lo que la fila volvia a quedar descuadrada justo en el ancho de una
   tablet apaisada. (El min-width:0 que hace falta para poder encoger ya lo trae la
   regla base de .ubi-card-info, unas lineas mas arriba.) */
/* Los nombres de producto de este catalogo traen trozos sin espacios
   ("INOX./NEGRO", "TVLED-58NUHD-MPS"): si uno no cabe, el navegador prefiere
   desbordar antes que partirlo. Medido, a 920 px la tarjeta del exprimidor se salia
   14 px de su columna. Con anywhere se parte donde haga falta. */
.ubi-card-nombre { overflow-wrap: anywhere; }
.ubi-card-codigo,
.ubi-card-marca,
.ubi-card-hace {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Sin el max-width el recorte NO ocurre: con nowrap el ancho minimo de la caja
     pasa a ser el maximo, asi que se planta en sus 212 px y se sale de la tarjeta en
     lugar de encogerse. Medido, el codigo del exprimidor sobresalia 25 px a 920 px
     de ventana. Atado al ancho disponible, aparecen los puntos. */
  max-width: 100%;
}
/* Y el codigo se pinta como BLOQUE dentro de la tarjeta, no como inline-flex.
   text-overflow no recorta con puntos dentro de un contenedor flex: alli el texto es
   un item anonimo y se corta a hachazo, que parece un error de pintado.
   Va con doble clase a proposito: la regla general de .ubi-card-codigo esta mas
   abajo en esta misma hoja y, a igual especificidad, ganaria ella. */
.ubi-card .ubi-card-codigo { display: block; max-width: 100%; }
/* El hueco lleva icono Y un espacio duro. Con solo el icono medi 0 px: un <i> vacio
   no genera caja de linea —CSS trata como de altura cero las lineas sin texto ni
   margenes— asi que el renglon desaparecia igual que antes. El espacio duro le da
   texto real, y entonces mide lo mismo que un codigo de verdad. */
.ubi-card-btn { margin-top: auto; }
.ubi-card-var.es-sube { color: #E5484D; background: rgba(229, 72, 77, .14); }
.ubi-card-var.es-baja { color: #2E9E5B; background: rgba(46, 158, 91, .16); }
.ubi-card-btn { width: 100%; justify-content: center; }

/* ============================================================================
   Admin Formularios · quick-view de respuesta (modal) + lista escaneable (v119)
   ============================================================================ */
/* Barra de navegación del modal (anterior · N/N · siguiente · cerrar). */
.ff-resp-nav { display: inline-flex; align-items: center; gap: 4px; }
.ff-resp-nav [data-resp-pos] { min-width: 52px; text-align: center; font-size: var(--txt-sm, .82rem); font-variant-numeric: tabular-nums; }
.ff-resp-nav .icon-btn[disabled] { opacity: .35; cursor: default; pointer-events: none; }
/* El cuerpo del quick-view tiene su propio scroll: el encabezado con prev/next
   queda fijo mientras se recorre un detalle largo. */
.ff-resp-cuerpo { max-height: min(66vh, 560px); overflow: auto; overscroll-behavior: contain; }

/* Lista de respuestas: cabecera pegajosa al hacer scroll dentro de la tabla. */
.tabla-scroll--sticky { max-height: calc(100vh - 240px); overflow-y: auto; }
.tabla-scroll--sticky table.tabla-admin thead th { position: sticky; top: 0; z-index: 3; background: var(--color-superficie-alt); }
/* Filas de alerta resaltadas con barra roja: se ubican sin activar "Solo alertas".
   La especificidad table.tabla-admin tbody tr.ff-row--alerta gana al :nth-child(even). */
table.tabla-admin tbody tr.ff-row--alerta { box-shadow: inset 3px 0 0 var(--color-alerta-alto); background: var(--color-alerta-fondo); }
@media (hover: hover) { table.tabla-admin tbody tr.ff-row--alerta:hover { background: var(--color-superficie-alt); } }
/* Config de formularios: fila de un tipo desactivado. Antes se atenuaba la fila
   ENTERA con opacity: .55, y opacity crea una capa: texto y fondo se componen
   por separado contra la página, así que caían los CINCO elementos de la fila.
   Medido: el nombre en 3,51:1, el badge "Inactivo" en 2,16 y —lo peor— el texto
   del botón "Activar" en 2,28:1 en claro y 2,84:1 en oscuro; el control que
   devuelve el formulario a la vida era el peor iluminado de la tabla. El estado
   ya lo dicen el badge y el propio botón, así que la opacidad no aportaba
   información: se atenúa sólo el nombre, y por COLOR, con el mismo token que ya
   usa la categoría dos etiquetas más abajo (6,41:1 en claro, 8,36:1 en oscuro). */
table.tabla-admin tr.fila-off .ff-def-txt strong {
  color: var(--color-texto-suave);
  font-weight: 600;
}

/* ============================================================================
   Registro de precio · CATEGORÍA en el paso de modelo (v120)
   MARCA → CATEGORÍA → MODELO: chips para acotar por categoría + categoría
   visible en cada resultado de modelo.
   ============================================================================ */
.modelo-cat-filtros-wrap { margin: 2px 0 12px; }
.modelo-cat-filtros-lbl { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; font-weight: 700; color: var(--color-texto-suave); margin-bottom: 7px; }
.modelo-cat-filtros { display: flex; flex-wrap: wrap; gap: 10px; }
/* Equivocarse de chip cuesta caro (con la lista truncada dispara la carga del
   catálogo entero), así que va al objetivo táctil de la casa. */
.modelo-cat-filtro {
  display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: .85rem; font-weight: 600;
  min-height: 44px; padding: 10px 14px; border-radius: var(--radio-pill); border: 1.5px solid var(--color-borde);
  background: var(--color-superficie); color: var(--color-texto); cursor: pointer;
  transition: background var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out);
}
.modelo-cat-filtro i { font-size: 1.05em; }
@media (hover: hover) { .modelo-cat-filtro:hover { background: var(--color-superficie-alt); } }
/* Tinta navy y no blanca: el blanco sobre el naranja de marca da 3,12:1 en LOS
   DOS temas (los dos valores son literales). Es el par de .btn-energia. */
.modelo-cat-filtro.is-active { background: var(--color-energia); border-color: var(--color-energia); color: #22223B; }

/* Categoría visible en cada tarjeta de modelo (identificar el producto de un
   vistazo). Selector cualificado con .almacen-card .info para ganarle en
   especificidad a la regla que pinta en gris los <span> de la tarjeta. */
.almacen-card .info .modelo-cat-chip {
  display: inline-flex; align-items: center; gap: 4px; align-self: flex-start; margin-top: 3px;
  font-size: var(--txt-xs, .72rem); font-weight: 700; padding: 1px 8px; border-radius: var(--radio-pill);
  color: var(--color-acento-fuerte); background: var(--color-alerta-fondo);
}

/* ============================================================================
   Gráficos de barras horizontales largos: ventana de N filas con scroll (v125)
   Antes, una tarjeta con 20 marcas crecía hasta 1.100 px mientras su vecina medía
   250 px, y la comparación —el motivo de ponerlas lado a lado— se perdía. Ahora
   toda tarjeta mide lo mismo con 8 barras que con 40: se ven 7 y el resto se
   alcanza desplazándose DENTRO del gráfico, con el eje X fijo debajo.
   Va al final del archivo a propósito: .percha-chart-wrap canvas tiene la misma
   especificidad, así que manda el orden de aparición.
   ============================================================================ */
.chart-scroll {
  overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable;
  /* Aviso de que hay más barras abajo. Se usa mask-image y NO un degradado de
     color: un linear-gradient hacia transparent se desvanece hacia NEGRO
     transparente y en modo oscuro deja un manchón gris. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 22px), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 22px), transparent);
  /* SIN overscroll-behavior: contain a propósito: con `contain`, quien deslice el
     dedo sobre el gráfico no podría seguir bajando la página. */
}
.chart-scroll.es-fin { -webkit-mask-image: none; mask-image: none; }
.chart-scroll:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: 2px; }
.chart-scroll-lienzo { position: relative; width: 100%; }
.chart-scroll-lienzo canvas { width: 100% !important; height: 100% !important; }
.chart-scroll-eje { position: relative; height: 20px; margin-top: 2px; }
.chart-scroll-eje::before {
  content: ''; position: absolute; left: var(--eje-x0, 0); right: 0; top: 0;
  border-top: 2px solid var(--color-borde);
}
.chart-scroll-tick {
  position: absolute; top: 4px; transform: translateX(-50%);
  font-size: var(--text-2xs); font-weight: 700; line-height: 1;
  color: var(--color-texto-suave); white-space: nowrap; pointer-events: none;
}
.chart-scroll-nota { margin: 8px 0 0; font-size: var(--text-2xs); color: var(--color-texto-suave); }
@media (max-width: 899px) {
  .chart-scroll { overflow: visible; -webkit-mask-image: none; mask-image: none; }
}

/* Señal de los filtros que se auto-envían (v126). Ver el bloque correspondiente
   en assets/js/app.js: la barra avanza sola mientras el servidor responde y el
   formulario se atenúa, para que el analista sepa que su cambio SÍ se registró. */
.nav-progreso {
  position: fixed; top: 0; left: 0; z-index: 9999;
  height: 3px; width: 0; opacity: 0; pointer-events: none;
  background: linear-gradient(90deg, var(--color-acento), var(--color-energia));
}
.nav-progreso.activa {
  opacity: 1; width: 92%;
  /* Curva larga y desacelerada: nunca llega al 100% porque no sabemos cuánto
     falta; al navegar, la página nueva se lleva la barra por delante. */
  transition: width 8s cubic-bezier(.1, .75, .2, 1), opacity .15s linear;
}
/* Se atenúa, pero NO se bloquea con pointer-events: si la navegación fallara,
   el formulario tiene que seguir siendo usable. */
.form-enviando { opacity: .62; transition: opacity .15s linear; }
@media (prefers-reduced-motion: reduce) {
  .nav-progreso.activa { transition: opacity .15s linear; width: 92%; }
}

/* Código de modelo en las tarjetas de actividad del promotor (v127). Sin él, un
   producto cuyo nombre comercial repite la marca ("HACEB" / Haceb) no se podía
   identificar: la tarjeta no decía QUÉ modelo era. */
.ubi-card-codigo, .feed-codigo {
  display: inline-flex; align-items: center; gap: 4px; align-self: flex-start;
  font-size: var(--txt-sec, .78rem); font-weight: 700; letter-spacing: .01em;
  color: var(--color-texto-suave);
}
.almacen-card .info .feed-codigo { color: var(--color-texto-suave); }

/* Dos utilidades que el markup del panel ya usaba pero que nunca se declararon:
   .d-block (para que un <small> baje de línea) y .flex-entre (etiqueta a un lado,
   cifra al otro). Se declaran aquí para que dejen de ser clases fantasma. */
.d-block { display: block; }
.flex-entre { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }

/* ============================================================================
   Reporte de la beta de gamificación (admin/gamificacion.php) — v129
   ============================================================================ */
.gamif-rep-personajes {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--e3); margin-top: var(--e3);
}
.gamif-rep-personaje {
  display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center;
  padding: var(--e3) var(--e2); border: 1.5px solid var(--color-borde);
  border-radius: var(--radio-md); background: var(--color-fondo);
}
.gamif-rep-personaje strong { font-size: .95rem; line-height: 1.2; }
.gamif-rep-personaje .text-suave { font-size: var(--txt-sec, .78rem); }
.gamif-rep-personaje .sd-barra { width: 100%; margin-top: 6px; }
.gamif-rep-img { width: 78px; height: 78px; object-fit: contain; }

/* Avatar dentro de la tabla: mascota + accesorios equipados, en pequeño. */
.gamif-rep-avatar { display: inline-flex; align-items: center; gap: var(--e2); }
.gamif-masco--mini { width: 46px; height: 46px; flex: 0 0 auto; }
.gamif-masco--mini .gamif-masco-stage { width: 100%; height: 100%; }
.gamif-masco--mini .gamif-masco-img { width: 100%; height: 100%; object-fit: contain; }

/* Listas de ranking (cosméticos e insignias). */
.gamif-rep-lista { list-style: none; margin: var(--e3) 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.gamif-rep-lista li {
  display: flex; align-items: center; gap: var(--e2);
  padding: 7px 10px; border: 1px solid var(--color-borde);
  border-radius: var(--radio-sm); background: var(--color-superficie);
}
.gamif-rep-lista-txt { flex: 1 1 auto; min-width: 0; font-size: var(--txt-md, .9rem); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gamif-rep-lista li strong { font-family: var(--fuente-titulo); font-size: 1.05rem; }
.gamif-rep-lista--off li { opacity: .72; }
.gamif-rep-aviso {
  margin: 0; padding: 8px 10px; border-radius: var(--radio-sm);
  background: var(--color-alerta-fondo); color: var(--color-acento-fuerte);
  font-size: .84rem; font-weight: 600;
}

/* ============================================================================
   Constructor de reportes: paleta con scroll propio y chips compactos (v132)
   Al sumar dimensiones y medidas de productividad, la lista de la izquierda creció
   tanto que empujaba las zonas de soltar (Filas/Columnas/Valores/Filtros) fuera de
   la pantalla: había que hacer scroll de página y al arrastrar se perdía el destino.
   Ahora la paleta se desplaza DENTRO de sí misma y el constructor entero cabe.
   ============================================================================ */
.rep-paleta {
  max-height: min(56vh, 480px);
  overflow-y: auto;
  scrollbar-gutter: stable;
  /* El scroll de la paleta no arrastra a la página detrás cuando llega al final. */
  overscroll-behavior: contain;
}
/* Chips de la PALETA más compactos: entran 2-3 por fila. Se dejan ajustar en dos
   líneas para que una etiqueta larga no ocupe una fila entera ella sola.
   Los chips YA COLOCADOS en las zonas NO se tocan: llevan su botón de quitar y su
   menú de agregación, y deben seguir siendo cómodos de pulsar. */
/* REJILLA, no flujo libre: con flex-wrap una etiqueta larga ("Hora media de
   inicio") se quedaba sola en su fila y bajaban a ~1,2 chips por fila. Con
   columnas de ancho fijo entran 2 (o 3 si el panel es más ancho) y las etiquetas
   largas simplemente ajustan en dos líneas. */
.rep-campos {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px;
  align-items: stretch;
}
.rep-campos .rep-chip {
  display: flex; justify-content: flex-start;
  padding: 5px 8px; min-height: 30px; font-size: .74rem; gap: 5px;
  white-space: normal; line-height: 1.2; text-align: left; min-width: 0;
}
.rep-campos .rep-chip i { flex: 0 0 auto; }
@media (min-width: 861px) {
  /* Un poco más de ancho para que quepan de verdad 2-3 chips por fila. */
  .rep-shell { grid-template-columns: 240px 1fr; }
}
@media (max-width: 860px) {
  .rep-paleta { max-height: 42vh; }
}

/* Diálogo de exportación del constructor de reportes (v135): elegir entre la
   vista actual y la completa antes de descargar. */
.rep-export-ops { display: grid; gap: 10px; margin-top: 14px; }
.rep-export-op {
  display: flex; align-items: flex-start; gap: 12px; width: 100%; text-align: left;
  padding: 14px; border: 2px solid var(--color-borde); border-radius: var(--radio-md);
  background: var(--color-superficie); color: var(--color-texto); cursor: pointer;
  font: inherit; box-shadow: var(--sombra-dura-sm);
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.rep-export-op:hover { transform: translate(-2px, -2px); box-shadow: var(--sombra-dura); }
.rep-export-op > i { flex: 0 0 auto; font-size: 1.5rem; color: var(--color-acento); margin-top: 2px; }
.rep-export-op strong { display: block; font-family: var(--fuente-titulo); font-size: 1rem; }
.rep-export-op small { display: block; margin-top: 2px; font-size: var(--txt-sm, .82rem); line-height: 1.35; color: var(--color-texto-suave); }

/* Variante alta del contenedor de gráfico. Antes era un style="height:240px"
   suelto en el marcado del resumen; como clase queda junto al resto del sistema
   y se puede reusar. */
.chart-mini-wrap--alto { height: 240px; }

/* Bloque desplegable de análisis del resumen (v137).
   OJO con .insight-grid[hidden]: la clase declara display:grid, que GANA al
   atributo hidden, así que sin esta regla el bloque "oculto" se seguiría viendo.
   Es la misma trampa que ya apareció con las listas de fusión de almacenes. */
.insight-grid[hidden] { display: none; }
.mas-analisis-barra { display: flex; justify-content: center; margin: 2px 0 18px; }

/* ===========================================================================
   Rotación de producto (doble check) — app/rotacion-pantalla.php
   Va al final del archivo a propósito: así estas reglas ganan por orden a las
   genéricas sin necesidad de subir la especificidad con selectores anidados.
   =========================================================================== */
.rot-tit {
  display: flex; align-items: center; gap: 8px;
  font-size: 1.05rem; margin: 0 0 4px;
}
.rot-sub { margin: 0 0 14px; font-size: var(--txt-md, .9rem); }

/* Contador junto al título: da el volumen de un vistazo. */
.rot-cont {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 26px; padding: 0 8px; margin-left: 4px;
  border-radius: 999px; background: var(--color-borde-suave);
  font-size: .85rem; font-weight: 700;
}
.rot-cont--alerta { background: var(--color-alerta-fondo); color: var(--color-alerta-alto); }

.rot-form { display: block; }
.rot-lbl {
  display: block; margin: 14px 0 6px;
  font-weight: 600; font-size: .92rem;
}
.rot-form input[type="text"], .rot-form input[type="number"] { width: 100%; }

/* El campo de código manda; el botón de escanear lo acompaña sin robarle sitio. */
.rot-codigo-row { display: flex; gap: 8px; align-items: stretch; }
.rot-codigo-row input { flex: 1 1 auto; min-width: 0; }
.rot-codigo-row .btn { flex: 0 0 auto; white-space: nowrap; }

.rot-cta { width: 100%; justify-content: center; }

/* Lista de productos vigilados */
.rot-lista { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.rot-item {
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio);
  padding: 12px; background: var(--color-superficie);
}
.rot-item--alerta { border-color: var(--color-alerta-alto); background: var(--color-alerta-fondo); }
.rot-item-info { display: grid; gap: 2px; }
.rot-item-info strong { line-height: 1.3; }
.rot-item-info .text-suave { font-size: .85rem; }
.rot-nota { font-style: italic; }

/* Aviso de espera: informa sin parecer un error, porque no lo es. */
.rot-espera {
  display: flex; align-items: center; gap: 6px;
  margin: 8px 0 0; padding: 7px 10px;
  border-radius: var(--radio); background: var(--color-borde-suave);
  font-size: .85rem;
}

/* Botones en fila; en pantallas estrechas se apilan a ancho completo para que
   el dedo no falle -- esto se usa de pie y con una sola mano. */
.rot-item-acciones { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.rot-item-acciones form { flex: 1 1 auto; }
.rot-item-acciones .btn { width: 100%; justify-content: center; min-height: 44px; }
.rot-btn-sigue { border-color: var(--color-alerta-alto); color: var(--color-alerta-alto); }

@media (max-width: 380px) {
  .rot-item-acciones { flex-direction: column; }
  .rot-codigo-row { flex-direction: column; }
  .rot-codigo-row .btn { width: 100%; justify-content: center; }
}

/* Sin esta regla el display:flex de .form-tile le gana al atributo [hidden] y
   los resultados filtrados del buscador seguirían viéndose. Misma trampa que
   .hoja-ov[hidden]; se declara aquí para que valga en toda la app. */
.form-tile[hidden] { display: none; }

/* Promotor v234: atajos de ruta, check-in y captura. */
.proxima-tienda {
  display: grid; grid-template-columns: 46px minmax(0,1fr) auto; gap: 12px; align-items: center;
  margin: 12px 0 18px; padding: 15px; border: 1px solid var(--color-borde);
  border-radius: calc(var(--radio, 14px) + 2px); background: var(--color-superficie);
  box-shadow: var(--sombra-suave, 0 8px 24px rgba(30,30,50,.08));
}
.proxima-tienda-ic { width: 46px; height: 46px; display: grid; place-items: center; border-radius: 14px; background: color-mix(in srgb, var(--color-energia) 18%, transparent); color: var(--color-energia-txt); font-size: 1.25rem; }
.proxima-tienda-txt { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.proxima-tienda-txt > strong { font-size: 1.02rem; line-height: 1.2; }
.proxima-tienda-txt small { color: var(--color-texto-suave); overflow-wrap: anywhere; }
.proxima-tienda-dist { margin-top: 3px; color: var(--color-energia-txt); font-size: .76rem; font-weight: 800; }
.proxima-tienda-acciones { display: flex; gap: 7px; flex-wrap: wrap; justify-content: flex-end; }
.tienda-hoy-continuar { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 4px; }

.ci-cerca > span { display: flex; flex: 1; flex-direction: column; gap: 3px; }
.ci-cerca small { color: var(--color-texto-suave); font-weight: 650; }
.ci-cerca small.is-low { color: var(--color-alerta-alto); }
.ci-gps-retry { width: 34px; height: 34px; flex: 0 0 auto; border: 1px solid var(--color-borde); border-radius: 10px; background: var(--color-superficie); color: var(--color-texto); cursor: pointer; }
.ci-atajos { margin: 0 0 12px; }
.ci-atajos-lista { display: flex; gap: 7px; overflow-x: auto; padding: 6px 1px 3px; scrollbar-width: thin; }
.ci-atajo-chip { display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; max-width: 210px; padding: 8px 10px; border: 1px solid var(--color-borde); border-radius: 999px; background: var(--color-superficie); color: var(--color-texto); font: inherit; font-size: .76rem; font-weight: 750; cursor: pointer; }
.ci-atajo-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ci-atajo-chip.is-fav i { color: #a66b00; }
.ci-picker .ci-store-row { padding: 0; display: grid; grid-template-columns: minmax(0,1fr) 42px; gap: 0; }
.ci-store-main { grid-column: 1; min-width: 0; display: grid; grid-template-columns: 42px minmax(0,1fr) 38px; align-items: center; gap: 11px; min-height: 92px; padding: 13px 6px 13px 12px; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.ci-favorito { grid-column: 2; width: 38px; height: 38px; align-self: center; border: 0; border-radius: 50%; background: transparent; color: var(--color-texto-suave); font-size: 1.15rem; cursor: pointer; }
.ci-favorito.is-active { color: #a66b00; background: #fff3ce; }
.ci-picker .ci-store-row.is-cercano { border-color: var(--color-energia); background: color-mix(in srgb, var(--color-energia) 6%, var(--color-superficie)); box-shadow: inset 4px 0 0 var(--color-energia); }
.ci-picker .ci-store-row .form-tile-ic { grid-column: 1; }
.ci-picker .ci-store-row .form-tile-txt { grid-column: 2; }
.ci-picker .ci-store-row .ci-elegir { grid-column: 3; }

.cam-contexto { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; padding: 9px 11px; border-radius: 11px; background: var(--color-superficie-alt); font-size: var(--txt-sm, .82rem); }
.cam-contexto i { color: var(--color-energia-txt); }
.ruta-card--salida-pendiente { box-shadow: 0 0 0 2px var(--color-alerta-alto, #b42318); }
.ruta-salida-aviso { flex: 1 1 100%; margin: 0 0 5px; padding: 9px 10px; border-radius: 10px; background: #fde2e1; color: var(--color-alerta-alto, #b42318); font-size: .8rem; font-weight: 750; }
.visita-productos-contador { position: sticky; top: calc(var(--header-alto, 64px) + 6px); z-index: 8; display: flex; align-items: center; gap: 8px; width: max-content; max-width: 100%; margin: 8px 0 12px; padding: 8px 11px; border: 1px solid var(--color-borde); border-radius: 999px; background: var(--color-superficie); box-shadow: 0 5px 16px rgba(30,30,50,.1); font-size: .8rem; }
.modelo-fav-toggle { margin-left: auto; width: 38px; height: 38px; display: grid; place-items: center; flex: 0 0 auto; border-radius: 50%; color: var(--color-texto-suave); }
.modelo-fav-toggle.is-active { color: #a66b00; background: #fff3ce; }
.referencia-copiar { display: inline-flex; align-items: center; gap: 5px; margin-top: 9px; padding: 7px 9px; border: 1px solid currentColor; border-radius: 9px; background: transparent; color: inherit; font: inherit; font-size: .76rem; font-weight: 800; cursor: pointer; }
.promo-descuento { grid-column: 1 / -1; display: block; padding: 9px 11px; border-radius: 10px; background: var(--color-superficie-alt); color: var(--color-texto-suave); font-size: .8rem; font-weight: 750; }
.promo-descuento.is-error { background: #fde2e1; color: var(--color-alerta-alto, #b42318); }

@media (max-width: 600px) {
  .proxima-tienda { grid-template-columns: 42px minmax(0,1fr); }
  .proxima-tienda-acciones { grid-column: 1 / -1; justify-content: stretch; }
  .proxima-tienda-acciones .btn { flex: 1; }
  .ci-store-main { grid-template-columns: 38px minmax(0,1fr) 34px; gap: 8px; padding-left: 9px; }
}

/* ===========================================================================
   Teclado móvil abierto — trabaja con el bloque de app.js que mide
   visualViewport y publica --teclado-alto y --vv-alto.

   El problema que resuelve: al escribir, el promotor no veía lo que tecleaba y
   tenía que bajar el teclado para leerlo.
   =========================================================================== */

/* 1. La barra de navegación es fixed y se comía la franja donde el navegador
      deja el campo enfocado. Mientras se escribe no sirve para nada, así que
      se quita: no hay reflujo porque el relleno inferior del contenido no
      cambia, la barra sólo flota encima. */
body.teclado-abierto .bottom-nav { display: none; }

/* 2. Las hojas se anclan abajo, justo donde aparece el teclado. Se sube todo el
      overlay por encima de él y se le recorta el alto a la vista realmente
      visible. NO se puede usar dvh: por especificación esas unidades siguen la
      interfaz del navegador, no el teclado virtual. */
body.teclado-abierto .hoja-ov { bottom: var(--teclado-alto, 0px); }
body.teclado-abierto .hoja { max-height: calc(var(--vv-alto, 92vh) - 24px); }

/* 3. Aire alrededor del campo al desplazarlo a la vista, para que no quede
      pegado al borde del teclado ni bajo la barra superior. */
input, textarea, select, [contenteditable="true"] {
  scroll-margin-bottom: 96px;
  scroll-margin-top: 72px;
}

/* 4. Espacio desplazable extra mientras se escribe. Sin esto, un campo situado
      al final de una pantalla corta NO se puede subir por encima del teclado
      aunque el código lo pida: sencillamente no hay a dónde desplazar. El
      relleno desaparece al cerrar el teclado, así que no altera el diseño. */
body.teclado-abierto { padding-bottom: var(--teclado-alto, 0px); }

/* 5. El campo enfocado, inconfundible.
      Los promotores reportaban que "se vuelve difícil identificar cuál es el
      campo que están llenando": hasta ahora enfocar sólo cambiaba el color del
      borde, un cambio de 2px que en un teléfono a plena luz no se distingue.

      Se usa box-shadow y NO border/outline con grosor variable: box-shadow se
      pinta fuera del flujo y NO refluye nada. Cualquier cambio de grosor de
      borde movería el contenido de alrededor justo al enfocar, que es
      precisamente el efecto que se quiere evitar.

      Va sobre :focus y no sólo :focus-visible: en móvil interesa que se marque
      siempre que el campo esté activo, sin depender de la heurística del
      navegador. */
input[type=text]:focus, input[type=email]:focus, input[type=password]:focus,
input[type=tel]:focus, input[type=number]:focus, input[type=search]:focus,
input[type=date]:focus, input[type=month]:focus,
select:focus, textarea:focus {
  border-color: var(--color-energia-txt);
  box-shadow: 0 0 0 4px rgba(255, 90, 31, .28);
}

/* ===========================================================================
   Estandarizar: contexto de cada valor (admin/estandarizar.php)
   Un "22" suelto no dice si son pulgadas o litros. Estas clases muestran la
   categoría de la que sale el dato y marcan los valores que se usan en varias
   categorías, que son los peligrosos de unificar.
   =========================================================================== */

/* Tinte ámbar sobre .alerta-info para el aviso de ambigüedad: no es un error
   (el dato no está mal), es algo que hay que mirar antes de tocar. */
.est-alerta-ojo {
  border-color: var(--color-alerta-bajo, #C9A227);
  background: var(--color-alerta-fondo);
  display: flex; align-items: flex-start; gap: 9px;
}
.est-alerta-ojo > span { font-weight: 500; }

.est-cats { display: flex; flex-wrap: wrap; gap: 5px; }
.est-cat {
  display: inline-block; padding: 2px 8px;
  border: 1px solid var(--color-borde-suave); border-radius: 999px;
  background: var(--color-superficie-alt, #F2F1EE);
  font-size: .76rem; font-weight: 600; white-space: nowrap;
}

/* El aviso por fila: discreto pero imposible de no ver al recorrer la tabla. */
.est-aviso {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 5px;
  font-size: .76rem; font-weight: 700; color: var(--color-alerta-alto);
  cursor: help;
}

/* La fila entera se tiñe: así el problema salta al hojear 182 valores en vez de
   obligar a leer chip por chip. Solo el borde izquierdo para no competir con el
   campo de texto editable. */
.est-fila--ambigua > td:first-child { box-shadow: inset 3px 0 0 var(--color-alerta-alto); }

/* Modal de origen */
.est-org-grupo { margin-top: 14px; }
.est-org-cat {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 6px; font-size: .92rem; text-transform: uppercase; letter-spacing: .03em;
  color: var(--color-texto-suave);
}
.est-org-lista { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.est-org-lista a {
  display: block; padding: 9px 11px;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio);
  text-decoration: none; color: inherit; line-height: 1.35;
}
@media (hover: hover) { .est-org-lista a:hover { border-color: var(--color-energia); } }

/* --- Estandarizar: unidades por categoría y sugerencia por fila ------------- */

/* Chip de categoría vacía: se distingue de una categoría real sin gritar. */
.est-cat--nula { border-style: dashed; color: var(--color-texto-suave); }

/* "mide en L": la unidad configurada de esa categoría, junto a su nombre. */
.est-unidad {
  display: inline-block; margin-left: 6px;
  font-size: .74rem; color: var(--color-texto-suave); font-style: italic;
}

/* Sugerencia: se pulsa y rellena el campo. Es un atajo, no un automatismo --
   con 182 filas, cambiar el catálogo entero sin que nadie lo pida sería pasarse. */
.est-sugerido {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 6px;
  padding: 4px 10px; border: 1px dashed var(--color-energia); border-radius: 999px;
  background: transparent; color: var(--color-texto); cursor: pointer;
  font: inherit; font-size: var(--txt-sec, .78rem); font-weight: 700;
}
@media (hover: hover) { .est-sugerido:hover { background: var(--color-alerta-fondo); } }
.est-campo--tocado { border-color: var(--color-energia); }

/* Panel de unidades */
.est-unidades { margin-bottom: 14px; }
.est-unidades-grid {
  display: grid; gap: 10px; margin-top: 12px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.est-unidad-campo label {
  display: block; margin-bottom: 4px;
  font-size: var(--txt-sec, .78rem); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--color-texto-suave);
}
.est-unidad-campo input { width: 100%; }

/* ===========================================================================
   Registro de venta: fila de modelo (app/ventas.php)
   Cada modelo pasa de tres controles apretados en una línea a una tarjeta con
   los campos a lo ancho. Se elige la categoría, luego el modelo, y cantidad y
   quitar quedan en un pie. En un teléfono los tres en línea eran de acertar
   con el dedo por milímetros.
   =========================================================================== */
.percha-fila.venta-fila {
  /* Anula el grid de 3 columnas de .percha-fila: aquí los campos se apilan. */
  display: block;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio);
  padding: 12px;
  margin-bottom: 12px;
  background: var(--color-superficie);
}
.venta-fila-lbl {
  display: block; margin: 0 0 5px;
  font-size: .8rem; font-weight: 700;
}
.venta-fila-lbl + select,
.venta-fila-lbl + input { margin-bottom: 10px; }
.venta-fila .venta-cat,
.venta-fila .venta-modelo-buscar { width: 100%; }
.venta-fila .venta-modelo-buscador { margin-bottom: 2px; }

/* Pie: la cantidad crece y el botón de quitar se queda en su tamaño, pero con
   área táctil suficiente. */
.venta-fila-pie {
  display: flex; align-items: flex-end; gap: 10px; margin-top: 8px;
}
.venta-fila-cant { flex: 0 1 130px; }
.venta-fila-cant input { width: 100%; }
.venta-fila-pie .percha-fila-quitar { flex: 0 0 auto; min-height: 44px; min-width: 44px; }

/* --- Modal de descarga de informes (admin/formularios.php) ----------------- */
.inf-lista { display: grid; gap: 8px; margin-top: 4px; }

/* Cada formulario es un botón de ancho completo: en el modal se elige con el
   dedo o con el ratón, y una lista de enlaces pequeños se falla más. */
.inf-opcion {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; min-height: 48px; padding: 10px 14px; text-align: left;
  border: 1px solid var(--color-borde); border-radius: var(--radio);
  background: var(--color-superficie); color: inherit;
  font: inherit; font-weight: 600; cursor: pointer;
}
@media (hover: hover) { .inf-opcion:not(:disabled):hover { border-color: var(--color-energia); } }

/* Los formularios sin respuestas se ven apagados y no se pueden pulsar: entrar
   para descargar un informe vacío es un viaje en balde. */
.inf-opcion:disabled { opacity: .5; cursor: not-allowed; }

.inf-formatos { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; }
.inf-formatos .btn { flex: 1 1 190px; justify-content: center; }

/* --- Filtros en acordeón + rango de meses del informe ---------------------- */
.ff-acordeon { margin-bottom: 14px; }
.ff-acordeon .ff-filtros { margin-top: 12px; }
.ff-acordeon-pista { font-size: var(--txt-sm, .82rem); font-weight: 400; }

.inf-meses {
  display: grid; gap: 10px; margin: 14px 0 4px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.inf-meses label {
  display: block; margin-bottom: 4px;
  font-size: var(--txt-sec, .78rem); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--color-texto-suave);
}
.inf-meses input { width: 100%; }

/* ===========================================================================
   Itinerario: tope de visitas visibles por día
   Con 20 visitas en un día la columna crecía sin freno y descuadraba toda la
   fila del calendario -- los días vecinos quedaban en blanco y había que
   desplazar la página entera para ver el resto del mes.
   Ahora la lista tiene tope y scroll propio: la rejilla mantiene su forma.
   =========================================================================== */
.itin-lista {
  --itin-visibles: 5;        /* mes: 5 visitas a la vista */
  --itin-chip-alto: 2.45rem; /* alto real de un chip incluida su separación */
  max-height: calc(var(--itin-visibles) * var(--itin-chip-alto));
  overflow-y: auto;
  overscroll-behavior: contain;   /* al llegar al final no arrastra la página */
}
/* La vista semana tiene columnas mucho más altas: caben 15 sin descuadrar. */
.itin-cal--semana .itin-lista { --itin-visibles: 15; }

/* Barra de scroll discreta: con una por columna, las del sistema ensucian. */
.itin-lista::-webkit-scrollbar { width: 6px; }
.itin-lista::-webkit-scrollbar-thumb {
  background: var(--color-borde); border-radius: 999px;
}
.itin-lista { scrollbar-width: thin; }

/* Foto del modal de visita: ocupa todo el ancho y una altura generosa,
   recortando por el centro. Antes se topaba en 220 px y en una foto de fachada
   apaisada quedaba una franja diminuta. */
#itinChipModal .modal-ts-foto {
  width: 100%;
  height: 300px;
  max-height: 42vh;
  object-fit: cover;
  object-position: center;
}

/* Mientras se arrastra una visita, las listas sueltan el recorte: el overflow
   que las mantiene compactas hacía desaparecer el chip al sacarlo de su columna
   y era imposible soltarlo en otro día. */
body.itin-arrastrando .itin-lista { overflow: visible; max-height: none; }

/* Resumen de lo que mueve una fusión de marcas (admin/marcas-fusionar.php). */
.mf-lista { list-style: none; margin: 12px 0; padding: 0; display: grid; gap: 6px; }
.mf-lista li {
  padding: 8px 12px; border-radius: var(--radio);
  background: var(--color-superficie-alt, #F2F1EE);
}


/* Vista disponible pero con reparo (pastel de un promedio): se puede pulsar,
   pero se marca para que no pase por una lectura sin matices. */
.rep-vista button.rep-vista-aviso { border-style: dashed; }

/* Detalle de la porción "Otras" del pastel (admin/reporte.php).
   Tres columnas: nombre, barra de peso relativo y valor. La barra es lo que
   hace comparable de un vistazo lo que una lista de números no. */
.rep-otras-lista { list-style: none; margin: 10px 0 0; padding: 0; max-height: 50vh; overflow-y: auto; }
.rep-otras-fila {
  display: grid; grid-template-columns: minmax(90px, 1fr) minmax(60px, 1.1fr) auto;
  align-items: center; gap: 12px; padding: 9px 2px;
  border-bottom: 1px solid var(--color-borde-suave);
}
.rep-otras-fila:last-child { border-bottom: none; }
.rep-otras-etq { font-weight: 600; overflow-wrap: anywhere; }
.rep-otras-barra {
  height: 8px; border-radius: 999px; background: var(--color-borde-suave); overflow: hidden;
}
.rep-otras-barra i { display: block; height: 100%; background: var(--color-energia); border-radius: 999px; }
.rep-otras-val { white-space: nowrap; font-weight: 700; text-align: right; }
.rep-otras-val small { color: var(--color-texto-suave); font-weight: 600; margin-left: 4px; }
@media (max-width: 460px) {
  /* En móvil la barra sobra antes que el dato: se oculta y quedan dos columnas. */
  .rep-otras-fila { grid-template-columns: 1fr auto; }
  .rep-otras-barra { display: none; }
}

/* --- Cabecera del gráfico del constructor de reportes ---------------------- */
.rep-chart-cab {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; margin-bottom: 6px;
}
/* El título ocupa el hueco de la derecha, encima de la leyenda: es donde la
   vista ya está mirando cuando lee las categorías. */
.rep-chart-titulo {
  margin: 0; font-weight: 700; font-size: .98rem; line-height: 1.3;
  color: var(--color-texto);
}
.rep-chart-titulo:empty { display: none; }
.rep-ampliar { flex: 0 0 auto; }

/* Ampliado: el gráfico ocupa la pantalla. La leyenda cabe entera y las
   porciones pequeñas se distinguen, que es justo para lo que sirve. */
body.rep-ampliado { overflow: hidden; }
.rep-chart-wrap--grande {
  position: fixed; inset: 0; z-index: 70;
  /* Alto EXPLÍCITO: con inset:0 no basta si algo declara height, y dvh además
     descuenta la barra del navegador en móvil. */
  width: 100%; height: 100vh; height: 100dvh; max-height: none;
  background: var(--color-superficie);
  padding: 18px; display: flex; flex-direction: column;
}
/* El lienzo suelta su alto fijo y ocupa todo lo que sobra. min-height:0 es
   imprescindible: sin él un hijo flex no baja de su contenido y no encoge. */
.rep-chart-wrap--grande .rep-chart-lienzo {
  flex: 1 1 auto; height: auto; min-height: 0;
}
.rep-chart-wrap--grande .rep-chart-titulo { font-size: 1.25rem; }

/* --- "Ver más" de actividad en las tarjetas de Status Day ------------------ */
/* Va en la esquina de la tarjeta, discreto: es un atajo, no la acción principal
   (esa sigue siendo tocar el nombre para ver la ficha). */
.sd-prom-vermas {
  position: absolute; bottom: 12px; right: 14px; z-index: 2;
  display: inline-flex; align-items: center; gap: 3px;
  padding: 4px 9px; border: 1px solid var(--color-borde-suave); border-radius: 999px;
  background: var(--color-superficie); color: var(--color-texto-suave);
  font: inherit; font-size: var(--txt-xs, .72rem); font-weight: 700; cursor: pointer;
}
@media (hover: hover) {
  .sd-prom-vermas:hover { border-color: var(--color-energia); color: var(--color-texto); }
}


/* El botón "Ver más" se ancla a la esquina de su tarjeta: sin position aquí
   se posicionaría respecto a la página y acabaría en una esquina cualquiera. */
.sd-prom-card { position: relative; padding-bottom: 46px; }


/* ===========================================================================
   Modal de actividad del promotor (Status Day > Ver más)
   Con el ancho por defecto el estado de cada visita se cortaba contra el borde,
   y los nombres de almacén pesaban igual que todo lo demás. Se ensancha, se da
   aire a los bordes y se baja la escala tipográfica.
   =========================================================================== */
#actModal { max-width: 620px; width: calc(100% - 32px); padding: 22px 24px 24px; }
#actModal .modal-ts-head { margin-bottom: 2px; }
#actModal .ayuda { margin: 0 0 4px; }

.act-lista {
  list-style: none; margin: 14px 0 0; padding: 0;
  display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
}
.act-item {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 15px; border-radius: var(--radio);
  border: 1px solid var(--color-borde-suave); background: var(--color-superficie);
}
.act-item i {
  flex: 0 0 auto; display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); font-size: .98rem;
}
.act-item strong { font-size: 1.2rem; line-height: 1.1; font-variant-numeric: tabular-nums; }
.act-item span { display: block; font-size: .76rem; color: var(--color-texto-suave); line-height: 1.3; }

.act-tit {
  display: flex; align-items: center; gap: 7px;
  margin: 22px 0 12px; font-size: .74rem; text-transform: uppercase;
  letter-spacing: .06em; color: var(--color-texto-suave);
}

/* Recorrido como línea de tiempo. El estado va en insignia: así no compite con
   el nombre del almacén ni se corta contra el borde. */
.act-ruta { list-style: none; margin: 0; padding: 0 0 0 5px; }
.act-ruta li {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  padding: 11px 0 11px 18px; position: relative;
}
.act-ruta li::before {
  content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--color-energia); box-shadow: 0 0 0 3px var(--color-alerta-fondo);
}
.act-ruta li::after {
  content: ''; position: absolute; left: 4px; top: 50%; bottom: -11px; width: 1px;
  background: var(--color-borde-suave);
}
.act-ruta li:last-child::after { display: none; }
.act-hora { font-weight: 800; font-size: .88rem; font-variant-numeric: tabular-nums; }
.act-lugar {
  display: flex; align-items: center; gap: 7px; min-width: 0;
  font-size: .88rem; font-weight: 600; overflow-wrap: anywhere;
}
.act-lugar i { flex: 0 0 auto; color: var(--color-texto-suave); font-size: .95rem; }
.act-estado { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; font-size: var(--txt-xs, .72rem); }

@media (max-width: 520px) {
  /* En móvil el estado baja bajo el nombre antes que recortarse. */
  .act-ruta li { grid-template-columns: 44px minmax(0, 1fr); row-gap: 4px; }
  .act-estado { grid-column: 2; justify-self: start; }
}

/* El almacén dentro del modal de actividad abre su ficha: se subraya de forma
   discreta para que se note que lleva a algún sitio. Sin la señal, nadie
   descubre que se puede pulsar. */
.act-lugar .enlace-ent {
  cursor: pointer; text-decoration: underline;
  text-decoration-color: var(--color-borde);
  text-underline-offset: 3px; overflow-wrap: anywhere;
}
@media (hover: hover) {
  .act-lugar .enlace-ent:hover { text-decoration-color: var(--color-energia); }
}

/* Contadores pulsables del modal de actividad: los que tienen detalle se ven
   como algo en lo que se puede entrar; el check-in, que ya se detalla abajo en
   "Dónde estuvo", se queda como dato estático. */
.act-item--link {
  width: 100%; text-align: left; cursor: pointer; font: inherit;
  transition: border-color .12s ease;
}
.act-item--link .act-item-flecha { margin-left: auto; width: auto; height: auto; background: none; color: var(--color-texto-suave); font-size: .85rem; }
@media (hover: hover) {
  .act-item--link:hover { border-color: var(--color-energia); }
  .act-item--link:hover .act-item-flecha { color: var(--color-energia); }
}
.act-volver { margin-bottom: 12px; }
/* En el detalle la tercera columna es el almacén, también pulsable. */
.act-detalle .act-almacen { font-size: var(--txt-sec, .78rem); cursor: pointer; text-decoration: underline; text-decoration-color: var(--color-borde); text-underline-offset: 3px; }
.act-detalle .act-lugar small { font-weight: 500; }

/* Salidas de la ficha de almacén hacia las listas filtradas. Van en fila y se
   parten en varias líneas en móvil: son secundarias frente a "Abrir dirección",
   así que no compiten por el ancho. */
.modal-ts-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2, .5rem);
  margin-top: var(--e2, .5rem);
}
.modal-ts-acciones .btn { flex: 1 1 auto; justify-content: center; }
/* Aviso de deep-link: explica por qué la lista llega recortada. */
.ff-deeplink { margin-bottom: var(--e2, .5rem); }

/* ===========================================================================
   TANDA v166 — aprobación de altas, evidencia de visita, objetivos
   verificados y ficha del punto de venta para el promotor.
   =========================================================================== */
/* === Punto 1 — Aprobación de altas de promotor === */
/* --- Usuarios y roles: bandeja de altas pendientes ------------------------
   La tarjeta de un alta sin revisar es la única del listado que exige una
   decisión, así que se separa del resto con una franja de acento. */
.usuario-pendiente {
  border-color: var(--color-acento);
  border-left: 4px solid var(--color-acento);
}
.usuario-pendiente-meta { margin: 8px 0 0; font-size: var(--txt-sm, .82rem); }
.us-filtros .filtro-panel-grid { grid-template-columns: minmax(240px, 1.35fr) repeat(2, minmax(180px, 1fr)) auto; align-items: end; }
.us-filtro-buscar input {
  width: 100%; min-height: 48px; padding: 10px 12px;
  border: 2px solid var(--color-borde); border-radius: var(--radio);
  background: var(--color-superficie); color: var(--color-texto); font: inherit;
}
.us-listado-cab { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 18px 0 10px; }
.us-listado-cab h2 { font-size: 1.08rem; }
.usuario-card { padding: 0; overflow: hidden; margin-bottom: 10px; }
.us-card-cab { display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; align-items: center; gap: 11px; padding: 13px 14px; }
.us-avatar {
  width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 13px; background: var(--color-texto); color: var(--color-superficie);
  font-family: var(--fuente-titulo); font-size: 1.05rem; font-weight: 800;
}
.us-identidad { min-width: 0; }
.us-identidad > strong, .us-identidad .celda-enlace strong { font-size: .95rem; }
.us-identidad .text-suave { display: block; margin-top: 2px; font-size: .76rem; line-height: 1.35; overflow-wrap: anywhere; }
.us-badges { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
.us-estado { display: inline-flex; align-items: center; gap: 5px; font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .03em; }
.us-estado::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--color-texto-suave); }
.us-estado--activo::before { background: var(--color-alerta-bajo); }
.us-estado--inactivo::before, .us-estado--rechazado::before { background: var(--color-alerta-alto); }
.us-estado--suspendido::before, .us-estado--pendiente::before { background: var(--color-alerta-medio); }
.usuario-pendiente .usuario-pendiente-meta, .usuario-pendiente > .fila-acciones { margin-inline: 14px; }
.usuario-pendiente > .fila-acciones { margin-bottom: 14px; }
.usuario-gestionar { border-top: 1px solid var(--color-borde-suave); }
.usuario-gestionar > summary {
  list-style: none; min-height: 44px; padding: 9px 14px; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  color: var(--color-acento-fuerte); font-size: .8rem; font-weight: 800;
}
.usuario-gestionar > summary::-webkit-details-marker { display: none; }
.usuario-gestionar > summary span { display: inline-flex; align-items: center; gap: 6px; }
.usuario-gestionar-flecha { transition: transform .16s; }
.usuario-gestionar[open] .usuario-gestionar-flecha { transform: rotate(180deg); }
.usuario-gestionar[open] > summary { background: var(--color-superficie-alt); color: var(--color-texto); }
.usuario-acciones { margin: 0; padding: 12px 14px 14px; background: var(--color-superficie-alt); border-top: 1px solid var(--color-borde-suave); }
.usuario-acciones .fila-inline { flex: 1 1 250px; }
@media (max-width: 900px) { .us-filtros .filtro-panel-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .us-filtros .filtro-panel-grid { grid-template-columns: 1fr; gap: 9px; }
  .us-card-cab { grid-template-columns: 42px minmax(0, 1fr); align-items: start; padding: 12px; }
  .us-avatar { width: 42px; height: 42px; }
  .us-badges { grid-column: 2; justify-content: flex-start; }
  .usuario-acciones { display: grid; grid-template-columns: 1fr; }
  .usuario-acciones .fila-inline, .usuario-pendiente .fila-inline { width: 100%; display: grid; grid-template-columns: 1fr; }
  .usuario-acciones select, .usuario-acciones .btn, .usuario-pendiente select, .usuario-pendiente .btn { width: 100%; }
}

/* Contador dentro de las pestañas de estado (reutilizan .tab-tamanio). Se le
   quita el punto de .badge::before: ahí dentro compite con el número. */
.tab-conteo { margin-left: 8px; padding: 2px 8px; font-size: var(--txt-micro, .7rem); }
.tab-conteo::before { display: none; }

/* === Puntos 3 y 7 — Evidencia de visita y objetivos verificados === */
/* ===========================================================================
   Check-out honesto: cada objetivo con su respaldo real (mi-ruta.js)
   La fila pasó de "label + texto" a "label + bloque nombre/detalle", por eso se
   alinea arriba en vez de al centro.
   =========================================================================== */
.co-obj { align-items: flex-start; }
.co-obj-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.co-obj-det { font-weight: 600; font-size: var(--txt-sec, .78rem); line-height: 1.35; color: var(--color-texto-suave); }
/* Objetivo SIN respaldo: se ve y se entiende POR QUÉ, pero no se puede marcar
   (el input va disabled desde el JS; el borde punteado lo hace legible sin
   depender sólo del color). */
.co-obj--sin { opacity: .72; cursor: not-allowed; border-style: dashed; }
.co-obj--sin input { cursor: not-allowed; }
.co-obj--sin .co-obj-det { color: var(--color-alerta-alto, #B42318); }

/* ===========================================================================
   Status Day: rastro de las reaperturas de check-in
   .sd-reab      -> línea dentro de la tarjeta del promotor / de fuera de zona
   .sd-reab-badge-> pastilla sobre la foto de check-in (.sd-checkin ya es
                    position:relative; el badge de "fuera de zona" ocupa la
                    esquina izquierda, éste va a la derecha para no chocar)
   =========================================================================== */
.sd-reab {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 8px;
  font-size: var(--txt-sec, .78rem); font-weight: 700; line-height: 1.35;
  color: var(--color-acento-fuerte, #9A6103);
}
.sd-reab-badge {
  position: absolute; top: 6px; right: 6px; display: inline-flex; align-items: center; gap: 4px;
  background: var(--color-acento-fuerte, #9A6103); color: #fff; font-size: .68rem; font-weight: 800;
  padding: 3px 7px; border-radius: 999px; line-height: 1;
}

/* === Punto 9 — El promotor ve lo levantado antes en ese punto de venta === */
/* ============================================================================
   Ficha del punto de venta para el promotor (app/almacen-ficha.php):
   lo ya levantado en esa tienda — precios, flooring y formularios — solo lectura.
   ========================================================================= */
.afi-secciones { display: flex; flex-wrap: wrap; gap: var(--e2); margin-bottom: var(--e4); }
.afi-secciones .chip-capa { text-decoration: none; }

.afi-lista { display: flex; flex-direction: column; gap: var(--e2); }

/* --- Último precio por producto --- */
.afi-precio {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto;
  gap: var(--e3);
  align-items: center;
  padding: var(--e3);
  border: 1px solid var(--color-borde);
  border-radius: var(--radio-md);
  background: var(--color-superficie);
  box-shadow: var(--sombra-dura-sm);
}
/* El display:grid de arriba le gana al atributo [hidden] que usa el buscador:
   sin esta regla las filas filtradas seguirían viéndose (trampa ya vista tres
   veces en este proyecto). */
.afi-precio[hidden] { display: none; }
/* Misma trampa, y además arregla un caso YA existente: .empty-state-mini declara
   display:grid, así que el aviso "Sin coincidencias" de app/almacenes.php
   (#sinResultadosAlmacenes, que se apaga con .hidden desde almacenes.js) queda
   visible siempre. Se declara global porque el patrón se repite. */
.empty-state-mini[hidden] { display: none; }

.afi-precio-foto { width: 56px; height: 56px; border-radius: var(--radio-sm); object-fit: cover; background: var(--color-fondo); }
.afi-precio-foto--vacia { display: grid; place-items: center; color: var(--color-texto-suave); font-size: 1.4rem; }
.afi-precio-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.afi-precio-nombre { font-size: var(--text-md); line-height: 1.25; }
.afi-precio-meta,
.afi-precio-firma { font-size: var(--text-xs); color: var(--color-texto-suave); }
.afi-precio-nota { font-size: var(--text-xs); color: var(--color-texto-suave); font-style: italic; }
.afi-precio-cifra { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; text-align: right; }
.afi-precio-valor { font-family: var(--fuente-titulo); font-weight: 800; font-size: var(--text-lg); white-space: nowrap; }
/* Mismos tintes que .ubi-card-var: hue fijo sobre fondo translúcido, legible en
   claro y en oscuro sin overrides. */
.afi-precio-var { display: inline-flex; align-items: center; gap: 4px; font-size: var(--text-2xs); font-weight: 700; padding: 2px 8px; border-radius: var(--radio-pill); white-space: nowrap; }
.afi-precio-var.es-sube { color: #E5484D; background: rgba(229, 72, 77, .14); }
.afi-precio-var.es-baja { color: #2E9E5B; background: rgba(46, 158, 91, .16); }
.afi-precio-antes { font-size: var(--text-2xs); color: var(--color-texto-suave); white-space: nowrap; }

/* --- Flooring: frentes por marca del último conteo --- */
.afi-flo { margin-top: var(--e4); padding-top: var(--e3); border-top: 1px dashed var(--color-borde-suave); }
.afi-flo:first-of-type { margin-top: var(--e3); padding-top: 0; border-top: 0; }
.afi-flo-head { display: flex; align-items: center; justify-content: space-between; gap: var(--e2); flex-wrap: wrap; margin-bottom: var(--e2); }
.afi-flo-head strong { display: block; font-family: var(--fuente-titulo); }
.afi-flo-head .text-suave { display: block; font-size: var(--text-xs); }
.afi-flo-total { font-weight: 800; font-size: var(--text-xs); padding: 4px 10px; border-radius: var(--radio-pill); background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); white-space: nowrap; }
.afi-flo-foto { display: block; width: 100%; max-height: 180px; object-fit: cover; border-radius: var(--radio-sm); border: 1px solid var(--color-borde-suave); margin-bottom: var(--e2); cursor: zoom-in; }
.afi-flo-fila { display: grid; grid-template-columns: minmax(72px, 26%) minmax(0, 1fr) auto; gap: var(--e2); align-items: center; margin-bottom: 6px; }
.afi-flo-marca { font-size: var(--text-xs); font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.afi-flo-barra { height: 12px; border-radius: var(--radio-pill); background: var(--color-superficie-alt); border: 1px solid var(--color-borde-suave); overflow: hidden; }
.afi-flo-barra-fill { display: block; height: 100%; background: var(--color-acento); }
.afi-flo-frentes { font-size: var(--text-xs); white-space: nowrap; }

/* --- Formularios llenados en la tienda --- */
.afi-form { display: flex; align-items: center; gap: var(--e3); padding: var(--e3); border: 1px solid var(--color-borde); border-radius: var(--radio-md); background: var(--color-superficie); box-shadow: var(--sombra-dura-sm); }
.afi-form-ico { flex: 0 0 auto; width: 40px; height: 40px; border-radius: var(--radio-sm); display: grid; place-items: center; background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); font-size: 1.15rem; }
.afi-form-info { min-width: 0; margin-right: auto; display: flex; flex-direction: column; gap: 2px; }
.afi-form-info .text-suave { font-size: var(--text-xs); }
.afi-form-valores { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--e2); margin-top: var(--e3); }
.afi-form-valor { padding: var(--e2); border: 1px dashed var(--color-borde-suave); border-radius: var(--radio-sm); background: var(--color-fondo); }
.afi-form-valor-et { display: block; font-size: var(--text-2xs); color: var(--color-texto-suave); margin-bottom: 2px; }
.afi-form-valor-foto { display: block; width: 100%; height: 110px; object-fit: cover; border-radius: var(--radio-sm); cursor: zoom-in; }

/* En móvil el precio baja a su propia línea: con tres columnas el nombre del
   producto quedaba en dos caracteres por renglón. */
@media (max-width: 560px) {
  .afi-precio { grid-template-columns: 48px minmax(0, 1fr); }
  .afi-precio-foto { width: 48px; height: 48px; }
  .afi-precio-cifra { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: flex-start; text-align: left; gap: var(--e2); }
}
/* La fila de acciones de la ficha de almacén pasó de 5 a 6 botones: sin
   flex-wrap el sexto se recortaba fuera de la tarjeta (overflow:hidden) y
   quedaba inalcanzable en un teléfono de 360 px. */
.almacen-ficha-acciones { flex-wrap: wrap; }

/* El almacén sugerido por GPS: un borde de acento basta para que salte a la
   vista sin competir con el resto de la lista. La distancia ya se pinta en
   .ff-alm-dist, así que aquí no se repite información. */
.form-tile--cercano { border-color: var(--color-acento); box-shadow: inset 3px 0 0 0 var(--color-acento); }

/* Chip de objetivo que ABRE la captura (antes solo palomeaba una casilla). La
   flechita es la única señal de que sale de la pantalla; sin ella el promotor
   no distingue el chip que navega del que marca. */
.chip-objetivo--ir { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
/* El acento se queda SÓLO en la flechita: como color de todo el chip daba 4,30:1
   sobre el fondo de la píldora, y el texto se pasó a --color-texto (12,91:1).
   Un icono es elemento gráfico y le aplica el 3:1, que sí cumple. */
/* Sin opacidad: la regla ya traía opacity .7 de cuando la flecha era adorno.
   Compuesta contra el fondo daba 2,64:1 en claro —por debajo del 3:1 que pide un
   gráfico— y el comentario de más abajo dice que esta flecha es la ÚNICA señal de
   que el chip sale de la pantalla. Sin ella: 4,30:1 en claro, 7,84:1 en oscuro. */
.chip-objetivo--ir i { font-size: .8em; color: var(--color-acento-fuerte); }
/* ============================================================================
   admin/codigo-barras.php — trampa [hidden] vs display.
   Las dos clases declaran `display: inline-flex` (app.css:4752 y app.css:4780),
   y una regla display: le GANA al atributo [hidden]. Sin estas dos líneas:
     - .bc-revertir  ("Quitar todos") se ve siempre, aunque no haya ningún
       duplicado soltado: el `hidden` del markup y el `rev.hidden = n === 0`
       de codigo-barras.js:33 no hacen nada.
     - .bc-merge-vacio ("Suelta aquí los duplicados…") no desaparece al soltar
       el primero: el `vacio.hidden = true` de codigo-barras.js:50 tampoco.
   Mismo patrón que .hoja-ov[hidden], .form-tile[hidden] o .insight-grid[hidden],
   que ya viven en app.css por esta misma razón.
   ========================================================================= */
.bc-revertir[hidden],
.bc-merge-vacio[hidden] { display: none; }
/* ===========================================================================
   TANDA v170 — pulido visual pendiente de la auditoría v125: hero con
   contraste, y las primeras reglas @media print del proyecto.
   =========================================================================== */
/* ===== Impresión de reportes del admin (v125-backlog). Pegar AL FINAL de app.css.
   Clases verificadas contra layout/admin_header.php y las páginas de admin/:
   .admin-sidebar/.admin-overlay/.topbar/.admin-subtabs (chrome), .filtro-panel/
   .sd-filtros/.filtros-matriz/.ff-filtros (filtros), .tarjeta/.tarjeta-plana/
   .hero-kpi (tarjetas), .chart-scroll* (visor con scroll de chart-defaults.js). */
@media print {
  /* Chrome de la app fuera: en papel solo va el contenido del informe. */
  .admin-sidebar, .admin-overlay, .topbar, .admin-subtabs, .admin-quick, .admin-back-top, .filter-memory,
  .btn, .icon-btn,
  .filtro-panel, .sd-filtros, .filtros-matriz, .ff-filtros,
  .page-hero-actions, .chart-scroll-nota,
  .alerta[data-flash-toast] { display: none !important; }

  /* Papel blanco: fuera la trama de puntos y el fondo crema/tinta. */
  html, body {
    background: #FFFFFF !important;
    background-image: none !important;
    color: #22223B !important;
  }
  .admin-shell { display: block; max-width: none; }
  .admin-contenido { padding-left: 0; }

  /* Una tarjeta partida entre dos páginas no se puede leer. */
  .tarjeta, .tarjeta-plana, .hero-kpi, .page-hero {
    break-inside: avoid;
    page-break-inside: avoid; /* alias viejo: WebKit/motores de impresión antiguos */
    box-shadow: none !important;
  }

  /* El hero admin pinta fondo tinta (--hero-admin-bg) con texto crema, pero los
     navegadores NO imprimen fondos por defecto: sin esto el titular saldría
     crema sobre papel blanco (invisible). Se re-entinta a tinta sobre blanco. */
  .page-hero--admin { background: #FFFFFF !important; }
  .page-hero--admin h1,
  .page-hero--admin p,
  .page-hero--admin .step-kicker { color: #22223B !important; }

  /* Visor con scroll de los gráficos horizontales: en papel no hay scroll; se
     despliega el lienzo completo para que se impriman TODAS las filas. */
  .chart-scroll { height: auto !important; overflow: visible !important;
    -webkit-mask-image: none !important; mask-image: none !important; }
}
/* ---- Bandeja de alertas abiertas (admin/formularios.php) -------------------
   Va arriba de la pantalla porque es lo único que exige una acción hoy. El
   semáforo de antigüedad usa BORDE IZQUIERDO y badge, no color de fondo: el
   fondo de tarjeta ya significa otra cosa y en modo oscuro un tinte suave se
   pierde. Igual que en las tarjetas de Status Day, el color nunca va solo:
   siempre lo acompaña el texto de antigüedad. */
.al-bandeja { margin-bottom: 18px; }
.al-bandeja-cab h2 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 1.05rem; }
.al-bandeja--vacia p { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.al-lista { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.al-item {
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px 14px;
  border: 2px solid var(--color-borde);
  border-left-width: 6px;
  border-radius: var(--radio-md, 10px);
  background: var(--color-superficie);
}
.al-item--bajo  { border-left-color: var(--color-alerta-bajo, #2E7D32); }
.al-item--medio { border-left-color: #E9940A; }
.al-item--alto  { border-left-color: var(--color-alerta-alto, #B42318); }

/* DOS LÍNEAS, NO TRES. Medido: de los 108 px que ocupaba una alerta, 80 eran el
   bloque de texto —nombre del formulario (25), tienda (22) y la meta con el "hace
   1 día" (28)— y sólo 48 los botones. O sea que la altura la mandaba el texto.
   La tienda y la meta bajan a la MISMA línea, que además es como se leen: "dónde y
   cuándo" es un solo dato para quien tria alertas. Envuelven si no caben, así que en
   pantallas estrechas vuelven a apilarse solas en vez de recortarse. */
.al-item-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.al-item-txt strong { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.al-item-linea2 { display: flex; align-items: center; gap: 8px 12px; flex-wrap: wrap; min-width: 0; }
.al-item-donde { font-size: .88rem; }
.al-item-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: var(--txt-sm, .82rem); }

.al-item-acciones { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.al-form { display: flex; gap: 8px; flex: 1; min-width: 0; align-items: center; }
.al-form input[type="text"] { flex: 1; min-width: 120px; margin: 0; }

/* Desde tablet, el texto y las acciones van en una fila y la nota deja de
   ocupar todo el ancho. */
@media (min-width: 780px) {
  .al-item { flex-direction: row; align-items: center; justify-content: space-between; gap: 16px; }
  .al-item-txt { flex: 1; }
  .al-item-acciones { flex: 0 0 auto; }
  .al-form input[type="text"] { min-width: 180px; max-width: 240px; }
}

/* ---- Alertas abiertas dentro de la tarjeta de visita (layout/visita_card.php)
   El promotor las ve ANTES de entrar a la tienda. Se distingue con borde
   izquierdo y no con fondo de color: la tarjeta de visita ya usa el fondo para
   su propio estado, y dos señales de fondo compitiendo no se leen. */
.ruta-alertas {
  margin-top: 12px;
  padding: 10px 12px;
  border-left: 4px solid var(--color-alerta-alto, #B42318);
  border-radius: var(--radio-sm, 8px);
  background: var(--color-fondo);
}
.ruta-alertas-tit {
  margin: 0 0 6px;
  font-weight: 700;
  font-size: var(--txt-md, .9rem);
  display: flex; align-items: center; gap: 6px;
}
.ruta-alertas-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.ruta-alertas-lista li { font-size: .85rem; }
.ruta-alertas-pie { margin: 6px 0 0; font-size: .8rem; }



/* ===========================================================================
   TERMÓMETRO DE ALMACENES (admin/termometro.php)

   Tres capas: cifras, tarjetas de lo que pide atención, y la tabla plegada.
   El color nunca va solo — cada señal lleva además su número o su texto —,
   porque el mismo patrón en Status Day ya demostró que un tinte suave se
   pierde en modo oscuro.
   =========================================================================== */

/* --- Cabecera ------------------------------------------------------------ */
.termo-filtro select { margin: 0; min-width: 190px; }
.termo-explica {
  display: flex; align-items: flex-start; gap: 7px;
  font-size: .84rem; line-height: 1.45;
  margin: -6px 0 18px; max-width: 74ch;
}
.termo-explica i { flex: 0 0 auto; margin-top: 2px; }

/* Colores de las cuatro cifras. Reusa .sd-kpi de Status Day: mismo componente,
   mismo espaciado, una sola forma de leer un KPI en todo el panel. */
.termo-kpi--alerta .sd-kpi-ic { background: var(--color-alerta-alto, #B42318); }
/* Gris FIJO, no --color-texto-suave: ese token se aclara en tema oscuro y el
   glifo blanco encima se quedaba en 1,97:1, por debajo del mínimo de 3:1 para
   un elemento gráfico. Con éste son 5,2:1 en los dos temas, como los otros tres. */
.termo-kpi--gris   .sd-kpi-ic { background: #6B6B80; }
.termo-kpi--tibio  .sd-kpi-ic { background: var(--color-alerta-medio, #A25C00); }
.termo-kpi--ok     .sd-kpi-ic { background: var(--color-alerta-bajo, #2E7D32); }

/* Cuatro en fila en escritorio y DOS POR FILA en móvil: con el mínimo anterior
   (210px) se apilaban de una en una y hacían falta 400px de scroll para llegar
   al contenido. Medido: 4 columnas a 1280px, 2 columnas a 375px. */
.termo-kpis { grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); }
/* El criterio de cada grupo, debajo del número. "Al día" o "Sin actividad" a
   secas son etiquetas que cada quien interpreta a su manera. */
.termo-kpi-pie {
  display: block; margin-top: 3px; font-size: var(--txt-xs, .72rem); line-height: 1.35;
  color: var(--color-texto-suave, #55556B); text-wrap: pretty;
}

/* --- "Cómo se calcula", plegado ------------------------------------------- */
.termo-comose { margin: 0 0 16px; }
/* min-height: es el ÚNICO control que abre la explicación y medía 22,3 px
   (14,4 de letra x 1,55 de interlínea). El padding de .tarjeta queda fuera de la
   caja del <summary>, así que no compensaba. */
.termo-comose > summary {
  cursor: pointer; font-weight: 700; font-size: var(--txt-md, .9rem);
  display: flex; align-items: center; gap: 8px; min-height: 26px;
}
.termo-comose > summary::-webkit-details-marker { display: none; }
.termo-comose > summary:focus-visible { outline: 2px solid var(--color-texto); outline-offset: 3px; border-radius: 6px; }
.termo-comose-cuerpo { margin-top: 12px; font-size: var(--txt-peq, .86rem); line-height: 1.55; }
.termo-comose-cuerpo ol { margin: 0 0 10px; padding-left: 20px; }
.termo-comose-cuerpo li { margin-bottom: 7px; }
.termo-comose-cuerpo li::marker { font-weight: 700; }

/* --- Títulos de sección --------------------------------------------------- */
.termo-titulo {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  font-size: 1.05rem; margin: 22px 0 12px;
}
.termo-titulo-sub { font-size: var(--txt-sm, .82rem); font-weight: 600; }

/* --- Tarjetas ------------------------------------------------------------- */
.termo-grid {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
}
.termo-card {
  display: flex; flex-direction: column; gap: 9px;
  padding: 14px 16px;
  /* El borde izquierdo es la señal de gravedad. Se usa borde y no fondo para
     que conviva con el fondo de tarjeta en claro y en oscuro. */
  border-left: 5px solid var(--color-alerta-alto, #B42318);
}
.termo-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.termo-card-id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* Es el enlace principal de cada tarjeta de "Requieren atención" y medía 19,6 px
   de alto. Como es un ítem flex en columna no hereda relleno de nadie: el
   min-height y el padding vertical son la única vía. */
.termo-card-nombre {
  font-weight: 800; font-size: .98rem; line-height: 1.25;
  color: var(--color-texto); text-decoration: none;
  display: inline-flex; align-items: center; min-height: 26px; padding: 2px 0;
}
@media (hover: hover) { .termo-card-nombre:hover { text-decoration: underline; } }
.termo-card-ciudad { font-size: .8rem; display: flex; align-items: center; gap: 4px; }
.termo-card-caida {
  flex: 0 0 auto;
  font-family: var(--fuente-titulo); font-weight: 800; font-size: 1.35rem;
  line-height: 1; font-variant-numeric: tabular-nums;
  color: var(--color-alerta-alto, #B42318);
}

/* La barra: lo capturado contra lo esperado. Es lo que convierte un "-100%"
   abstracto en "0 de 15". */
.termo-barra {
  height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--color-borde-suave, #E4E0D6);
}
.termo-barra > span {
  display: block; height: 100%; border-radius: 999px;
  background: var(--color-alerta-alto, #B42318);
  min-width: 3px;   /* un cero absoluto seguiría dejando ver que la barra existe */
}
.termo-card-cifra { margin: 0; font-size: .84rem; }
.termo-card-cifra strong { font-variant-numeric: tabular-nums; }

.termo-chips { list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.termo-chips li {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .75rem; font-weight: 600;
  padding: 3px 8px; border-radius: 999px;
  border: 1px solid var(--color-borde-suave, #E4E0D6);
  color: var(--color-texto-suave);
}

.termo-vacio p { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }



/* En modo oscuro el rojo de marca (#D7263D) sobre el fondo de tarjeta se queda
   en 3,3:1: pasa por ser texto grande, pero es un contraste justo para EL
   número que da sentido a la tarjeta. Se usa la variante clara que el proyecto
   ya define para este caso, igual que hacen .badge-alto y .rep-pct--sube.
   El BORDE izquierdo se deja en el rojo saturado a propósito: como forma, no
   como texto, ahí la saturación lee mejor que un tono lavado. */
@media (prefers-color-scheme: dark) {
  .termo-card-caida { color: var(--color-alerta-alto-txt, #FF6B78); }
  .termo-caida.es-mala  { color: var(--color-alerta-alto-txt, #FF6B78); }
  .termo-caida.es-buena { color: var(--color-alerta-bajo-txt, #57C98A); }
}
:root[data-theme="dark"] .termo-card-caida,
:root[data-theme="dark"] .termo-caida.es-mala { color: var(--color-alerta-alto-txt, #FF6B78); }
:root[data-theme="dark"] .termo-caida.es-buena { color: var(--color-alerta-bajo-txt, #57C98A); }
/* --- La tabla completa, plegada ------------------------------------------
   Rehecha porque la primera versión "se sentía mucha información": diez
   columnas, encabezados de tres palabras y nombres de almacén partidos en tres
   líneas. Las decisiones aquí van todas en la misma dirección — menos texto por
   fila, no menos datos. */
.termo-detalle { margin-top: 22px; }
.termo-detalle > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; font-weight: 700;
}
.termo-detalle > summary::-webkit-details-marker { display: none; }
.termo-detalle > summary::after {
  content: '\25BE'; margin-left: auto; transition: transform .15s ease; font-size: .9em;
}
.termo-detalle[open] > summary::after { transform: rotate(180deg); }
.termo-detalle-pista { font-size: var(--txt-sec, .78rem); font-weight: 600; }
.termo-detalle > summary:focus-visible { outline: 2px solid var(--color-texto); outline-offset: 3px; border-radius: 6px; }

.termo-tabla { font-size: var(--txt-sm, .82rem); }
.termo-tabla th, .termo-tabla td { padding: 7px 10px; vertical-align: middle; }
.termo-tabla .termo-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Cabecera en dos niveles: "Días sin…" se dice UNA vez sobre sus tres columnas.
   Las celdas vacías del primer nivel no llevan borde para que no parezcan
   columnas de datos. */
.termo-thead-grupo td { border: 0; padding: 0 10px 2px; }
.termo-thead-grupo th {
  border: 0; padding: 0 10px 2px;
  text-align: center; font-size: var(--txt-xs, .72rem); font-weight: 700;
  color: var(--color-texto-suave); text-transform: none;
  border-bottom: 1px solid var(--color-borde-suave);
}
.termo-tabla thead tr:last-child th { font-size: .74rem; }
/* Segunda línea del encabezado: dice qué son los dos números de la celda.
   "0 / 2,8" y "3/5" no se explican solos, y el pie de tabla queda lejos cuando
   se está mirando la fila 140. */
.termo-th-sub {
  display: block; font-size: .67rem; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--color-texto-suave, #55556B); margin-top: 1px;
}

/* EL nombre en UNA sola línea. Antes, "ALMACENES JACOME ARGENTINA Y 5 DE JUNIO
   - RIOBAMBA" ocupaba tres renglones y hacía la fila cuatro veces más alta que
   su contenido. Con corte y puntos suspensivos la tabla vuelve a escanearse de
   un vistazo; el nombre completo sigue disponible en el title y en la ficha. */
.termo-td-nombre {
  max-width: 260px; font-weight: 400; text-align: left;
}
.termo-td-nombre > a {
  display: block; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--color-texto); text-decoration: none; font-weight: 700;
}
@media (hover: hover) { .termo-td-nombre > a:hover { text-decoration: underline; } }
/* La ciudad sólo aparece cuando el nombre no la lleva ya dentro (ver
   $termoCiudadUtil): en producción casi todos los nombres terminan en su
   ciudad, así que esta línea desaparece de casi todas las filas. */
.termo-ciudad {
  display: block; font-size: var(--txt-xs, .72rem); color: var(--color-texto-suave);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.termo-esperado { color: var(--color-texto-suave); font-weight: 400; }
/* Sin opacity: al 55 % quedaba en 2,52:1. Y no es decoración — el pie de la tabla
   dice que un guión significa "ese dato nunca existió, no que valga cero", así que
   quien no lo distinga del fondo pierde esa lectura. */
.termo-vacia { color: var(--color-texto-suave); }
.termo-caida { font-weight: 700; font-variant-numeric: tabular-nums; }
.termo-caida.es-mala  { color: var(--color-alerta-alto, #B42318); }
.termo-caida.es-buena { color: var(--color-alerta-bajo, #2E7D32); }
/* El porcentaje sin bastante dato detrás se atenúa y lleva un asterisco: se
   sigue viendo, pero no compite con los que sí significan algo. */
/* NO se atenúa con opacity: al 50 % el rojo caía a 2,29:1 y el verde a 2,13, y
   ninguna opacidad por debajo del 100 % pasa el 4,5 con estos tonos.
   Y hay una razón mejor para el cambio: una cifra POCO FIABLE no debería gritar en
   rojo ni en verde. Se le quita el color de juicio y se queda en el gris de texto,
   a plena opacidad. El asterisco que ya la acompaña explica por qué. */
.termo-caida.es-flojo { color: var(--color-texto-suave); font-weight: 600; }
.termo-fila--alerta > :first-child { box-shadow: inset 3px 0 0 var(--color-alerta-alto, #B42318); }

/* Cabecera ordenable: la afordancia tiene que verse, si no nadie descubre que
   se puede pulsar. */
.tabla th.es-ordenable { cursor: pointer; user-select: none; white-space: nowrap; }
.tabla th.es-ordenable::after { content: ' \2195'; opacity: .35; font-size: .85em; }
.tabla th.es-ordenable[data-dir="asc"]::after  { content: ' \2191'; opacity: 1; }
.tabla th.es-ordenable[data-dir="desc"]::after { content: ' \2193'; opacity: 1; }
.tabla th.es-ordenable:focus-visible { outline: 2px solid var(--color-texto); outline-offset: -2px; }

/* ===========================================================================
   ACERCA DE — la gente del punto de venta (app/acerca-pantalla.php)

   Se usa de pie en una tienda, con una mano, mientras se conversa. De ahí las
   tres decisiones: objetivos de toque grandes, una columna en móvil, y nada de
   colores que signifiquen algo — aquí no hay estados ni alertas, sólo datos de
   personas.
   =========================================================================== */

.ac-titulo { font-size: 1.05rem; margin: 18px 0 12px; }
.ac-cab { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 18px 0 12px; }

/* --- Paso 1: a quién corresponde la ficha -------------------------------- */
.ac-perfiles { display: grid; gap: 10px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .ac-perfiles { grid-template-columns: 1fr 1fr; } }
.ac-perfil {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px; text-decoration: none; color: var(--color-texto);
  /* 64px de alto mínimo: se pulsa con el pulgar, de pie y en movimiento. */
  min-height: 64px;
}
.ac-perfil-ic {
  width: 44px; height: 44px; flex: 0 0 auto; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  /* El icono se pinta con el color del FONDO de la página, no con blanco fijo.
     En oscuro --color-texto es un crema claro, así que un #fff encima daba un
     contraste de ~1,06:1: el icono desaparecía y las cuatro tarjetas quedaban
     con un cuadrado vacío. */
  font-size: 1.35rem; color: var(--color-superficie); background: var(--color-texto);
}
.ac-perfil-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.ac-perfil-txt strong { font-size: 1rem; }
.ac-perfil-txt .text-suave { font-size: .8rem; }
.ac-perfil-flecha { flex: 0 0 auto; opacity: .5; }
@media (hover: hover) { .ac-perfil:hover .ac-perfil-flecha { opacity: 1; } }

/* --- Paso 2: las personas ya registradas -------------------------------- */
.ac-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.ac-item { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; }
.ac-item-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ac-item-txt strong { font-size: 1rem; }
.ac-item-txt .text-suave { font-size: var(--txt-sm, .82rem); display: flex; align-items: center; gap: 5px; }
/* El gusto puede ser largo; se corta a dos líneas en vez de estirar la tarjeta. */
.ac-item-gustos {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.ac-item-acciones { display: flex; gap: 8px; align-items: center; }
.ac-item-acciones form { display: inline-flex; margin: 0; }
@media (min-width: 620px) {
  .ac-item { flex-direction: row; align-items: center; justify-content: space-between; gap: 16px; }
  .ac-item-txt { flex: 1; }
  .ac-item-acciones { flex: 0 0 auto; }
}
.ac-vacio p { margin: 0; }

/* --- Paso 3: el formulario ---------------------------------------------- */
/* Los grupos van con borde y leyenda en vez de sólo un título: separan
   visualmente "cumpleaños" y "familia" del resto, que es como el promotor los
   pregunta — por temas, no de corrido. */
.ac-grupo {
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-md, 10px);
  padding: 12px 14px 14px;
  margin: 18px 0 0;
}
.ac-grupo > legend { padding: 0 6px; font-weight: 700; font-size: var(--txt-md, .9rem); }
.ac-grupo > .ayuda:first-of-type { margin-top: 0; }

/* Día · mes · año. En móvil el día y el mes comparten fila y el año va debajo:
   el año es opcional y no debe competir por el sitio con lo que sí importa. */
.ac-cumple { display: grid; gap: 8px; grid-template-columns: 90px 1fr; }
.ac-cumple > :nth-child(3) { grid-column: 1 / -1; }
@media (min-width: 520px) {
  .ac-cumple { grid-template-columns: 90px 1fr 140px; }
  .ac-cumple > :nth-child(3) { grid-column: auto; }
}
.ac-cumple input, .ac-cumple select { margin: 0; width: 100%; }

.ac-dos { display: grid; gap: 10px; grid-template-columns: 1fr; }
@media (min-width: 520px) { .ac-dos { grid-template-columns: 1fr 1fr; } }
.ac-dos select { margin: 0; }

.ac-form textarea { min-height: 72px; }

/* ---- "Estás en X": el almacén se dedujo del check-in (layout/visita_contexto.php)
   Se ve a propósito. Rellenar el almacén a escondidas es cómodo hasta el día en
   que el promotor hizo check-in en una tienda y está registrando de otra: con la
   barra delante, el error es visible ANTES de cometerlo, no después de guardar
   cuarenta precios. Verde suave y no ámbar: no es una advertencia, es una
   confirmación de dónde está. */
.vctx {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 0 0 14px; padding: 10px 14px;
  border: 1px solid var(--color-borde-suave);
  border-left: 5px solid var(--color-alerta-bajo, #1B7A43);
  border-radius: var(--radio-md, 10px);
  background: var(--color-superficie);
}
.vctx-ic {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; background: var(--color-alerta-bajo, #1B7A43);
}
.vctx-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.vctx-txt strong { font-size: .95rem; }
.vctx-txt .text-suave { font-size: var(--txt-sec, .78rem); line-height: 1.35; }
.vctx-btn { flex: 0 0 auto; }
/* Variante de AVISO (dos check-in abiertos): no es la misma noticia que "estás
   en X", así que no se pinta en verde. Se usan los tokens de alerta, que ya
   tienen su equivalente en oscuro, en vez de colores fijos. */
.vctx--aviso { border-left-color: var(--color-energia, #E9940A); background: var(--color-alerta-fondo, #FFF1DC); }
/* Tinta FIJA sobre el naranja, no var(--color-texto). El naranja es el mismo en
   los dos temas, pero --color-texto se invierte a crema en oscuro y dejaba el
   icono en 2,77:1 — por debajo del 3:1 que pide un icono. Con la tinta de la
   marca son 4,9:1 en ambos. Es el mismo motivo por el que el CTA fija #22223B. */
.vctx--aviso .vctx-ic { background: var(--color-energia, #E9940A); color: #22223B; }
.vctx--aviso .vctx-txt a { color: inherit; text-decoration: underline; font-weight: 700; }

/* ---- Acerca de, vista del admin (admin/acerca.php) ------------------------
   Tarjetas y no tabla: lo que se lee aquí son frases sobre personas ("la esposa
   se llama Elizabeth"), no cifras que se comparen en columnas. Una tabla las
   recortaría justo donde está el valor. */
.acx-filtros { margin-bottom: 16px; }
.acx-form { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }
.acx-form select, .acx-form input[type="search"] { margin: 0; min-width: 150px; }
.acx-form label { display: block; font-size: var(--txt-sec, .78rem); font-weight: 700; margin-bottom: 3px; }
.acx-buscar input { min-width: 200px; }
.acx-chk { display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: .85rem; }
.acx-chk input { margin: 0; }
.acx-conteo { margin: 0 0 10px; font-size: .84rem; }

.acx-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.acx-card { display: flex; flex-direction: column; gap: 8px; padding: 14px 16px; }
/* Una ficha dada de baja se ve, pero apagada: sigue estando para poder
   eliminarla del todo, no para consultarla. */
.acx-card--baja { opacity: .6; border-style: dashed; }

.acx-card-top { display: flex; flex-direction: column; gap: 6px; }
.acx-card-id { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.acx-card-id strong { font-size: 1rem; }
.acx-cumple {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--txt-sm, .82rem); font-weight: 600; color: var(--color-texto-suave);
}
.acx-donde { margin: 0; font-size: .8rem; display: flex; align-items: center; gap: 5px; }

.acx-datos { margin: 4px 0 0; display: flex; flex-direction: column; gap: 7px; }
.acx-datos dt {
  font-size: var(--txt-micro, .7rem); font-weight: 800; text-transform: uppercase;
  letter-spacing: .04em; color: var(--color-texto-suave); margin: 0;
}
.acx-datos dd { margin: 1px 0 0; font-size: var(--txt-peq, .86rem); line-height: 1.4; }
.acx-chips { display: block; font-weight: 700; }

.acx-origen { margin: 2px 0 0; font-size: var(--txt-xs, .72rem); line-height: 1.4; }
.acx-acciones { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.acx-acciones form { margin: 0; }

/* Etiquetas y leyendas de "Acerca de" con icono: cada bloque se distingue de un
   vistazo mientras se conversa, sin tener que leer el título. El icono es
   decorativo (aria-hidden) — el texto sigue siendo la etiqueta real. */
.ac-lbl, .ac-grupo > legend { display: flex; align-items: center; gap: 6px; }
.ac-lbl > i, .ac-grupo > legend > i { font-size: 1.05em; opacity: .75; }
.ac-titulo { display: flex; align-items: center; gap: 8px; }
.acx-datos dt { display: flex; align-items: center; gap: 5px; }
.acx-card-id .badge { display: inline-flex; align-items: center; gap: 4px; }

/* ---- Marcas: barra de trabajo, lote y gemelas (admin/marcas.php) -----------
   El filtrado es en cliente sobre las filas ya pintadas (~50 marcas), el mismo
   patrón de admin/percha.php. Nada de esto necesita SQL nuevo. */
.mk-barra { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 14px; }
.mk-buscar { position: relative; flex: 1 1 260px; min-width: 0; }
.mk-buscar i { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); opacity: .55; pointer-events: none; }
.mk-buscar input { width: 100%; padding-left: 36px; margin: 0; }
.mk-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip-filtro {
  border: 1px solid var(--color-borde-suave); background: var(--color-superficie);
  color: inherit; border-radius: var(--radio-pill, 999px);
  padding: 5px 12px; font-size: .8rem; font-weight: 600; cursor: pointer;
  transition: background .15s, border-color .15s;
}
.chip-filtro:hover { border-color: var(--color-texto); }
/* La tinta va fija porque el fondo del chip activo también lo es: heredar
   --color-texto lo dejaría crema sobre crema en oscuro. */
.chip-filtro.is-activo { background: var(--color-energia, #E9940A); border-color: var(--color-energia, #E9940A); color: #22223B; }
.mk-cuenta { flex: 1 0 100%; margin: 0; font-size: var(--txt-sm, .82rem); }
.mk-lote {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-bottom: 12px; padding: 10px 14px;
  border: 1px solid var(--color-borde-suave); border-left: 5px solid var(--color-energia, #E9940A);
  border-radius: var(--radio-md, 10px); background: var(--color-alerta-fondo, #FFF1DC);
}
/* TRAMPA [hidden] vs display, quinta aparición en este archivo: el marcado es
   <div class="mk-lote" id="mkLote" hidden> y el JS alterna ese atributo, pero el
   display: flex de arriba le GANA al [hidden] del navegador. O sea que la barra
   "0 marcadas · Habilitar · Quitar" estaba visible SIEMPRE, en contra de lo que
   dice el comentario del propio marcas.php. Con la barra ya al pie y pegajosa,
   dejarlo así taparía la tabla sin haber marcado nada. */
.mk-lote[hidden] { display: none; }
/* Al pie y pegada: marcando la mitad inferior del catálogo la barra quedaba
   fuera de pantalla y había que subir a ciegas para enviar. Va DESPUÉS del
   .tabla-scroll en el marcado (sticky-bottom sólo tira hacia arriba de un
   elemento cuya posición natural cae por debajo del borde inferior: declarar la
   regla sin mover el marcado no habría hecho nada). No lleva background propio:
   .mk-lote ya trae --color-alerta-fondo, que sí se invierte en oscuro. */
.mk-lote--pie {
  position: sticky; bottom: 0; z-index: 2;
  margin-top: 10px;
  border-top: 1px solid var(--color-borde-suave);
  /* .mk-lote trae margin-bottom: 12px y en sticky lo que se alinea es la caja
     de MARGENES, no la de bordes: con bottom:0 la barra se pararia 12px por
     encima del borde y por esa franja se veria pasar la tabla. */
  margin-bottom: 0;
}
.mk-lote-n { font-size: .85rem; }
/* La casilla de lote es con lo que se monta el mes y medía 13x13 px sueltos en
   una celda de 34: pinchar al lado no marcaba nada. El <label> que la envuelve
   convierte la celda entera en blanco táctil. */
.mk-col-chk { width: 44px; text-align: center; }
.mk-col-chk input { margin: 0; }
.mk-chk-hit {
  display: flex; align-items: center; justify-content: center;
  width: 100%; min-height: 44px; cursor: pointer;
}
.mk-chk { width: 18px; height: 18px; }
.mk-alta { font-size: var(--txt-sec, .78rem); line-height: 1.3; white-space: nowrap; }
.mk-alta-quien { opacity: .75; }
.mk-vacio { margin: 14px 0 0; text-align: center; }
/* La fila a la que se llega desde un aviso ("esa marca ya existe"): se señala
   sin depender del scroll, que en móvil deja la fila fuera de vista. */
.fila-foco > td { background: var(--color-alerta-fondo, #FFF1DC); }
.mk-gemelas { border-left: 5px solid var(--color-energia, #E9940A); }
.mk-gemela {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px; padding: 10px 0; border-top: 1px solid var(--color-borde-suave);
}
.mk-gemela-lista { display: flex; flex-wrap: wrap; gap: 14px; min-width: 0; }
.mk-gemela-item { display: flex; flex-direction: column; gap: 1px; font-size: .85rem; }
.mk-gemela-item .text-suave { font-size: .75rem; }
.mk-modal-alta { font-size: var(--txt-sec, .78rem); }
.mk-editar { border-top: 1px solid var(--color-borde-suave); padding-top: 14px; margin-top: 4px; }
@media (max-width: 640px) {
  /* En móvil la columna de alta ya no cabe sin empujar la tabla; el dato sigue
     estando en el modal, que es donde se consulta a propósito. */
  .mk-alta, .tabla-admin th:nth-child(8) { display: none; }
}
/* Lista de marcas en impulso que no vendieron (admin/ventas.php). */
.vs-cero { list-style: none; padding: 0; margin: 0 0 14px; }
.vs-cero li { display: flex; justify-content: space-between; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--color-borde-suave); font-size: .88rem; }
.vs-cero li:last-child { border-bottom: 0; }
.vs-cero .text-suave { font-size: var(--txt-sec, .78rem); white-space: nowrap; }

/* ---- Almacenes: alias, duplicados y avisos de fila (admin/almacenes.php) --- */
.ed-alias-fila { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--color-borde-suave); font-size: .85rem; }
.ed-alias-x { flex: 0 0 auto; }
.ed-alias-add { display: flex; gap: 8px; margin-top: 8px; }
.ed-alias-add input { flex: 1; margin: 0; }
.alm-badge-nuevo, .alm-badge-gps { font-size: .62rem; padding: 1px 6px; }
.alm-cell-quien { font-size: .76rem; opacity: .8; }
.alm-dups { border-left: 5px solid var(--color-energia, #E9940A); }
.alm-dup { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 0; border-top: 1px solid var(--color-borde-suave); }
.alm-dup-lista { display: flex; flex-wrap: wrap; gap: 14px; min-width: 0; }
.alm-dup-item { display: flex; flex-direction: column; gap: 1px; font-size: .85rem; }
.alm-dup-item .text-suave { font-size: .75rem; }

/* ---- Itinerario: estados, contador y selección múltiple -------------------
   Los cuatro colores van como borde izquierdo sobre --color-superficie-alt, no
   como fondo: un fondo de color deja el texto del chip ilegible en oscuro, y el
   borde funciona igual en los dos temas. */
.itin-leyenda { display: flex; flex-wrap: wrap; gap: 14px; margin: 0 0 10px; font-size: .76rem; }
.itin-ley { display: inline-flex; align-items: center; gap: 5px; }
.itin-ley-c { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
.itin-ley-c--planificada  { background: var(--color-borde, #C9C4B5); }
.itin-ley-c--en_curso     { background: var(--color-energia, #E9940A); }
.itin-ley-c--realizada    { background: var(--color-alerta-bajo, #1B7A43); }
.itin-ley-c--no_realizada { background: var(--color-alerta-alto, #D7263D); }

.itin-chip--e-en_curso     { border-left: 4px solid var(--color-energia, #E9940A); }
.itin-chip--e-realizada    { border-left: 4px solid var(--color-alerta-bajo, #1B7A43); }
.itin-chip--e-no_realizada { border-left: 4px solid var(--color-alerta-alto, #D7263D); }
/* En la vista "Todos" el borde ya lo pinta el color del promotor: ahí manda ése,
   porque distinguir de quién es la visita es el propósito de esa vista. */
.itin-cal--todos .itin-chip--e-en_curso,
.itin-cal--todos .itin-chip--e-realizada,
.itin-cal--todos .itin-chip--e-no_realizada { border-left-width: 4px; }
.itin-chip--muerto { opacity: .72; }
.itin-chip-warn { color: var(--color-alerta-alto, #D7263D); flex: 0 0 auto; font-size: .8rem; }

.itin-dia-cuenta {
  margin-left: auto; margin-right: 4px; font-size: .66rem; font-weight: 800;
  padding: 1px 6px; border-radius: var(--radio-pill, 999px);
  background: var(--color-superficie-alt); color: var(--color-texto-suave);
}
.itin-dia-cuenta.es-parcial  { background: rgba(233, 148, 10, .2); color: var(--color-texto); }
.itin-dia-cuenta.es-completo { background: rgba(27, 122, 67, .2); color: var(--color-texto); }

.itin-otras-ciudades { display: flex; align-items: center; gap: 6px; font-size: .8rem; margin: 6px 0 0; font-weight: 600; }
.itin-res { display: flex; align-items: flex-start; gap: 9px; }
.itin-res-chk { flex: 0 0 auto; margin: 2px 0 0; }
.itin-res-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.itin-res--puesto { opacity: .55; cursor: not-allowed; }
.itin-repetir { display: grid; grid-template-columns: 1fr 90px; gap: 10px; align-items: end; margin-top: 10px; }
.itin-repetir select, .itin-repetir input { margin: 0; }
.cd-editar { border-top: 1px solid var(--color-borde-suave); margin-top: 14px; padding-top: 12px; }
@media (max-width: 560px) { .itin-repetir { grid-template-columns: 1fr; } }

/* ---- Inicio del promotor: la tienda de hoy y su gente (app/inicio.php) -----
   Va ANTES de "Tu actividad" a propósito: si el promotor ya hizo check-in, lo
   que necesita a mano no son sus registros de ayer, es con quién va a hablar. */
.tienda-hoy {
  margin: 0 0 18px; padding: 12px;
  border: 1px solid var(--color-borde-suave);
  border-left: 5px solid var(--color-alerta-bajo, #1B7A43);
  border-radius: var(--radio-md, 12px);
  background: var(--color-superficie);
}
.tienda-hoy-cab { display: flex; align-items: center; gap: 10px; }
.tienda-hoy-ic {
  width: 38px; height: 38px; flex: 0 0 auto; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.2rem; color: #fff; background: var(--color-alerta-bajo, #1B7A43);
}
.tienda-hoy-txt { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.tienda-hoy-kicker { font-size: .62rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; opacity: .6; line-height: 1.4; }
.tienda-hoy-txt strong { font-size: 1rem; line-height: 1.25; }
.tienda-hoy-sub { font-size: .74rem; opacity: .7; line-height: 1.3; }

/* Chips en vez de una frase: "2 personas anotadas en esta tienda. Toca para ver
   lo que sabemos de cada una" son dos renglones que se leen una sola vez. */
.th-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 8px; }
.th-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--txt-xs, .72rem); font-weight: 700; padding: 3px 9px;
  border-radius: var(--radio-pill, 999px);
  background: var(--color-superficie-alt); color: var(--color-texto);
}
.th-chip--fiesta { background: var(--color-alerta-fondo, #FFF1DC); }

.ah-lista { display: flex; flex-direction: column; gap: 7px; }
.ah-ficha { border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px); background: var(--color-superficie-alt); }
/* Cumple HOY: el dato deja de ser una ficha y pasa a ser una tarea del día. */
.ah-ficha--hoy { border-color: var(--color-energia, #E9940A); box-shadow: 0 0 0 1px var(--color-energia, #E9940A); }
/* El <summary> conserva su display por defecto. Ponerle flex hace que Chrome
   deje de tratarlo como el resumen del <details>. El flex va dentro. */
.ah-cab { padding: 9px 11px; cursor: pointer; list-style: none; user-select: none; }
.ah-cab-in { display: flex; align-items: center; gap: 9px; }
/* El triángulo por defecto, en LOS DOS motores. Safari de iOS ignora
   list-style en summary y necesita su pseudo-elemento propio: sin esta línea
   sale un ▶ delante del nombre, que es como se veía en el iPhone. */
.ah-cab::-webkit-details-marker { display: none; }
.ah-cab::marker { content: ''; }

/* Inicial + sello del perfil: se reconoce a la persona sin leer. */
.ah-ava {
  position: relative; width: 34px; height: 34px; flex: 0 0 auto;
  border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-size: .88rem; font-weight: 800; letter-spacing: .01em;
  background: var(--color-texto); color: var(--color-superficie);
}
.ah-ava-sello {
  position: absolute; right: -3px; bottom: -3px;
  width: 16px; height: 16px; border-radius: 50%;
  display: grid; place-items: center; font-size: .55rem;
  background: var(--color-energia, #E9940A); color: #22223B;
  border: 2px solid var(--color-superficie-alt);
}
.ah-cab-txt { display: flex; flex-direction: column; gap: 0; min-width: 0; flex: 1; }
.ah-cab-txt strong { font-size: var(--txt-md, .9rem); line-height: 1.3; }
.ah-rol { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; opacity: .6; }
.ah-tag {
  flex: 0 0 auto; font-size: .68rem; font-weight: 800; padding: 2px 8px;
  border-radius: var(--radio-pill, 999px);
  background: var(--color-alerta-fondo, #FFF1DC); color: var(--color-texto);
}
.ah-tag--hoy { background: var(--color-energia, #E9940A); color: #22223B; }
.ah-flecha { flex: 0 0 auto; opacity: .5; transition: transform .18s; }
.ah-ficha[open] .ah-flecha { transform: rotate(180deg); }
.ah-datos { margin: 0; padding: 2px 11px 4px 54px; display: flex; flex-direction: column; gap: 8px; }
.ah-datos > div { display: flex; flex-direction: column; gap: 1px; }
.ah-datos dt { font-size: .64rem; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; opacity: .6; display: flex; align-items: center; gap: 5px; }
.ah-datos dd { margin: 0; font-size: .85rem; line-height: 1.4; }
.ah-editar { margin: 6px 11px 11px 54px; }
/* ---- Formularios disponibles: reordenar arrastrando (admin) --------------- */
.fc-barra { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; margin: 0 0 10px; }
/* "Guardar orden" baja al pie y se queda pegado. Arriba era inútil: con los 12
   formularios reales la tabla mide 894 px, así que al mover una de las últimas
   filas el botón que confirma el cambio aparecía ~880 px por encima, fuera de
   pantalla, y como no hay beforeunload en el proyecto el orden arrastrado se
   perdía en silencio al irse. Va DESPUÉS del .tabla-scroll en el marcado: un
   sticky-bottom colocado antes de la tabla no hace absolutamente nada.
   El fondo es --color-fondo y NO --color-superficie: en formularios-config.php
   no hay ni una .tarjeta (grep: cero), la barra va directamente sobre la página,
   y con superficie quedaría una banda blanca sobre el crema.
   Se oculta con .oculto (display:none !important), nunca con el atributo
   [hidden]: el display: flex de aquí le ganaría y la barra no se iría. */
.fc-barra-pie {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 10px; padding: 10px 12px;
  background: var(--color-fondo);
  border-top: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-sm, 8px);
}
.fc-barra-pie-txt { font-size: .85rem; color: var(--color-texto-suave); }
/* La columna crece a 92 px porque ahora conviven el agarre (44) y los botones
   ▲▼ (44), que son la única vía con teclado para reordenar. */
.fc-col-mover { width: 92px; text-align: center; padding-right: 0; }
/* Arrastrar sólo funciona desde este agarre (SortableJS va con handle), y medía
   17x17 px. */
.fc-agarre {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; min-height: 44px;
  color: var(--color-texto-suave); cursor: grab; font-size: 1.05rem;
}
.fc-agarre:active { cursor: grabbing; }
/* Compromiso consciente, no se vende como 44x44: los dos botones apilados suman
   22+2+22 = 46 px de alto dentro de una fila de 71,5, con 44 px de ancho cada
   uno. Dos de 44x44 (90 px) harían crecer TODAS las filas y agravarían justo el
   desbordamiento que arregla la barra del pie. */
.fc-mover { display: inline-flex; flex-direction: column; gap: 2px; vertical-align: middle; }
.fc-mover-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; min-height: 22px;
  border: 1px solid var(--color-borde-suave); background: var(--color-superficie);
  color: var(--color-texto); border-radius: var(--radio-sm, 8px); cursor: pointer;
}
.fc-mover-btn:disabled { opacity: .4; cursor: default; }
.fc-fila-fantasma { opacity: .4; }


/* ---- "Acerca de" dentro del modal compartido de almacén ------------------- */
.ma-titulo { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.ma-acerca {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid var(--color-borde); background: var(--color-superficie);
  color: inherit; border-radius: var(--radio-pill, 999px);
  padding: 4px 10px; font-size: var(--txt-sec, .78rem); font-weight: 800; cursor: pointer;
  transition: background .15s, border-color .15s;
}
.ma-acerca:hover { border-color: var(--color-energia, #E9940A); }
.ma-acerca.es-abierto { background: var(--color-energia, #E9940A); border-color: var(--color-energia, #E9940A); color: #22223B; }
.ma-acerca-n { font-variant-numeric: tabular-nums; }
.ma-acerca-caja { display: flex; flex-direction: column; gap: 8px; margin: 10px 0 4px; }
.ma-per {
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px);
  background: var(--color-superficie-alt); padding: 10px 12px;
}
.ma-per--fiesta { border-left: 4px solid var(--color-energia, #E9940A); }
.ma-per-cab { display: flex; align-items: center; gap: 9px; }
.ma-per-ava {
  width: 30px; height: 30px; flex: 0 0 auto; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--txt-sm, .82rem); font-weight: 800;
  background: var(--color-texto); color: var(--color-superficie);
}
.ma-per-txt { display: flex; flex-direction: column; gap: 0; min-width: 0; flex: 1; }
.ma-per-txt strong { font-size: var(--txt-md, .9rem); line-height: 1.3; }
.ma-per-rol { font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; opacity: .6; }
.ma-per-tag { flex: 0 0 auto; font-size: .68rem; font-weight: 800; padding: 2px 8px; border-radius: var(--radio-pill, 999px); background: var(--color-alerta-fondo, #FFF1DC); }
.ma-per-dato { display: flex; flex-direction: column; gap: 0; margin-top: 7px; padding-left: 39px; }
.ma-per-k { font-size: .62rem; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; opacity: .6; }
.ma-per-dato > span:last-child { font-size: .84rem; line-height: 1.4; }
.ma-per-pie { font-size: var(--txt-micro, .7rem); margin: 8px 0 0; padding-left: 39px; }

/* ---- Panel "Acerca de": tiendas y calendario de cumpleaños ---------------- */
/* --- Acerca de: dónde falta, quién reporta y próximos cumpleaños ---------- */
.ap-h2 {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  font-size: 1.02rem; margin: 22px 0 10px;
}
.ap-h2-sub { font-size: var(--txt-sm, .82rem); font-weight: 600; }
.ap-todo-ok p { display: flex; align-items: center; gap: var(--esp-sm); flex-wrap: wrap; }

/* Cobertura por ciudad: la barra deja ver de un vistazo qué plaza va floja,
   que es lo que un porcentaje suelto en una lista no consigue. */
.ap-cob { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--esp-sm); }
.ap-cob li { display: grid; grid-template-columns: minmax(84px, 1fr) 2fr auto; align-items: center; gap: var(--esp-md); }
.ap-cob-ciudad { font-weight: 700; font-size: var(--txt-peq, .86rem); }
.ap-cob-barra {
  height: 9px; border-radius: 999px; overflow: hidden;
  background: var(--color-borde-suave, #E2DACB);
}
.ap-cob-barra > span { display: block; height: 100%; background: var(--color-alerta-bajo, #1B7A43); }
.ap-cob-cifra { font-size: .8rem; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Los huecos son una rejilla de fichas cortas: se recorren con la vista y se
   tocan para abrir el modal del almacén. */
.ap-huecos { display: grid; gap: var(--esp-sm); grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
.ap-hueco {
  display: flex; flex-direction: column; gap: 1px; text-align: left;
  padding: 10px 12px; border: 2px solid var(--color-texto); border-radius: var(--radio, 10px);
  background: var(--color-fondo-tarjeta, #fff); color: inherit; cursor: pointer;
  font: inherit; box-shadow: 2px 2px 0 var(--color-texto);
}
.ap-hueco:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--color-texto); }
.ap-hueco:focus-visible { outline: 2px solid var(--color-texto); outline-offset: 3px; }
.ap-hueco strong { font-size: .88rem; }
.ap-hueco span { font-size: .76rem; }
/* Amarillo del "a medias": tiene gente anotada, pero nadie que decida. */
.ap-hueco--medio { border-left-width: 7px; border-left-color: var(--color-alerta-medio, #A25C00); }

/* Tabla de quién reporta. */
.ap-tabla { font-size: .84rem; }
.ap-tabla th, .ap-tabla td { padding: 8px 10px; vertical-align: middle; }
.ap-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ap-th-sub {
  display: block; font-size: .67rem; font-weight: 500; text-transform: none;
  letter-spacing: 0; color: var(--color-texto-suave, #55556B); margin-top: 1px;
}
.ap-flojo { color: var(--color-alerta-alto, #D7263D); font-weight: 700; }
/* Hondura: el número solo ("2,3") no dice si eso es mucho o poco sobre 6. */
.ap-hondura { display: inline-flex; align-items: center; gap: var(--esp-sm); justify-content: flex-end; }
.ap-hondura-barra {
  width: 46px; height: 7px; border-radius: 999px; overflow: hidden; flex: 0 0 auto;
  background: var(--color-borde-suave, #E2DACB);
}
.ap-hondura-barra > span { display: block; height: 100%; background: var(--color-texto); }

/* Próximos cumpleaños. */
.ap-proximos { border-top: 5px solid var(--color-acento, #E9940A); }
.ap-prox-lista { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: var(--esp-sm); }
.ap-prox-lista li {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 0; border-bottom: 1px solid var(--color-borde-suave, #E2DACB);
}
.ap-prox-lista li:last-child { border-bottom: 0; }
.ap-prox-lista li.es-hoy { font-weight: 700; }
.ap-prox-cuando {
  flex: 0 0 auto; min-width: 92px; font-size: var(--txt-sm, .82rem); font-weight: 700;
  display: flex; flex-direction: column; line-height: 1.25;
}
.ap-prox-cuando span { font-weight: 500; font-size: var(--txt-xs, .72rem); }
.ap-prox-quien {
  flex: 1 1 190px; min-width: 0; text-align: left; background: none; border: 0;
  padding: 0; font: inherit; color: inherit; cursor: pointer;
  display: flex; flex-direction: column; gap: 1px;
}
.ap-prox-quien:hover strong { text-decoration: underline; }
.ap-prox-quien:focus-visible { outline: 2px solid var(--color-texto); outline-offset: 3px; border-radius: 5px; }
.ap-prox-quien span { font-size: .76rem; }
.ap-prox-aviso {
  flex: 0 0 auto; font-size: var(--txt-xs, .72rem); font-weight: 700; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 9px; border-radius: 999px;
  background: var(--color-alerta-fondo, #FFF1DC); color: var(--color-alerta-medio, #A25C00);
}
@media (prefers-color-scheme: dark) {
  .ap-prox-aviso { color: var(--color-alerta-alto-txt, #FF6B78); }
}
:root[data-theme="dark"] .ap-prox-aviso { color: var(--color-alerta-alto-txt, #FF6B78); }

/* "Ver las otras N": mismo gesto que la tabla plegada del termómetro. */
.ap-mas { margin-top: var(--esp-md); }
.ap-mas > summary {
  cursor: pointer; font-weight: 700; font-size: .84rem;
  display: inline-flex; align-items: center; gap: 6px;
}
.ap-mas > summary::-webkit-details-marker { display: none; }
.ap-mas > summary:focus-visible { outline: 2px solid var(--color-texto); outline-offset: 3px; border-radius: 6px; }
.ap-mas[open] > summary i { transform: rotate(180deg); }

.ap-vistas {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--esp-sm); margin: 0 0 16px;
}
.ap-vista {
  min-width: 0; display: grid; grid-template-columns: 38px minmax(0, 1fr);
  align-items: center; gap: 9px; padding: 11px 12px;
  color: inherit; text-decoration: none; border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-md, 12px); background: var(--color-superficie);
  transition: border-color .15s, transform .15s, box-shadow .15s;
}
.ap-vista > i {
  width: 38px; height: 38px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 11px; background: var(--color-superficie-alt); font-size: 1.1rem;
}
.ap-vista > span { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ap-vista strong { font-size: .84rem; line-height: 1.2; }
.ap-vista small { color: var(--color-texto-suave); font-size: .68rem; line-height: 1.25; }
.ap-vista:hover { border-color: var(--color-energia, #E9940A); transform: translateY(-1px); }
.ap-vista.is-activo {
  border: 2px solid var(--color-texto); padding: 10px 11px;
  box-shadow: var(--sombra-dura-sm); background: var(--color-superficie-alt);
}
.ap-vista.is-activo > i { background: var(--color-energia, #E9940A); color: #22223B; }
@media (max-width: 880px) { .ap-vistas { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) {
  .ap-vistas { display: flex; overflow-x: auto; padding: 0 2px 7px; scroll-snap-type: x proximity; }
  .ap-vista { flex: 0 0 210px; scroll-snap-align: start; }
}
.ap-tiendas { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: var(--esp-md); }
.ap-tienda {
  display: flex; align-items: center; gap: 11px; text-align: left;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 12px);
  background: var(--color-superficie); padding: 12px; cursor: pointer;
  transition: border-color .15s, transform .15s;
}
.ap-tienda:hover { border-color: var(--color-energia, #E9940A); transform: translateY(-2px); }
.ap-tienda-n {
  width: 40px; height: 40px; flex: 0 0 auto; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.05rem; font-weight: 800; font-variant-numeric: tabular-nums;
  background: var(--color-texto); color: var(--color-superficie);
}
.ap-tienda-txt { display: flex; flex-direction: column; gap: var(--esp-2xs); min-width: 0; flex: 1; }
.ap-tienda-txt strong { font-size: .92rem; line-height: 1.3; }
.ap-tienda-txt .text-suave { font-size: .74rem; }
.ap-tienda-tag { flex: 0 0 auto; font-size: var(--txt-micro, .7rem); font-weight: 800; padding: 2px 8px; border-radius: var(--radio-pill, 999px); background: var(--color-alerta-fondo, #FFF1DC); }

.ap-calendario-card { overflow: hidden; }
.ap-calendario-cab { display: flex; align-items: center; gap: 11px; margin-bottom: 15px; }
.ap-calendario-cab h2 { font-size: 1.05rem; }
.ap-calendario-cab p { font-size: .76rem; }
.ap-calendario-ic {
  width: 42px; height: 42px; flex: 0 0 auto; display: inline-flex; align-items: center;
  justify-content: center; border-radius: 12px; background: var(--color-texto);
  color: var(--color-superficie); font-size: 1.15rem;
}
.ap-mes-nav {
  display: flex; align-items: center; justify-content: center; gap: var(--esp-lg);
  margin: 0 0 14px; padding: 10px; border-radius: var(--radio-sm, 9px);
  background: var(--color-superficie-alt);
}
.ap-mes-nombre { font-size: 1.05rem; min-width: 190px; text-align: center; }
.ap-cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--esp-sm); }
.ap-cal-cab { font-size: .66rem; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; opacity: .6; text-align: center; padding-bottom: 4px; }
.ap-cal-dia {
  padding: 6px; border-radius: var(--radio-sm, 8px);
  border: 1px solid var(--color-borde-suave); background: var(--color-superficie);
  display: flex; flex-direction: column; gap: 4px;
}
.ap-cal-dia--fuera { background: none; border: 0; }
.ap-cal-dia--hoy { border-color: var(--color-energia, #E9940A); box-shadow: 0 0 0 1px var(--color-energia, #E9940A); }
.ap-cal-dia--fiesta { background: var(--color-alerta-fondo, #FFF1DC); }
.ap-cal-num { font-size: var(--txt-xs, .72rem); font-weight: 800; opacity: .65; }
/* Píldora de UNA línea. Con el almacén y la ciudad debajo del nombre, un día
   con dos cumpleaños medía el triple que sus vecinos y la rejilla dejaba de
   leerse como un calendario. La tienda vive en el title y en el modal. */
.ap-cumple {
  display: flex; align-items: center; gap: 5px; width: 100%; text-align: left;
  border: 0; background: var(--color-superficie); border-radius: var(--radio-sm, 6px);
  padding: 4px 6px; cursor: pointer; font-size: .68rem; line-height: 1.3;
}
.ap-cumple:hover { background: var(--color-texto); color: var(--color-superficie); }
.ap-cumple i { flex: 0 0 auto; font-size: var(--txt-sec, .78rem); }
.ap-cumple-nom {
  font-weight: 700; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Altura FIJA, no un mínimo. Con min-height, la fila entera se estiraba hasta
   el día más cargado y el calendario quedaba con filas de alturas distintas; el
   día de tres cumpleaños medía 110 px contra 92 px de sus vecinos. Con altura
   fija, el que acumule más hace scroll dentro de su casilla y la rejilla se
   mantiene regular, que es lo que hace que un calendario se lea como tal. */
.ap-cal-dia { height: 106px; max-height: 106px; overflow-y: auto; }
.ma-acerca-mas { align-self: flex-start; margin-top: 2px; }

/* La misma información como lista: el calendario de 7 columnas no cabe en un
   teléfono sin comerse los nombres, y quien lo consulta en la tienda usa el
   teléfono. Cada vista se muestra donde funciona, no se encoge la otra. */
.ap-cumple-lista { display: none; list-style: none; padding: 0; margin: 0; }
.ap-cumple-lista li { margin-bottom: 8px; }
.ap-cl-item {
  width: 100%; display: flex; align-items: center; gap: var(--esp-md); padding: 10px;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio-sm, 9px);
  background: var(--color-superficie); color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.ap-cumple-lista li.es-hoy .ap-cl-item { background: var(--color-alerta-fondo, #FFF1DC); border-color: var(--color-acento); }
.ap-cl-item:active { transform: scale(.99); }
.ap-cl-dia {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .88rem; font-weight: 800; font-variant-numeric: tabular-nums;
  background: var(--color-superficie-alt);
}
.ap-cl-txt { display: flex; flex-direction: column; gap: var(--esp-2xs); min-width: 0; flex: 1; }
.ap-cl-txt strong { font-size: .88rem; }
.ap-cl-txt .text-suave { font-size: var(--txt-xs, .72rem); }
.ap-cl-hoy { flex: 0 0 auto; font-size: var(--txt-micro, .7rem); font-weight: 800; }
.ap-cl-ir { flex: 0 0 auto; opacity: .5; }
@media (max-width: 760px) {
  .ap-cal { display: none; }
  .ap-cumple-lista { display: block; }
  .ap-mes-nombre { min-width: 140px; font-size: .95rem; }
  .ap-calendario-card { padding-inline: 12px; }
}

/* ---- Formularios con ramas: el interruptor como pestañas ------------------ */
.ff-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.ff-tab { flex: 1 1 140px; position: relative; cursor: pointer; }
.ff-tab input { position: absolute; opacity: 0; pointer-events: none; }
.ff-tab span {
  display: block; text-align: center; padding: 10px 12px;
  border: 2px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px);
  background: var(--color-superficie); font-weight: 700; font-size: .88rem;
  transition: border-color .15s, background .15s;
}
.ff-tab input:checked + span {
  border-color: var(--color-energia, #E9940A);
  background: var(--color-energia, #E9940A);
  color: #22223B;
}
/* Foco visible con teclado: el input real está oculto, así que el anillo lo
   tiene que pintar la etiqueta o no habría ninguna señal al tabular. */
.ff-tab input:focus-visible + span { outline: 2px solid var(--color-texto); outline-offset: 2px; }
.ff-campo--interruptor { margin-bottom: 18px; }

/* Cuando se llega desde "Acerca de", las personas van ARRIBA del histórico. */
.ma-acerca-caja--primero { order: -1; }
.modal-ts-inner:has(.ma-acerca-caja--primero) { display: flex; flex-direction: column; }
.modal-ts-inner:has(.ma-acerca-caja--primero) > .ma-titulo { order: -2; }

/* ---- Filtros del panel "Acerca de" --------------------------------------- */
.ap-filtros { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--esp-md); margin: 0 0 16px; }
.ap-filtro { display: flex; flex-direction: column; gap: var(--esp-xs); min-width: 165px; }
.ap-filtro label { font-size: var(--txt-micro, .7rem); font-weight: 800; text-transform: uppercase; letter-spacing: .04em; opacity: .65; display: flex; align-items: center; gap: var(--esp-xs); margin: 0; }
.ap-filtro select { margin: 0; }
.ap-limpiar { align-self: flex-end; }

/* ---- Rotación: bandejas priorizadas ------------------------------------- */
.rot-bandejas {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--esp-md); margin: 0 0 16px;
}
.rot-bandeja {
  min-width: 0; display: grid; grid-template-columns: 42px minmax(0, 1fr) auto;
  align-items: center; gap: 10px; padding: 12px 14px;
  color: inherit; text-decoration: none; border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-md, 12px); background: var(--color-superficie);
  transition: transform .15s, border-color .15s, box-shadow .15s;
}
.rot-bandeja:hover { transform: translateY(-1px); border-color: var(--color-texto); }
.rot-bandeja-ic {
  width: 42px; height: 42px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 12px; background: var(--color-superficie-alt); font-size: 1.15rem;
}
.rot-bandeja > span:nth-child(2) { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.rot-bandeja strong { font-size: .88rem; line-height: 1.2; }
.rot-bandeja small { color: var(--color-texto-suave); font-size: var(--txt-micro, .7rem); }
.rot-bandeja > b {
  min-width: 30px; height: 30px; padding: 0 7px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--color-superficie-alt); font-size: .8rem;
  font-variant-numeric: tabular-nums;
}
.rot-bandeja.is-activa { border: 2px solid var(--color-texto); padding: 11px 13px; box-shadow: var(--sombra-dura-sm); }
.rot-bandeja.is-activa .rot-bandeja-ic { background: var(--color-texto); color: var(--color-superficie); }
.rot-bandeja--alerta.is-activa .rot-bandeja-ic { background: var(--color-alerta-alto, #D7263D); color: #fff; }
.rot-bandeja--revision.is-activa .rot-bandeja-ic { background: var(--color-alerta-medio, #A25C00); color: #fff; }
.rot-panel { border-top: 5px solid var(--color-texto); }
.rot-panel--alerta { border-top-color: var(--color-alerta-alto, #D7263D); }
.rot-panel--revision { border-top-color: var(--color-alerta-medio, #A25C00); }
.rot-tabla td:first-child { font-weight: 700; }
@media (max-width: 760px) {
  .rot-bandejas { grid-template-columns: 1fr; gap: var(--esp-sm); }
  .rot-bandeja { padding-block: 9px; }
  .rot-bandeja.is-activa { padding-block: 8px; }
  .rot-tabla td:nth-child(2), .rot-tabla td:nth-child(4) { display: none !important; }
}

/* Fachada del modal de almacén: alto fijo y recorte centrado, para que llene
   su hueco en vez de dejar bandas de fondo alrededor. Va sobre la <img> de
   siempre y NO sobre un envoltorio nuevo: así el HTML y el JS no cambian, y si
   esta hoja llega tarde al navegador de alguien la foto se sigue viendo (sin
   recortar) en lugar de desaparecer. */
.ma-foto-cover {
  height: 190px; max-height: 190px; width: 100%;
  object-fit: cover; object-position: center; cursor: zoom-in;
}
/* Salvo cuando la imagen no cargó: ahí manda la regla de img-error, que la
   muestra completa. Se repite el selector para ganarle en especificidad. */
img.ma-foto-cover.img-error { object-fit: contain; }

/* ---- Ruta foránea: "estoy en tránsito" (app/check-in.php) ----------------- */
.tr-caja {
  margin: 18px 0 0; padding: 14px;
  border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-md, 12px);
  background: var(--color-superficie);
}
/* Viajando AHORA: se ve distinto de un botón en reposo, porque es un estado. */
.tr-caja--activo { border-left: 5px solid var(--color-energia, #E9940A); background: var(--color-alerta-fondo, #FFF1DC); }
.tr-abrir summary { list-style: none; cursor: pointer; }
.tr-abrir summary::-webkit-details-marker { display: none; }
.tr-abrir summary::marker { content: ''; }
.tr-sum, .tr-cab { display: flex; align-items: center; gap: 11px; }
.tr-ic {
  width: 38px; height: 38px; flex: 0 0 auto; border-radius: 11px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 1.15rem; background: var(--color-superficie-alt); color: var(--color-texto);
}
.tr-ic--vivo { background: var(--color-energia, #E9940A); color: #22223B; }
.tr-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.tr-txt strong { font-size: .95rem; }
.tr-txt .text-suave { font-size: var(--txt-sec, .78rem); line-height: 1.3; }
.tr-flecha { flex: 0 0 auto; opacity: .55; transition: transform .18s; }
.tr-abrir[open] .tr-flecha { transform: rotate(180deg); }
.tr-form { margin-top: 14px; }
.tr-cab { margin-bottom: 10px; }
/* En camino, en Status Day. */
.sd-transitos { display: flex; flex-direction: column; gap: var(--esp-sm); margin-bottom: 22px; }
.sd-transito {
  display: flex; align-items: center; gap: 11px; padding: 10px 12px;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px);
  background: var(--color-superficie);
}
.sd-transito--vivo { border-left: 4px solid var(--color-energia, #E9940A); }
.sd-transito-ic {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-superficie-alt); font-size: 1rem;
}
.sd-transito-txt { display: flex; flex-direction: column; gap: 0; min-width: 0; flex: 1; }
.sd-transito-txt strong { font-size: var(--txt-md, .9rem); }
.sd-transito-txt .text-suave { font-size: .75rem; line-height: 1.35; }
.sd-transito-dur { flex: 0 0 auto; font-variant-numeric: tabular-nums; }

/* Modelo cuya MARCA está dada de baja. Se puede elegir igual —esconderlo hacía
   desaparecer productos que la gente usaba— pero se señala en ámbar para que la
   decisión se tome con el dato delante. */
.modelo-baja-chip {
  display: inline-block; margin-left: 6px;
  font-size: .64rem; font-weight: 800; padding: 2px 7px;
  border-radius: var(--radio-pill, 999px);
  background: var(--color-alerta-fondo, #FFF1DC); color: var(--color-texto);
  border: 1px solid var(--color-energia, #E9940A);
}

/* "Ver más modelos": el tope de la lista por marca dejó de ser una pared. */
.modelos-tope {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 10px; margin: 0 0 12px; padding: 10px 12px;
  border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px);
  background: var(--color-superficie-alt);
}
.modelos-tope .ayuda { flex: 1 1 200px; }
/* Chip de categoría mientras trae el resto del catálogo. */
.modelo-cat-filtro.esta-cargando { opacity: .6; pointer-events: none; }

/* ============================================================================
   Comparativo · modo "Último registro"
   La matriz puede mezclar meses a propósito: cada celda trae el último precio
   conocido de esa tienda, sea de este mes o de hace cuatro. El rótulo de mes
   NO es decoración — es lo único que distingue un precio de ayer de uno viejo,
   y sin él este modo se convertiría en una forma elegante de mentir.
   ============================================================================ */
.celda-precio--arrastrada { position: relative; }
.celda-precio-edad {
  display: block; font-size: .62rem; font-weight: 700; line-height: 1.1;
  letter-spacing: .02em; text-transform: uppercase; opacity: .78;
  margin-top: 1px;
}
/* Borde izquierdo tenue: se lee de un vistazo qué columna viene arrastrada sin
   tener que leer cada rótulo. */
.celda-precio--arrastrada::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--color-texto-suave); opacity: .45; border-radius: 2px 0 0 2px;
}

/* ============================================================================
   Flooring · comparación de share entre dos meses
   La variación va en PUNTOS PORCENTUALES y los frentes aparte: una marca puede
   perder share y ganar frentes si el mueble creció, y son conclusiones
   opuestas. Por eso el chip lleva las dos cifras y no una sola.
   ============================================================================ */
.fl-comp-filtros {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
  margin: 0 0 12px; font-size: var(--txt-peq, .86rem);
}
.fl-comp-filtros label { font-weight: 700; color: var(--color-texto-suave); margin: 0; }
.fl-comp-filtros select {
  /* `width: auto` es lo que hace que quepan en UNA línea. Los <select> del panel
     llevan width:100% por regla global, así que cada uno se comía los 984 px de
     la tarjeta y los dos desplegables ocupaban 146 px de alto, en cinco renglones,
     para elegir dos meses. */
  width: auto; min-width: 0; flex: 0 0 auto;
  min-height: 34px; padding: 4px 8px; font: inherit; font-size: var(--txt-peq, .86rem); font-weight: 700;
  border: 1.5px solid var(--color-borde); border-radius: var(--radio-sm, 8px);
  background: var(--color-superficie); color: var(--color-texto);
}
/* La leyenda explica la lectura UNA vez, para que cada fila no tenga que
   arrastrar "pp" y "f" —jerga que obliga a traducir mentalmente 14 veces—. */
.fl-leyenda {
  margin: -2px 0 12px; font-size: var(--txt-sec, .78rem); line-height: 1.45;
  color: var(--color-texto-suave);
}
.fl-leyenda b { color: var(--color-texto); font-weight: 700; }
.fl-total-var { font-style: normal; opacity: .8; }
.fl-cat-aviso {
  display: flex; align-items: center; gap: 6px;
  margin: 2px 0 8px; font-size: var(--txt-sec, .78rem); line-height: 1.35;
  color: var(--color-alerta-medio, #A25C00);
}
/* La fila de una marca que ya no está en el piso: se muestra igual —es el cambio
   más grande que puede haber— pero atenuada para que no compita con las vivas. */
.fl-marca--fuera .fl-marca-nom,
.fl-marca--fuera .fl-marca-pct { opacity: .62; }
/* Dónde estaba antes, sobre la propia barra. */
.fl-barra { position: relative; }
.fl-barra-antes {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  background: var(--color-texto); opacity: .55; border-radius: 1px;
  pointer-events: none;
}
.fl-var {
  display: inline-flex; align-items: center; gap: 3px; white-space: nowrap;
  font-size: .74rem; font-weight: 800; font-variant-numeric: tabular-nums;
  padding: 2px 7px; border-radius: var(--radio-pill, 999px);
  border: 1px solid currentColor;
}
.fl-var small { font-weight: 700; opacity: .75; font-size: .92em; }
/* El verde de la casa es --color-alerta-bajo ("todo bien"), no un token de
   "éxito": ese no existe y la variante inventada habría caído siempre al
   literal, sin invertirse nunca en oscuro. */
.fl-var--sube  { color: var(--color-alerta-bajo); }
.fl-var--baja  { color: var(--color-alerta-alto); }
.fl-var--entra { color: var(--color-alerta-bajo); }
.fl-var--sale  { color: var(--color-alerta-alto); }
.fl-var--igual,
.fl-var--nd    { color: var(--color-texto-suave); border-style: dashed; }
@media (prefers-color-scheme: dark) {
  /* Los tokens de éxito/alerta no se invierten solos: sin esto los chips quedan
     por debajo de 4,5:1 sobre el fondo oscuro de la tarjeta. */
  .fl-var--sube, .fl-var--entra { color: var(--color-alerta-bajo-txt, #57C98A); }
  .fl-var--baja, .fl-var--sale  { color: var(--color-alerta-alto-txt, #FF6B78); }
  .fl-barra-antes { opacity: .7; }
}

/* Densidad de la tarjeta de share: era casi una pantalla entera de alto para
   14 filas de una barra cada una. Se aprieta la cabecera de categoría y el
   espacio entre categorías; el contenido no cambia, sólo deja de sobrar aire. */
.fl-share { gap: var(--esp-lg) 18px; margin-top: var(--esp-xs); }
.fl-cat-cab { margin-bottom: var(--esp-sm); padding-bottom: 4px; }
.fl-cat-aviso { margin: 0 0 6px; }
.fl-var { padding: 1px 6px; font-size: var(--txt-micro); }
/* El porcentaje y la variación son dos números seguidos y sin separación se
   leían pegados ("43% · 3 14"). Se aleja el chip y se apaga el conteo de
   frentes, que es el dato secundario de la fila. */
.fl-marca-pct { display: inline-flex; align-items: baseline; gap: var(--esp-xs); }
.fl-marca-pct .text-suave { font-weight: 600; font-size: .92em; }
.fl-marca .fl-var { margin-left: 4px; }
@media (prefers-color-scheme: dark) {
  /* --color-alerta-medio NO está entre los tokens que se invierten: el ámbar
     oscuro pensado para fondo claro daba 3,19:1 sobre la tarjeta oscura. Se usa
     la variante de texto de energía, que sí se invierte. Cuarta vez que aparece
     esta trampa; por eso la corrección va por token y no pintando un hex aquí. */
  .fl-cat-aviso { color: var(--color-energia-txt, #FF8A4C); }
}

/* ============================================================================
   BARRIDO DE CONTRASTE · correcciones de tema oscuro
   ----------------------------------------------------------------------------
   Salieron de un barrido de la hoja entera, no de mirar pantallas: se
   resolvieron los tokens en los dos temas y se calculó el contraste de las 200
   reglas que declaran fondo y color juntos. Nueve se veían perfectas en claro y
   se rompían en oscuro, todas por la misma causa — un lado sale de un token que
   SE INVIERTE y el otro es un valor fijo, así que en oscuro se juntan.
   Es la sexta vez que aparece este patrón; por eso se corrige de una tacada y
   con tokens, no con hex sueltos.
   ============================================================================ */
@media (prefers-color-scheme: dark) {

  /* --- Superficies "tinta con texto claro" --------------------------------
     background: var(--color-texto) + color fijo casi blanco.
     En claro es navy con crema (14,2:1). En oscuro el fondo se vuelve crema y
     el texto sigue casi blanco: 1,05:1. Se mantiene la intención —una
     superficie oscura destacada— usando la superficie elevada del tema. */
  .admin-sidebar,
  /* El barral del promotor se quedo fuera de esta misma regla cuando se escribio:
     mismo patron, mismo fallo (crema sobre crema, 1,05:1), y solo se ve en
     escritorio, que es donde menos se mira la app de campo. */
  .app-sidebar { background: var(--color-superficie-alt); }
  .admin-sidebar a,
  .app-sidebar a { color: var(--color-texto); }

  /* --- Fichas y contadores rellenos --------------------------------------
     Piezas pequeñas y macizas (inicial del avatar, número de parada, CTA de
     ruta). Aquí no vale oscurecer el fondo: perderían el peso visual que las
     hace legibles de un vistazo. Se usa el patrón que ya estrenaron las
     pastillas Sí/No: naranja de marca con tinta navy fija. */
  .avatar-inicial,
  .marca-tile .inicial,
  .ruta-orden {
    background: var(--color-energia, #FF5A1F);
    color: #22223B;
  }
  /* La tarjeta de ruta es grande: en naranja pleno sería un bloque gritando en
     una pantalla oscura. Superficie elevada y texto del tema. */
  .ruta-cta { background: var(--color-superficie-alt); color: var(--color-texto); }

  /* --- Rellenos ámbar con texto blanco -----------------------------------
     --color-acento-fuerte pasa de #9A6103 (oscuro) a #F0B24A (claro) al invertir
     el tema, así que el blanco encima deja de leerse. El ámbar no cambia de
     familia: lo que tiene que cambiar es el texto. */
  .sd-reab-badge { color: #22223B; }
  .marca-tile.activa { color: #22223B; }

  /* --- Rojo y verde de marca sobre superficie -----------------------------
     --color-alerta-alto y --color-alerta-bajo NO se invierten (son el color de
     relleno, pensado para llevar texto blanco encima). Usados como COLOR DE
     TEXTO sobre la superficie oscura se quedan cortos; para eso existen sus
     variantes -txt. */
  .bc-merge-chip { color: var(--color-alerta-bajo-txt, #57C98A); border-color: var(--color-alerta-bajo-txt, #57C98A); }
  .context-clear { color: var(--color-alerta-alto-txt, #FF6B78); }
}

/* ============================================================================
   HOJAS que sustituyen a los <dialog> del promotor
   ----------------------------------------------------------------------------
   Siete diálogos nativos pasaron a overlays propios (corregir precio, actualizar
   precio, borrar notificación, foto de fachada, escáner y producto reconocido).
   El motivo es el de siempre en esta app: `<dialog>` da problemas reales en el
   WebView de Android que usan los promotores.

   El contenido interior NO cambió de clase —sigue siendo `.modal-ts`,
   `.ntf-confirm` o `.escaner-modal`—, así que aquí sólo hace falta que esas
   cajas se comporten como la hoja que ahora las envuelve.
   ============================================================================ */
.modal-ts-ov > .modal-ts,
.escaner-ov > .escaner-modal {
  /* Dentro de .hoja-ov el posicionamiento lo pone el contenedor: la caja sólo
     tiene que ocupar su ancho y poder scrollear si no cabe. */
  position: static; margin: 0; padding: 0; border: 0;
  width: 100%; max-width: 560px; max-height: 88vh; overflow-y: auto;
  background: var(--color-superficie); color: var(--color-texto);
}
.escaner-ov > .escaner-modal { max-width: 640px; }
/* El ::backdrop del <dialog> lo pintaba el navegador; ahora lo pone la hoja. */
.modal-ts-ov, .escaner-ov { padding: 0; }

/* Fila del feed cuyo registro se acaba de anular: se marca en el acto, sin
   esperar a la recarga, para que el promotor vea que su anulación entró. */
.feed-anulado { opacity: .72; }
.feed-anulado .feed-precio {
  text-decoration: line-through;
  color: var(--color-texto-suave);
}

/* ---- Constructor de reportes: sitio de verdad para el resultado ------------
   PRIMER INTENTO, y por qué se deshizo: acoté el constructor al alto de la
   ventana para que la página no tuviera scroll. Resultado en una pantalla normal:
   la paleta se desplazaba por dentro, los estantes también, y a la tabla le
   quedaban cuatro filas. Tres barras de scroll y nada cómodo. Peor todavía, mover
   el scroll al lienzo dejó a .rep-tabla-wrap sin altura y con eso la cabecera
   pegajosa dejó de activarse: cientos de cifras sin rótulo de columna.

   AHORA AL REVÉS: el resultado tiene un mínimo generoso y, si en la ventana cabe
   más, se estira hasta llenarla (--rep-alto lo calcula el JS con la posición real
   del bloque). Si aun así no cabe, la página hace scroll — que es lo normal y no
   molesta a nadie. Lo que NO se negocia es que la tabla se lea. */
.rep-preview { min-height: max(520px, var(--rep-alto, 520px)); display: flex; flex-direction: column; }
.rep-preview .rep-vista { flex: 0 0 auto; }
.rep-preview .rep-lienzo { flex: 1 1 auto; min-height: 0; }

/* El scroll vive AQUÍ, no en el lienzo: es el requisito del `position: sticky` de
   la cabecera. Sin altura propia el thead nunca se pega y se va con el scroll. */
.rep-tabla-wrap { max-height: max(420px, calc(var(--rep-alto, 560px) - 118px - var(--rep-banda, 0px))); }
.rep-chart-lienzo { height: max(360px, calc(var(--rep-alto, 480px) - 118px - var(--rep-banda, 0px))); }

@media (min-width: 861px) {
  /* Los estantes sí se acotan: con cuatro medidas en VALORES crecían hasta ~350 px
     y ese hueco sale del resultado. Dos filas de chips y scroll dentro. */
  .rep-zone { max-height: 152px; display: flex; flex-direction: column; }
  .rep-zone-drop { overflow-y: auto; min-height: 0; overscroll-behavior: contain; }
  /* La paleta tampoco puede crecer sin fin, pero con el menú de fuentes plegado
     las 23 dimensiones de sell in caben sin desplazarse. */
  .rep-paleta:not(.rep-paleta--oculta) { max-height: 68vh; overflow-y: auto; overscroll-behavior: contain; }
}

/* Ampliado manda la ventana entera y los topes de arriba no aplican. */
.rep-preview.es-amplia .rep-lienzo { overflow: hidden; }
.rep-preview.es-amplia .rep-tabla-wrap { max-height: none; height: 100%; }
.rep-preview.es-amplia .rep-chart-wrap { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.rep-preview.es-amplia .rep-chart-lienzo { flex: 1 1 auto; height: auto; min-height: 0; }

/* ---- Menu de fuentes plegable + chips densos -------------------------------
   MEDIDO en 1280x720 con la fuente "Sell in" activa: la paleta necesita 441 px
   y solo le quedaban 172, porque las SEIS opciones de fuente ocupan la mitad del
   panel. Diez de las dieciseis dimensiones quedaban fuera de vista.

   Una vez elegida la fuente, las otras cinco no aportan nada: el menu se pliega
   a la activa y se reabre pulsandola. Eso devuelve ~170 px a las dimensiones, que
   es lo que el usuario necesita ver para poder arrastrar. En pantallas altas
   (>900 px) no hace falta y el menu se queda desplegado. */
.rep-fuente-cambiar {
  display: none;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 4px 10px;
  border: 0;
  background: transparent;
  color: var(--color-texto-suave);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  cursor: pointer;
}
.rep-fuente-cambiar:hover { color: var(--color-acento-fuerte); }
.rep-fuente-cambiar:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: 2px; border-radius: var(--radio-sm, 8px); }

.rep-fuente--plegado .rep-fuente-op:not(.activo) { display: none; }
.rep-fuente--plegado + .rep-fuente-cambiar,
.rep-fuente-cambiar.se-usa { display: flex; }

@media (min-width: 861px) and (max-height: 900px) {
  /* Chips mas densos: 40 px de alto por chip son 8 filas = 440 px solo de
     dimensiones. A 34 px la misma lista baja a ~330 y entra entera. Sigue por
     encima del minimo comodo para arrastrar con el raton. */
  .rep-chip { padding: 5px 9px; min-height: 34px; font-size: var(--txt-sec, .78rem); }
  .rep-campos { gap: 6px; }
  .rep-panel-tit { font-size: var(--txt-xs, .72rem); }
}

/* ---- Las tres fuentes de sell en una sola fila ----------------------------
   Sell in, Sell out y el cruce ocupaban tres filas del menu para ser variantes
   del mismo dato. Juntas bajo un rotulo comun ocupan una, y esas dos filas son
   justo las que le faltaban a las dimensiones para caber. */
.rep-fuente-sell { padding: 2px 0 0; }
.rep-fuente-sell-tit {
  display: block;
  padding: 0 12px 4px;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--color-texto-suave);
}
.rep-fuente-sell-ops { display: flex; gap: 4px; padding: 0 6px; }
.rep-fuente-op--corta {
  width: auto;
  flex: 1 1 0;
  min-width: 0;
  justify-content: center;
  gap: 5px;
  padding: 7px 4px;
  font-size: var(--txt-sec, .78rem);
}
/* El icono aguanta; el rotulo se recorta antes que desbordar la columna. */
.rep-fuente-op--corta i { flex: 0 0 auto; }
.rep-fuente-op--corta span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Plegado, solo sobrevive la activa: si es una de las tres, su fila se queda y
   el rotulo del grupo tambien; si no, el bloque entero desaparece. */
.rep-fuente--plegado .rep-fuente-sell:not(:has(.activo)) { display: none; }
.rep-fuente--plegado .rep-fuente-sell-tit { display: none; }

/* Enlace al resto de plantillas cuando la hoja viene filtrada por fuente. */
.rep-sheet-todas { padding: 10px 4px 2px; text-align: center; }

/* ---- Flooring: sell in / sell out de la tienda -----------------------------
   Mismo lenguaje que el resto de la pantalla: cabecera con filete inferior como
   .fl-cat-cab, pastillas como .fl-hm-tab y la cifra en tabular-nums. Dos columnas
   en cuanto hay sitio, porque se leen COMPARANDO una contra otra: apiladas, para
   ver cuánto compró y cuánto vendió hay que recordar el primer número. */
.fl-sell {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
  margin-bottom: 18px;
}
@media (min-width: 700px) { .fl-sell { grid-template-columns: 1fr 1fr; } }
/* grid-auto-rows:1fr las iguala: la de sell in lleva una línea más de pie y sin
   esto quedaban de dos alturas, que es justo lo que se pidió corregir. */
.fl-sell { grid-auto-rows: 1fr; }
.fl-sell-card { display: flex; flex-direction: column; gap: var(--esp-md); margin: 0; }
.fl-sell-cab {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  flex-wrap: wrap; padding-bottom: 6px;
  border-bottom: 2px solid var(--color-borde-suave);
}
.fl-sell-cab h3 { font-size: 1rem; display: flex; align-items: center; gap: var(--esp-sm); }
.fl-sell-cifra {
  margin: 0; font-size: 2.3rem; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.fl-sell-pie { margin: 0; font-size: var(--txt-peq); line-height: 1.45; margin-top: auto; }
.fl-sell-per { display: block; font-variant-numeric: tabular-nums; opacity: .8; margin-top: var(--esp-xs); }
/* Pastillas iguales a .fl-hm-tab, que es el conmutador que ya vive en esta pantalla. */
.fl-sell-uni { display: inline-flex; gap: var(--esp-sm); }
.fl-sell-uni-op {
  display: inline-flex; align-items: center; padding: 4px 11px;
  border: 1.5px solid var(--color-borde-suave); border-radius: 999px;
  background: var(--color-superficie); color: var(--color-texto);
  font-size: .74rem; font-weight: 700; cursor: pointer;
  transition: background var(--dur-1), color var(--dur-1), border-color var(--dur-1);
}
.fl-sell-uni-op:hover { border-color: var(--color-acento); }
.fl-sell-uni-op.activo { background: var(--color-texto); color: var(--color-superficie); border-color: var(--color-texto); }
.fl-sell-uni-op:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: 2px; }
.fl-sell-uni-fija {
  font-size: var(--txt-micro, .7rem); font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--color-texto-suave);
}

/* ---- Reporte: ampliar el resultado a pantalla completa ---------------------
   El constructor reparte la ventana entre la paleta, los estantes y el
   resultado, así que a la tabla le quedan cuatro o cinco filas. Con quince
   columnas eso no se lee, y la salida era exportar a Excel sólo para mirar.
   Ampliado, el resultado toma toda la ventana; el resto sigue montado debajo,
   así que al volver no se pierde nada de lo que se estaba armando. */
.rep-ampliar { margin-left: auto; }
.rep-preview.es-amplia {
  position: fixed;
  inset: 0;
  z-index: 60;
  margin: 0;
  border-radius: 0;
  background: var(--color-fondo, #F7F3EB);
  padding: 14px 18px 18px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* min-height:0 otra vez: sin esto el lienzo se niega a encoger y la tabla
   desborda la ventana en vez de desplazarse por dentro. */
.rep-preview.es-amplia .rep-lienzo { flex: 1 1 auto; min-height: 0; overflow: auto; }
.rep-preview.es-amplia .rep-tabla-wrap { max-height: none; }
/* Con el resultado ampliado el fondo no debe desplazarse detrás. */
body.rep-sin-scroll { overflow: hidden; }

/* El "Ampliar" viejo vivía DENTRO de la tarjeta del gráfico, así que en vista
   Tabla no existía — que es justo lo que hacía falta ampliar. El nuevo está en la
   barra de vistas y sirve para las dos, así que el viejo se oculta para no dejar
   dos botones que hacen lo mismo. Su script sigue ahí y es inofensivo. */
.rep-chart-cab .rep-ampliar { display: none; }

/* ---- Asistente de almacén padre (admin/cliente-padre.php) ------------------
   Tres pasos siempre a la vista: saber cuántos quedan es la mitad de lo que hace
   soportable un asistente. */
.wiz-pasos { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; padding: 0; margin: 0 0 14px; }
.wiz-paso {
  display: inline-flex; align-items: center; gap: 7px; padding: 6px 13px;
  border: 1.5px solid var(--color-borde-suave); border-radius: 999px;
  background: var(--color-superficie); color: var(--color-texto-suave);
  font-size: .8rem; font-weight: 700;
}
.wiz-paso-n {
  display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%;
  background: var(--color-superficie-alt); font-size: var(--txt-xs, .72rem); font-variant-numeric: tabular-nums;
}
.wiz-paso.es-actual { border-color: var(--color-texto); background: var(--color-texto); color: var(--color-superficie); }
.wiz-paso.es-actual .wiz-paso-n { background: rgba(255,255,255,.22); color: inherit; }
.wiz-paso.es-hecho { border-color: var(--color-acento); color: var(--color-acento-fuerte); }

.wiz-caja { max-width: 900px; }
.wiz-acc { display: flex; gap: 10px; align-items: center; margin-top: 18px; }
.wiz-acc .btn-primario { margin-left: auto; }
.wiz-buscar { display: flex; gap: 8px; margin: 12px 0; }
.wiz-buscar-input { flex: 1; margin: 0; }
/* La lista se desplaza por dentro: con 300 candidatos, el botón de continuar
   quedaba a un scroll de distancia y parecía que no existía. */
.wiz-lista { max-height: 46vh; overflow-y: auto; border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md); }
.wiz-item { display: flex; gap: 10px; align-items: flex-start; padding: 9px 12px; cursor: pointer; }
.wiz-item + .wiz-item { border-top: 1px solid var(--color-borde-suave); }
.wiz-item:hover { background: var(--color-superficie-alt); }
.wiz-item input { margin-top: 3px; flex: 0 0 auto; }
.wiz-item-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.wiz-item-txt .text-suave { font-size: .8rem; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.wiz-arbol { border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md); padding: 14px; }
.wiz-arbol-raiz { margin: 0 0 12px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.wiz-arbol-raiz .text-suave { font-size: var(--txt-sm, .82rem); }
/* Sangría + filete: el árbol se lee de un vistazo sin dibujar líneas. */
.wiz-arbol-madre { border-left: 3px solid var(--color-acento); padding: 4px 0 4px 12px; margin-left: 6px; }
.wiz-arbol-madre + .wiz-arbol-madre { margin-top: 12px; }
.wiz-arbol-nom { margin: 0 0 4px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.wiz-arbol-tiendas { margin: 0; padding-left: 18px; font-size: .84rem; }
.wiz-arbol-tiendas li + li { margin-top: 2px; }
.wiz-arbol-vacio { margin: 0; font-size: var(--txt-sm, .82rem); }


/* Selector de periodo y gráfico dentro de las tarjetas de sell in / sell out. */
.fl-sell-ctrl { display: flex; align-items: center; gap: var(--esp-sm); flex-wrap: wrap; }
.fl-sell-rango { margin: 0; font-size: var(--txt-sec); padding: 4px 8px; }
/* Alto fijo y modesto: el protagonista es la cifra; el gráfico da el contexto. */
.fl-sell-chart { position: relative; height: 150px; margin-top: var(--esp-2xs); }

/* ---- Filtros de marca y categoría dentro de las tarjetas de sell -----------
   Las marcas van en un desplegable con casillas y NO en una fila de botones: con
   nueve marcas de nombre largo la fila ocupaba tres líneas y empujaba la cifra y
   el gráfico fuera de la tarjeta. Plegado ocupa una línea. */
.fl-sell-filtros { display: flex; flex-wrap: wrap; align-items: center; gap: var(--esp-sm); }
.fl-sell-cat { margin: 0; font-size: var(--txt-sec); padding: 5px 8px; max-width: 100%; }

.fl-drop { position: relative; }
/* El botón imita a un <select>: mismo alto, misma flecha, mismo peso visual, para
   que se reconozca como desplegable de un vistazo. */
.fl-drop-btn {
  display: inline-flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 6px 10px; min-width: 178px;
  border: 1.5px solid var(--color-borde); border-radius: var(--radio-sm, 8px);
  background: var(--color-superficie); color: var(--color-texto);
  font-size: var(--txt-sm, .82rem); font-weight: 600; cursor: pointer; list-style: none;
}
.fl-drop-btn::-webkit-details-marker { display: none; }
.fl-drop-btn:hover { border-color: var(--color-acento); }
.fl-drop-btn:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: 2px; }
.fl-drop.tiene-seleccion .fl-drop-btn { border-color: var(--color-acento); font-weight: 700; }
.fl-drop[open] .fl-drop-btn i { transform: rotate(180deg); }
.fl-drop-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* El panel imita la LISTA de un desplegable nativo: pegado al botón, filas a todo
   lo ancho y sin adornos. Flota para que abrirlo no mueva la cifra ni el gráfico. */
.fl-drop-panel {
  position: absolute; z-index: 20; top: calc(100% + 2px); left: 0;
  min-width: 100%; width: max-content; max-width: 320px;
  border: 1.5px solid var(--color-borde); border-radius: var(--radio-sm, 8px);
  background: var(--color-superficie); box-shadow: 0 8px 22px rgba(0,0,0,.16);
  padding: 0;
  /* SIN overflow:hidden. Lo tenía para recortar la cabecera a las esquinas
     redondeadas, pero de paso RECORTABA LA BARRA DE DESPLAZAMIENTO de la lista de
     dentro: la esquina se comía el final del riel y el scroll parecía roto. Ahora
     el radio lo lleva cada parte (cabecera arriba, lista abajo) y la barra queda
     entera. */
}
.fl-drop-acc {
  display: flex; align-items: center; gap: 8px; padding: 7px 12px;
  font-size: .74rem; border-bottom: 1px solid var(--color-borde-suave);
  background: var(--color-superficie-alt);
  border-radius: calc(var(--radio-sm, 8px) - 2px) calc(var(--radio-sm, 8px) - 2px) 0 0;
}
/* El tope también mira la VENTANA: con el desplegable cerca del borde inferior,
   un 260 px fijo dejaba media lista fuera de la pantalla y no había forma de
   llegar a las últimas opciones. Y scrollbar-gutter reserva el sitio de la barra,
   así el texto no baila al aparecer. */
.fl-drop-lista {
  max-height: min(260px, 42vh);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  border-radius: 0 0 calc(var(--radio-sm, 8px) - 2px) calc(var(--radio-sm, 8px) - 2px);
}
.fl-drop-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px; cursor: pointer; font-size: .84rem; line-height: 1.35;
}
.fl-drop-item:hover { background: var(--color-superficie-alt); }
/* Elegida: invertida, el mismo recurso que .fl-hm-tab.es-activo en esta pantalla.
   Con el ✓ la fila se lee sin depender sólo del color de fondo. */
.fl-drop-item.es-sel { background: var(--color-texto); color: var(--color-superficie); }
.fl-drop-item.es-sel:hover { background: var(--color-texto); }
.fl-drop-item input:focus-visible + .fl-drop-sw,
.fl-drop-item input:focus-visible + .fl-drop-nom { outline: 3px solid var(--color-energia-txt); outline-offset: 2px; }
.fl-drop-sw { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; box-shadow: 0 0 0 1px rgba(0,0,0,.18); }
/* Sobre el fondo invertido el punto necesita un halo claro o desaparece. */
.fl-drop-item.es-sel .fl-drop-sw { box-shadow: 0 0 0 1.5px var(--color-superficie); }
.fl-drop-nom { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1 1 auto; }
.fl-drop-tick { flex: 0 0 auto; font-weight: 800; opacity: 0; }
.fl-drop-item.es-sel .fl-drop-tick { opacity: 1; }

.fl-sell-chart { height: 140px; }

/* Los ya elegidos del asistente de padre: su propio bloque, arriba y siempre
   presente aunque el buscador muestre otra cosa. */
.wiz-elegidos { border: 1.5px solid var(--color-acento); border-radius: var(--radio-md); padding: 10px 12px; margin: 12px 0; background: var(--color-alerta-fondo, transparent); }
.wiz-elegidos-tit { display: flex; align-items: center; gap: 6px; margin: 0 0 8px; font-size: .85rem; font-weight: 800; }
.wiz-elegidos-tit .text-suave { font-weight: 600; }
.wiz-lista--corta { max-height: 30vh; border: 0; }

/* ---- Categorías comunes (admin/categorias-comun.php) ---------------------- */
.cc-resumen { border-left: 5px solid var(--color-acento); }
.cc-resumen-kpis { display: flex; flex-wrap: wrap; gap: 22px; margin-bottom: 10px; }
.cc-resumen-kpis div { display: flex; flex-direction: column; }
.cc-resumen-kpis strong { font-size: 1.6rem; font-variant-numeric: tabular-nums; line-height: 1.1; }
.cc-resumen-kpis span { font-size: .8rem; color: var(--color-texto-suave); }
.cc-nota { font-size: .76rem; }
.cc-check { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; font-weight: 700; cursor: pointer; }
.cc-acc { display: flex; justify-content: flex-end; margin: 16px 0 24px; }

/* ---- Flooring: banda "de quién es la percha" (admin/percha.php) ------------
   La pantalla se llama Participación en Flooring y sólo mostraba contadores de
   actividad. Esto responde, antes de leer una fila, de quién es el piso — y con
   DOS medidas, porque una sola engaña: participación (qué % de los frentes) y
   presencia (en cuántas tiendas). Una marca con 30 % en dos tiendas no está
   fuerte, está concentrada. */
/* Evolución de la participación: alto fijo porque Chart.js con
   maintainAspectRatio:false necesita que el contenedor mande, y sin alto propio
   colapsa a 0 px. */
/* ── Esta tienda frente al resto de su ciudad ──────────────────────────────────
   Dos barras por marca, la de aquí en el color de la marca y la de la ciudad en
   gris: el ojo compara longitudes sin tener que leer dos cifras. La escala es
   relativa al mayor de los dos (ver el PHP), porque una escala 0-100 con
   participaciones del 10-40 % deja todo pegado a la izquierda. */
/* ── Índice de la ficha de tienda ──────────────────────────────────────────────
   Pegajoso bajo la barra superior para que siga a mano al bajar. `top` sale del
   alto real de esa barra: si se clava a 0 queda tapado por ella. */
/* Los botones de salida de un estado vacío: centrados bajo el texto y con aire,
   para que se lean como la continuación de la frase y no como un pie de página. */
.cob-vacio-acc { display: flex; gap: var(--esp-sm); justify-content: center; flex-wrap: wrap; margin-top: var(--esp-lg); }

.fl-indice { position: sticky; top: 56px; z-index: 30; display: flex; align-items: center; gap: var(--esp-sm); flex-wrap: wrap;
             margin: 0 0 14px; padding: 8px 12px; border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px);
             background: var(--color-superficie); box-shadow: 0 2px 8px rgba(0,0,0,.06); }
.fl-indice-et { font-size: var(--txt-micro); font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--color-texto-suave); margin-right: 2px; }
.fl-indice-op { display: inline-flex; align-items: center; min-height: 26px; padding: 4px 10px; border-radius: 99px;
                font-size: var(--txt-sec); font-weight: 700; color: var(--color-texto); text-decoration: none; white-space: nowrap; }
.fl-indice-op:hover { background: var(--color-fondo-alt, rgba(127,127,140,.12)); }
/* La sección que se está mirando. La marca el JS; sin él, el índice sigue
   funcionando y sólo se pierde el resaltado. */
.fl-indice-op.es-aqui { background: var(--chrome-bg, var(--color-texto)); color: var(--chrome-texto, var(--color-superficie)); }
/* El ancla queda bajo la barra pegajosa si no se reserva sitio al saltar. */
[id^="b-"] { scroll-margin-top: 110px; }
@media (max-width: 700px) { .fl-indice { top: 0; overflow-x: auto; flex-wrap: nowrap; } }

.fl-vs-lista { display: flex; flex-direction: column; gap: var(--esp-md); margin-top: var(--esp-lg); }
.fl-vs-fila { display: grid; grid-template-columns: minmax(110px, 1.1fr) minmax(90px, 2fr) auto; gap: var(--esp-md); align-items: center; }
.fl-vs-marca { display: flex; align-items: center; gap: var(--esp-sm); font-weight: 700; font-size: var(--txt-peq); min-width: 0; }
.fl-vs-marca { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fl-vs-sw { flex: 0 0 auto; width: 12px; height: 12px; border-radius: 3px; }
.fl-vs-barras { display: flex; flex-direction: column; gap: var(--esp-xs); min-width: 0; }
.fl-vs-b { display: block; height: 9px; border-radius: 3px; min-width: 2px; }
.fl-vs-b--alla { background: var(--color-borde-suave); }
.fl-vs-cifras { display: flex; align-items: baseline; gap: var(--esp-sm); font-size: var(--txt-sec); font-variant-numeric: tabular-nums; white-space: nowrap; }
.fl-vs-dif { font-weight: 800; }
.fl-vs-dif--sube  { color: var(--color-alerta-bajo); }
.fl-vs-dif--baja  { color: var(--color-alerta-alto); }
.fl-vs-dif--igual { color: var(--color-texto-suave); font-weight: 600; }
/* En oscuro los saturados no se leen: misma variante clara que usa el resto. */
@media (prefers-color-scheme: dark) {
  .fl-vs-dif--sube { color: var(--color-alerta-bajo-txt); }
  .fl-vs-dif--baja { color: var(--color-alerta-alto-txt); }
}
:root[data-theme="dark"] .fl-vs-dif--sube { color: var(--color-alerta-bajo-txt); }
:root[data-theme="dark"] .fl-vs-dif--baja { color: var(--color-alerta-alto-txt); }
@media (max-width: 620px) {
  .fl-vs-fila { grid-template-columns: 1fr auto; }
  .fl-vs-barras { grid-column: 1 / -1; }
}

/* Aviso de antigüedad del agregado. Ámbar y discreto: informa de una limitación
   del dato, no es una alerta que exija actuar ya. */
/* Cabecera que ordena: tiene que PARECER pulsable, o nadie descubre que lo es. */
/* Nota bajo el gráfico de sell in/out. [hidden] necesita ganar al display de la
   clase: es la trampa que este proyecto ya ha pisado cuatro veces. */
/* ── Sell in / Sell out · Analítica ────────────────────────────────────────────
   Los <select multiple> con size=1 se ven como un desplegable normal hasta que se
   abren, que es lo que se pidió para los filtros de flooring y aquí conviene por
   lo mismo: caben cinco filtros en una línea sin parecer un formulario de aduana. */
/* Los BOTONES FUERA del reparto de columnas iguales. Medido: "Aplicar" +
   "Limpiar" necesitan 196 px y el grid les daba una columna de 162, así que se
   salían de la tarjeta. Ahora el grid se queda con los filtros —que sí admiten
   estrecharse— y las acciones toman su ancho natural. */
/* ── Pestañas del panel con submenú ────────────────────────────────────────────
   El rótulo del grupo se ve igual que una pestaña suelta: si pareciera otra cosa,
   dejaría de leerse como parte de la misma fila. La flecha es la única diferencia,
   y es la que avisa de que hay más detrás. */
.admin-subtab-menu { position: relative; display: inline-block; }
.admin-subtab-menu > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: var(--esp-xs); }
.admin-subtab-menu > summary::-webkit-details-marker { display: none; }
/* Hereda el aspecto de .admin-subtab para no duplicar la definición del chip. */
/* Los MISMOS valores que .admin-subtab (app.css:2076), copiados a mano y no
   heredados porque son elementos distintos: si aquel se ajusta, éste hay que
   ajustarlo con él. El comentario de allí explica por qué son esas medidas y no
   otras. */
.admin-subtab-menu > summary { border: 1px solid var(--color-borde); background: var(--color-superficie); color: var(--color-texto); border-radius: 999px; padding: 6px 12px; font-weight: 700; font-size: var(--txt-sec, .78rem); line-height: 1.35; white-space: nowrap; transition: background-color .12s ease; }
.admin-subtab-menu.es-activo > summary { background: var(--chrome-bg, var(--color-texto)); color: var(--chrome-texto, var(--color-superficie)); border-color: transparent; }
.admin-subtab-menu > summary:focus-visible { outline: 2px solid var(--color-acento); outline-offset: 2px; }
.admin-subtab-menu[open] > summary .ph-caret-down { transform: rotate(180deg); }
.admin-subtab-menu .ph-caret-down { font-size: .7em; transition: transform .15s ease; }
@media (prefers-reduced-motion: reduce) { .admin-subtab-menu .ph-caret-down { transition: none; } }

/* El panel FLOTA: si empujara el contenido, abrir el menú movería la página entera
   bajo el cursor. z-index alto por el mapa de Leaflet, que se pinta por encima de
   casi todo. */
/* El <details> cerrado oculta sus hijos por hoja de estilos del navegador, pero
   ese ocultado lo pisa cualquier `display` propio: con `display:flex` fijo, los
   CUATRO paneles del menú se maquetaban estando cerrados. Medido a 375 px: uno de
   ellos llegaba a 417 px de borde derecho sobre un viewport de 360 y hacía que la
   página se desplazara en horizontal. No se veían —no se pintan— pero empujaban.
   Así que el display se da SÓLO cuando está abierto. */
.admin-subtab-menu:not([open]) .admin-subtab-panel { display: none; }
.admin-subtab-panel { position: absolute; top: calc(100% + var(--esp-sm)); left: 0; z-index: 1200; min-width: 210px; display: flex; flex-direction: column; gap: var(--esp-2xs); padding: var(--esp-sm); border: 2px solid var(--color-borde); border-radius: var(--radio-md, 10px); background: var(--color-superficie); box-shadow: var(--sombra-dura-sm, 3px 3px 0 rgba(0,0,0,.15)); }
.admin-subtab-op { display: block; padding: 8px 11px; border-radius: var(--radio-sm, 7px); font-size: .84rem; font-weight: 600; color: var(--color-texto); text-decoration: none; white-space: nowrap; }
.admin-subtab-op:hover { background: var(--color-fondo-alt, rgba(127,127,140,.12)); }
.admin-subtab-op.activo { background: var(--color-acento); color: #22223B; font-weight: 800; }
/* De qué lado cae el panel NO depende del ancho de la pantalla sino de dónde esté
   ese menú en la fila, así que no se decide con una media query: se mide al abrir
   y se pone .es-derecha si se saldría. Con la regla fija por ancho, el primer menú
   —que está pegado a la izquierda— se salía 108 px por ese lado. */
.admin-subtab-menu.es-derecha .admin-subtab-panel { left: auto; right: 0; }

/* Banda de contexto: se ha llegado desde la ficha de una tienda. Tinte de acento
   al borde izquierdo en vez de un fondo de color: dice "esto está acotado" sin
   competir con los KPI, que son lo que hay que leer. */
.sa-ctx { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--esp-lg); flex-wrap: wrap;
          margin: 0 0 12px; padding: 10px 14px; border: 1px solid var(--color-borde-suave);
          border-left: 4px solid var(--color-acento); border-radius: var(--radio-md, 10px);
          background: var(--color-superficie); }
.sa-ctx-txt { display: flex; flex-direction: column; gap: var(--esp-2xs); min-width: 0; }
.sa-ctx-et { font-size: var(--txt-micro); font-weight: 800; text-transform: uppercase; letter-spacing: .04em;
             color: var(--color-texto-suave); display: inline-flex; align-items: center; gap: 5px; }
.sa-ctx-txt strong { font-size: 1.05rem; line-height: 1.25; }
.sa-ctx-nota { font-size: var(--txt-sec); color: var(--color-texto-suave); margin-top: var(--esp-xs); max-width: 68ch; line-height: 1.4; }
.sa-ctx-acc { display: flex; gap: var(--esp-sm); flex-wrap: wrap; flex: 0 0 auto; }

.sa-filtros { padding: 16px; display: flex; gap: var(--esp-md); align-items: flex-end; flex-wrap: wrap; }
.sa-filtros-grid { flex: 1 1 420px; min-width: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--esp-md); align-items: end; }
.sa-filtros-grid label { display: block; font-size: var(--txt-micro); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); margin-bottom: var(--esp-xs); }
.sa-filtros-grid input[type="month"], .sa-multi { width: 100%; padding: 7px 10px; border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px); background: var(--color-superficie); color: var(--color-texto); font: inherit; font-size: var(--txt-peq); }
.sa-multi[multiple] { height: auto; min-height: 36px; }
.sa-mini { font-size: var(--txt-micro); text-transform: none; letter-spacing: 0; }
.sa-filtros-acc { flex: 0 0 auto; display: flex; gap: var(--esp-sm); align-items: flex-end; }
/* En estrecho pasan a línea propia en vez de comprimirse a la mitad. */
@media (max-width: 620px) { .sa-filtros-acc { flex: 1 1 100%; } }

.sa-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--esp-md); margin-bottom: var(--esp-md); }
/* 16 y no 14: eran las unicas 5 tarjetas del panel con otro relleno, frente a
   las 32 que llevan 16 por los cuatro lados. Dos pixeles no se ven de una en una,
   pero puestas al lado de otra tarjeta si. */
.sa-kpi { display: flex; flex-direction: column; gap: var(--esp-2xs); padding: 16px; }
.sa-kpi-et { font-size: var(--txt-micro); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-texto-suave); display: inline-flex; align-items: center; gap: var(--esp-xs); }
.sa-kpi-val { font-family: var(--fuente-titulo); font-size: 1.5rem; font-weight: 800; line-height: 1.15; font-variant-numeric: tabular-nums; }
.sa-kpi-sub { font-size: var(--txt-sec); color: var(--color-texto-suave); }
.sa-kpi-var { font-size: var(--txt-sec); font-weight: 800; font-variant-numeric: tabular-nums; margin-top: var(--esp-xs); }
.sa-kpi-var--sube  { color: var(--color-alerta-bajo); }
.sa-kpi-var--baja  { color: var(--color-alerta-alto); }
.sa-kpi-var--igual { color: var(--color-texto-suave); font-weight: 600; }
@media (prefers-color-scheme: dark) {
  .sa-kpi-var--sube { color: var(--color-alerta-bajo-txt); }
  .sa-kpi-var--baja { color: var(--color-alerta-alto-txt); }
}
:root[data-theme="dark"] .sa-kpi-var--sube { color: var(--color-alerta-bajo-txt); }
:root[data-theme="dark"] .sa-kpi-var--baja { color: var(--color-alerta-alto-txt); }

/* El aviso de cobertura no es decorativo: sin él, el sell-through miente por
   omisión. Va destacado pero en ámbar, no en rojo: informa, no alarma. */
.sa-aviso { font-size: var(--txt-sec); line-height: 1.45; color: var(--color-alerta-medio); background: var(--color-alerta-fondo); border: 1px solid var(--color-borde-suave); border-radius: var(--radio-md, 10px); padding: 10px 14px; margin: 0 0 14px; }
@media (prefers-color-scheme: dark) { .sa-aviso { color: #E8A33D; } }
:root[data-theme="dark"] .sa-aviso { color: #E8A33D; }

.sa-chart { height: 340px; margin-top: var(--esp-md); }
@media (max-width: 700px) { .sa-chart { height: 270px; } }

.sa-rank-cab { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--esp-lg); flex-wrap: wrap; }
.sa-dims { display: flex; gap: var(--esp-xs); flex-wrap: wrap; }
/* min-height 28: medido a 20 px, por debajo del mínimo táctil de WCAG 2.2. */
.sa-dim { display: inline-flex; align-items: center; min-height: 28px; border: 1px solid var(--color-borde-suave); border-radius: 999px; padding: 5px 13px; font-size: var(--txt-sec); font-weight: 700; color: var(--color-texto); text-decoration: none; white-space: nowrap; }
.sa-dim.es-activo { background: var(--chrome-bg, var(--color-texto)); color: var(--chrome-texto, var(--color-superficie)); border-color: transparent; }

.sa-tabla-wrap { max-height: 460px; margin-top: var(--esp-md); }
/* La casilla nativa mide 13x13 y no se puede agrandar de forma fiable entre
   navegadores. Se agranda el ÁREA: la celda entera es el objetivo, con la casilla
   centrada y escalada. Así se puede marcar una fila sin apuntar a un cuadradito. */
/* La casilla nativa mide 13x13 y agrandarla no es fiable entre navegadores. Se
   agranda el ÁREA envolviéndola en una <label> de 30x30: la etiqueta ES el
   objetivo, y al ser <label> el clic llega a la casilla sin JavaScript.
   (Un pseudo-elemento superpuesto habría tapado la casilla en vez de agrandarla:
   se vería igual y no se podría marcar.) */
.sa-th-check { width: 44px; text-align: center; padding: 2px; }
.sa-check { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: var(--radio-sm, 7px); cursor: pointer; }
.sa-check:hover { background: var(--color-fondo-alt, rgba(127,127,140,.12)); }
.sa-check .sa-cmp { width: 17px; height: 17px; cursor: pointer; accent-color: var(--color-acento-fuerte); }
.sa-check:focus-within { outline: 2px solid var(--color-acento); outline-offset: -2px; }
.sa-st { font-weight: 800; font-variant-numeric: tabular-nums; }
/* Tres tramos, no un degradado: el ojo compara categorías, no matices. */
.sa-st--alto  { color: var(--color-alerta-bajo); }
.sa-st--medio { color: var(--color-alerta-medio); }
.sa-st--bajo  { color: var(--color-alerta-alto); }
@media (prefers-color-scheme: dark) {
  .sa-st--alto { color: var(--color-alerta-bajo-txt); }
  .sa-st--bajo { color: var(--color-alerta-alto-txt); }
  .sa-st--medio { color: #E8A33D; }
}
:root[data-theme="dark"] .sa-st--alto { color: var(--color-alerta-bajo-txt); }
:root[data-theme="dark"] .sa-st--bajo { color: var(--color-alerta-alto-txt); }
:root[data-theme="dark"] .sa-st--medio { color: #E8A33D; }

/* La trampa de siempre: [hidden] pierde contra el display de la clase. */
.sa-cmp-panel[hidden] { display: none; }

/* El "+" que lleva de la ficha a la sección. Redondo y del tamaño de un dedo. */
/* min-width/min-height además de width/height: dentro de un flex, un ancho fijo
   sí se puede comprimir y el botón se quedaba en 16 px. */
.fl-sell-mas { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; min-width: 30px; min-height: 30px; border-radius: 50%; border: 1px solid var(--color-borde-suave); background: var(--color-superficie); color: var(--color-texto); text-decoration: none; font-size: 1rem; flex: 0 0 auto; }
.fl-sell-mas:hover { background: var(--color-acento); border-color: transparent; color: #22223B; }

.fl-sell-nota { font-size: var(--txt-micro); color: var(--color-texto-suave); margin: 6px 0 0; text-align: center; }
.fl-sell-nota[hidden] { display: none; }

/* `inherit` tomaba el gris de la cabecera de tabla y daba 4,30:1, justo por
   debajo del 4,5. Y con 14 px de alto no llegaba al objetivo táctil mínimo: el
   padding lo sube a 26 sin mover la fila, porque el <th> ya tenía ese aire. */
.pt-th-orden { color: var(--color-texto); text-decoration: none; display: inline-flex; align-items: center; gap: var(--esp-xs); white-space: nowrap; padding: 6px 2px; min-height: 26px; }
.pt-th-orden:hover { text-decoration: underline; }
.pt-th-orden.es-activo { font-weight: 800; }

.pt-frescura { font-size: var(--txt-sec); color: var(--color-alerta-medio); display: flex; align-items: center; gap: var(--esp-xs); margin-top: var(--esp-xs); }
@media (prefers-color-scheme: dark) { .pt-frescura { color: #E8A33D; } }
:root[data-theme="dark"] .pt-frescura { color: #E8A33D; }

.pt-evol-contexto { margin: 6px 0 0; }
.pt-evol-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--esp-sm);
  flex-wrap: wrap; padding: 10px 0 var(--esp-md); border-bottom: 1px solid var(--color-borde-suave);
}
.pt-evol-cortes { color: var(--color-texto-suave); font-size: var(--txt-sec); font-weight: 700; }
.pt-evol-vis { display: flex; align-items: center; gap: 4px; }
.pt-evol-vis .is-activo { background: var(--color-texto); border-color: var(--color-texto); color: var(--color-superficie); }
.pt-evol-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 230px; gap: var(--esp-lg);
  align-items: stretch; min-width: 0; padding-top: var(--esp-md);
}
.pt-evol-lienzo { height: 360px; min-width: 0; }
.pt-evol-series {
  display: flex; flex-direction: column; gap: 5px; min-width: 0; max-height: 360px;
  overflow: auto; padding: 3px 3px 3px 0;
}
.pt-evol-series-tit {
  position: sticky; top: -3px; z-index: 1; padding: 3px 4px 7px;
  background: var(--color-superficie); color: var(--color-texto-suave);
  font-size: var(--txt-micro); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
}
.pt-evol-serie {
  display: grid; grid-template-columns: 12px minmax(0, 1fr) auto; align-items: center;
  gap: 3px 8px; width: 100%; min-height: 48px; padding: 7px 9px;
  border: 1px solid transparent; border-radius: var(--radio-sm, 7px); background: transparent;
  color: var(--color-texto); text-align: left; cursor: pointer; opacity: .5;
}
.pt-evol-serie:hover { background: var(--color-superficie-alt); opacity: .85; }
.pt-evol-serie:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: 1px; }
.pt-evol-serie.is-activa { border-color: var(--color-borde-suave); background: var(--color-superficie-alt); opacity: 1; }
.pt-evol-sw { width: 11px; height: 11px; border-radius: 50%; box-shadow: 0 0 0 1px rgba(0,0,0,.18); }
.pt-evol-nom { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 750; }
.pt-evol-serie strong { font-size: var(--txt-md, .9rem); font-variant-numeric: tabular-nums; }
.pt-evol-serie small { grid-column: 2 / 4; color: var(--color-texto-suave); font-size: var(--txt-micro); font-variant-numeric: tabular-nums; }
.pt-evol-serie small.es-sube { color: var(--color-alerta-bajo); }
.pt-evol-serie small.es-baja { color: var(--color-alerta-alto); }
.pt-evol-insight { display: flex; align-items: flex-start; gap: var(--esp-sm); margin: var(--esp-md) 0 0; }
.pt-evol-insight i { color: var(--color-acento-fuerte); margin-top: 2px; }
@media (prefers-color-scheme: dark) {
  .pt-evol-serie small.es-sube { color: var(--color-alerta-bajo-txt); }
  .pt-evol-serie small.es-baja { color: var(--color-alerta-alto-txt); }
}
:root[data-theme="dark"] .pt-evol-serie small.es-sube { color: var(--color-alerta-bajo-txt); }
:root[data-theme="dark"] .pt-evol-serie small.es-baja { color: var(--color-alerta-alto-txt); }
@media (max-width: 850px) {
  .pt-evol-grid { grid-template-columns: 1fr; gap: var(--esp-md); }
  .pt-evol-lienzo { height: 300px; }
  .pt-evol-series { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); max-height: none; overflow: visible; }
  .pt-evol-series-tit { grid-column: 1 / -1; position: static; }
}
@media (max-width: 520px) {
  .pt-evol-toolbar { align-items: stretch; }
  .pt-evol-vis { width: 100%; }
  .pt-evol-vis .btn { flex: 1; }
  .pt-evol-lienzo { height: 270px; }
  .pt-evol-series { grid-template-columns: 1fr; }
}
@media print {
  .pt-evol-toolbar, .pt-evol-series { display: none !important; }
  .pt-evol-grid { grid-template-columns: 1fr; }
  .pt-evol-lienzo { height: 320px; }
}

.pt-banda { border-left: 5px solid var(--color-acento); }
.pt-cab { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--esp-lg); flex-wrap: wrap; margin-bottom: var(--esp-md); }
.pt-cab h3 { display: flex; align-items: center; gap: var(--esp-sm); font-size: 1.05rem; }
.pt-barra { margin-bottom: var(--esp-lg); }
/* "Otras" en gris de trama: se ve que existe sin competir con las marcas. */
.pt-seg-otras { background: repeating-linear-gradient(45deg, var(--color-borde-suave) 0 6px, var(--color-superficie-alt) 6px 12px); }
.pt-lista { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--esp-2xs) 18px; }
/* min-height 30: la fila es pulsable (abre la ficha de la marca) y medía 21 px,
   por debajo del mínimo táctil. Con el grid no basta subir el padding: hay que
   fijar el alto de la fila. */
.pt-item {
  display: grid; grid-template-columns: 12px 1fr auto auto; align-items: center; gap: 10px; min-height: 30px;
  padding: 7px 4px; border: 0; background: none; text-align: left; width: 100%;
  font-size: var(--txt-peq, .86rem); color: var(--color-texto); border-bottom: 1px solid var(--color-borde-suave);
}
button.pt-item { cursor: pointer; }
button.pt-item:hover { background: var(--color-superficie-alt); }
button.pt-item:focus-visible { outline: 3px solid var(--color-energia-txt); outline-offset: -2px; }
.pt-sw { width: 12px; height: 12px; border-radius: 50%; box-shadow: 0 0 0 1px rgba(0,0,0,.18); }
.pt-sw--otras { background: var(--color-borde); }
.pt-nom { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; }
.pt-pct { font-variant-numeric: tabular-nums; font-weight: 800; }
/* La presencia va con riel: el número solo ("3/12") obliga a dividir mentalmente
   en cada fila; la barrita se compara de un vistazo entre marcas. */
.pt-presencia { display: inline-flex; align-items: center; gap: var(--esp-sm); font-size: var(--txt-sec); color: var(--color-texto-suave); font-variant-numeric: tabular-nums; }
.pt-presencia-riel { display: inline-block; width: 44px; height: 6px; border-radius: 3px; background: var(--color-superficie-alt); overflow: hidden; }
.pt-presencia-fill { display: block; height: 100%; background: var(--color-texto-suave); }
.pt-item--otras { opacity: .75; }

/* ===========================================================================
   PERFIL DEL PROMOTOR
   ===========================================================================
   La ficha era administrativa y nada más: seis párrafos con sus datos y tres
   acordeones mudos. Es la única pantalla de la app que habla del promotor, y no
   le devolvía ni un número de lo que hace.
   =========================================================================== */

/* Los datos, en dos columnas: etiqueta apagada a la izquierda, dato firme a la
   derecha. Como <p><strong>Etiqueta:</strong> valor</p> se leían como una frase
   corrida y no había forma de barrer la columna buscando un dato concreto. */
.perfil-datos {
  display: grid;
  grid-template-columns: minmax(105px, auto) minmax(0, 1fr);
  gap: 0 var(--e4, 14px);
  margin: var(--e3, 12px) 0 0;
  align-items: baseline;
}
.perfil-datos dt {
  display: flex; align-items: center; gap: 6px; min-height: 39px;
  border-bottom: 1px solid var(--color-borde-suave);
  font-size: var(--txt-sec, .82rem);
  color: var(--color-texto-suave);
  font-weight: 600;
}
.perfil-datos dt i { width: 17px; color: var(--color-acento-fuerte); font-size: .95rem; }
.perfil-datos dd {
  min-height: 39px; margin: 0; padding-block: 9px; border-bottom: 1px solid var(--color-borde-suave);
  font-weight: 700;
  overflow-wrap: anywhere;   /* un correo largo parte, no desborda la tarjeta */
}
.perfil-datos dt:nth-last-of-type(1), .perfil-datos dd:last-child { border-bottom: 0; }
.perfil-seccion-cab { display: flex; align-items: center; gap: 11px; margin-bottom: 4px; }
.perfil-seccion-cab h2 { font-size: 1rem; }
.perfil-seccion-cab p { font-size: .75rem; }
.perfil-seccion-ic {
  width: 42px; height: 42px; flex: 0 0 auto; display: inline-flex; align-items: center;
  justify-content: center; border-radius: 12px; background: var(--color-texto);
  color: var(--color-superficie); font-size: 1.15rem;
}
.perfil-panel { overflow: hidden; }
/* Con caja tactil de verdad. Se barrieron los 314 elementos tocables de las ocho
   pantallas del promotor y este era el UNICO por debajo de los 24 px que pide
   WCAG 2.2: 17 px de alto, en un dedo, dentro de una tienda. Se le da altura y
   relleno en vez de agrandar la letra, para que la fila de datos no se estire. */
.perfil-datos-accion {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 24px;
  margin-left: 6px;
  padding: 0 6px;
  border-radius: var(--radio-sm, 7px);
  font-size: var(--txt-sec, .8rem);
  font-weight: 700;
  white-space: nowrap;
  color: var(--color-acento-fuerte);
}
@media (hover: hover) {
  .perfil-datos-accion:hover { background: var(--color-alerta-fondo); }
}

/* Cada ajuste dice QUÉ hace antes de abrirse: icono, título y una línea de
   contexto. Antes el resumen iba a pelo y había que abrir los tres para saber
   cuál era cuál. */
.perfil-ajuste {
  margin-top: 9px; padding: 0 11px; border: 1px solid var(--color-borde-suave);
  border-radius: var(--radio-sm, 9px); background: var(--color-superficie-alt);
  scroll-margin-top: 90px;
}
.perfil-ajuste[open] { padding-bottom: 13px; border-color: var(--color-texto); background: var(--color-superficie); }
.perfil-ajuste-cab {
  display: flex;
  align-items: center;
  gap: var(--e3, 10px);
  padding: var(--e3, 12px) 0;
}
.perfil-ajuste-ic {
  flex: 0 0 auto; width: 34px; height: 34px; display: inline-flex; align-items: center;
  justify-content: center; border-radius: 10px; background: var(--color-superficie);
  font-size: 1.05rem; color: var(--color-acento-fuerte);
}
.perfil-ajuste[open] .perfil-ajuste-ic { background: var(--color-alerta-fondo); }
.perfil-ajuste-txt { display: flex; flex-direction: column; min-width: 0; }
.perfil-ajuste-txt small {
  color: var(--color-texto-suave);
  font-size: var(--txt-sec, .78rem);
  line-height: 1.3;
}

/* La línea del total. Tres ceros a alguien con miles de registros a la espalda
   no informan de nada: cuando el mes viene vacío, el peso lo lleva esta frase. */
.perfil-total {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: var(--e3, 10px);
  font-size: var(--txt-sec, .84rem);
  color: var(--color-texto-suave);
}
.perfil-total i { color: var(--color-acento); font-size: 1.05rem; }
.perfil-total strong { color: var(--color-texto); }

/* La salida iba suelta al final de la página, fuera de toda tarjeta. */
.perfil-salir {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3, 12px);
}
@media (max-width: 560px) {
  .perfil-hero-id { align-items: flex-start; }
  .perfil-hero .avatar-grande { width: 64px; height: 64px; }
  .perfil-datos { grid-template-columns: 1fr; }
  .perfil-datos dt { min-height: 0; padding-top: 10px; border-bottom: 0; font-size: var(--txt-micro, .7rem); text-transform: uppercase; letter-spacing: .035em; }
  .perfil-datos dd { padding: 3px 0 10px 23px; min-height: 0; }
  .perfil-datos-accion { float: right; }
  .perfil-salir .btn { width: 100%; justify-content: center; }
}

/* Salidas consistentes de Chart.js: no obligan a pasar el cursor para conocer
   cifras exactas y dejan una vía directa para reutilizar el análisis. */
/* LA BARRA DE ACCIONES NO PUEDE ROBARLE LA COLUMNA A NADIE.
   Se inserta con insertAdjacentElement('afterend') justo detras del contenedor
   del lienzo, y eso da por hecho que caera DEBAJO del grafico. En percha.php no:
   .pt-evol-grid es una rejilla de dos columnas (grafico | lista de marcas), asi
   que la barra entraba como tercer hijo y se quedaba con la columna de 230 px de
   las marcas, que bajaban a una segunda fila. Medido: el panel aterrizaba en la
   misma fila que el lienzo y la lista caia 376 px mas abajo.
   grid-column lo manda de ancho completo cuando el padre es una rejilla, y lo
   ignora sin efecto cuando no lo es: una linea que sirve para todos los graficos
   de la aplicacion sin tocar ninguna pantalla. */
.chart-acciones { grid-column: 1 / -1; order: 99; min-width: 0; }
/* El "order" es lo que devuelve la lista de marcas a su sitio. Con solo el
   grid-column, la barra segui­a siendo el SEGUNDO hijo y ocupaba la fila 2 entera,
   asi que las marcas -que son la leyenda del grafico y tienen que leerse JUNTO a
   el- bajaban a una tercera fila. Mandando la barra al final, la leyenda vuelve a
   la columna de la derecha y las acciones quedan debajo de las dos. */
.chart-acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}
.chart-datos { margin-right: auto; min-width: 0; max-width: 100%; }
/* El carril que de verdad recorta. min-width:0 arriba es lo que PERMITE encoger
   -un hijo flex se niega a bajar de su contenido si no se lo dices-, y este
   overflow es lo que hace que el sobrante se quede dentro de la caja. Hacen falta
   los dos: con uno solo la tabla seguia estirando el panel. */
.chart-datos-scroll { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; }
.chart-datos > summary {
  cursor: pointer;
  color: var(--color-acento-fuerte);
  font-weight: 700;
  min-height: 36px;
  display: flex;
  align-items: center;
}
.chart-datos[open] { width: 100%; }
.chart-datos-tabla { margin-top: 8px; font-size: .84rem; }
#alertas-precio { scroll-margin-top: 90px; }
.admin-topbar-corte {
  margin-left: auto;
  white-space: nowrap;
  color: var(--color-texto-suave);
  font-size: .76rem;
}
.admin-topbar-corte i { color: var(--color-acento-fuerte); }
.sync-queue-badge {
  position: fixed; z-index: 70; right: 12px; bottom: calc(var(--barra-inferior-alto, 68px) + 12px);
  display: inline-flex; align-items: center; gap: 7px; min-height: 40px; padding: 7px 11px;
  border: 2px solid var(--color-borde); box-shadow: 3px 3px 0 var(--color-borde);
  background: var(--color-superficie); color: var(--color-texto); font: inherit; font-size: .8rem; font-weight: 700;
}
/* El atributo ya viene en el HTML, pero el display de la clase tenía la misma
   trampa que otros componentes flex: la regla de autor ganaba sobre [hidden]. */
.sync-queue-badge[hidden] { display: none !important; }
.sync-queue-badge i { color: var(--color-acento-fuerte); }

/* Estado global sin conexión: el borde permanece mientras no haya red y el
   rótulo lo expresa también con texto, para no depender únicamente del color. */
body.sin-conexion::before {
  content: ''; position: fixed; inset: 0; z-index: 9998; pointer-events: none;
  border: 4px solid var(--color-alerta-alto-txt, #B42318);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.75);
}
.conexion-global {
  position: fixed; z-index: 9999; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--barra-inferior-alto, 68px) + 12px); display: inline-flex;
  align-items: center; gap: 7px; min-height: 38px; padding: 7px 12px;
  border-radius: 999px; background: var(--color-alerta-alto-txt, #B42318); color: #fff;
  box-shadow: 0 5px 18px rgba(0,0,0,.24); font-size: .8rem; font-weight: 800; white-space: nowrap;
}
.conexion-global[hidden] { display: none !important; }
body.sin-conexion .sync-queue-badge:not([hidden]) { bottom: calc(var(--barra-inferior-alto, 68px) + 60px); }
.modelos-recientes { margin: 12px 0; padding: 10px; border: 1px solid var(--color-borde-suave); background: var(--color-superficie-suave, var(--color-superficie)); }
.modelos-recientes-lista { display: flex; gap: 7px; overflow-x: auto; padding-top: 7px; }
.chip-modelo-reciente { flex: 0 0 auto; min-height: 40px; border: 1px solid var(--color-borde); background: var(--color-superficie); color: var(--color-texto); padding: 7px 9px; font: inherit; font-size: var(--txt-sec, .78rem); text-align: left; }
.chip-modelo-reciente strong { color: var(--color-acento-fuerte); }
.ruta-navegar { display: inline-flex; align-items: center; gap: 6px; margin: 8px 0; color: var(--color-acento-fuerte); font-size: .84rem; font-weight: 700; }
.cierre-jornada { display: grid; gap: 12px; margin-top: 16px; border-left: 5px solid var(--color-alerta-bajo); }
.cierre-jornada-kpis { display: flex; flex-wrap: wrap; gap: 8px; }
.cierre-jornada-kpis span { padding: 7px 9px; border: 1px solid var(--color-borde-suave); font-size: var(--txt-sm, .82rem); }
.cierre-jornada-kpis strong { color: var(--color-alerta-bajo); font-size: 1rem; }
@media (min-width: 980px) { .sync-queue-badge { bottom: 18px; } }

/* Campo: permisos, progreso y sincronización detallada (v235). */
body.sin-conexion::after { content: ""; position: fixed; inset: 0; z-index: 10020; border: 4px solid #c53b32; pointer-events: none; }
body.sin-conexion .topbar::after { content: "Sin conexión"; position: absolute; left: 50%; bottom: -12px; transform: translateX(-50%); padding: 3px 10px; border-radius: 999px; color: #fff; background: #c53b32; font: 700 .7rem/1.2 var(--fuente-cuerpo); z-index: 5; }
.sync-lista { display: grid; gap: 10px; overflow: auto; }
.sync-item { display: grid; grid-template-columns: 42px minmax(0,1fr) auto; align-items: center; gap: 10px; padding: 12px; border: 1px solid var(--color-borde-suave); border-radius: 16px; background: #fff; }
.sync-item-ic { display: grid; place-items: center; width: 42px; height: 42px; border-radius: 12px; background: var(--color-crema); color: var(--color-acento-fuerte); font-size: 1.25rem; }
.sync-item div { min-width: 0; display: grid; gap: 2px; }.sync-item small,.sync-item p { margin: 0; font-size: .76rem; }.sync-error { color: #a72e28; font-weight: 650; }
.empty-mini { display: grid; justify-items: center; gap: 5px; padding: 28px 12px; text-align: center; color: var(--color-texto-suave); }.empty-mini i { font-size: 2rem; color: var(--color-alerta-bajo); }
.guia-promotor { display: grid; gap: 8px; padding: 0; margin: 10px 0; list-style: none; }
.permisos-hoja .hoja-head > div,.sync-lista + .hoja-acciones { display: grid; gap: 3px; }
.guia-promotor li { display: grid; grid-template-columns: 38px minmax(0,1fr) auto; align-items: center; gap: 10px; padding: 10px; border: 1px solid var(--color-borde-suave); border-radius: 14px; }
.guia-promotor li > i { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 11px; background: var(--color-crema); color: var(--color-acento-fuerte); font-size: 1.15rem; }.guia-promotor span { display: grid; }.guia-promotor small { color: var(--color-texto-suave); }.guia-promotor b { font-size: var(--txt-micro, .7rem); color: var(--color-texto-suave); }
.ff-progreso { position: sticky; top: 74px; z-index: 3; margin: 0 0 14px; padding: 10px 12px; border: 1px solid var(--color-borde-suave); border-radius: 14px; background: rgba(255,255,255,.96); box-shadow: 0 5px 18px rgba(34,34,59,.08); }.ff-progreso div { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: var(--txt-sec, .78rem); }.ff-progreso progress { width: 100%; height: 7px; accent-color: var(--color-acento-fuerte); }
.ff-tipo-campo { margin-left: auto; padding: 2px 7px; border-radius: 999px; background: var(--color-crema); color: var(--color-texto-suave); font-size: .62rem; text-transform: none; letter-spacing: 0; }.ff-campo label { display: flex; align-items: center; gap: 5px; }
.campo-voz { align-self: flex-end; width: 38px; height: 38px; margin-top: -46px; margin-right: 5px; margin-bottom: 8px; border: 0; border-radius: 10px; background: var(--color-crema); color: var(--color-primario); z-index: 1; }.campo-voz.grabando { color: #fff; background: #c53b32; animation: pulsoVoz 1s infinite; }@keyframes pulsoVoz { 50% { transform: scale(.92); } }
.ff-atajos { display: flex; gap: 6px; overflow-x: auto; padding-top: 5px; }.ff-atajos .chip { white-space: nowrap; }
.form-resumen-lista { display: grid; gap: 0; padding: 0; margin: 0; list-style: none; border: 1px solid var(--color-borde-suave); border-radius: 14px; overflow: auto; }.form-resumen-lista li { display: grid; gap: 3px; padding: 10px 12px; border-bottom: 1px solid var(--color-borde-suave); }.form-resumen-lista li:last-child { border: 0; }.form-resumen-lista span { color: var(--color-texto-suave); font-size: var(--txt-xs, .72rem); }.form-resumen-lista strong { font-size: .87rem; }
.ff-mas-evidencia { align-self: flex-start; margin-top: 7px; }.ff-evidencias-grid { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 6px; }.ff-evidencias-grid span { position: relative; aspect-ratio: 1; overflow: hidden; border-radius: 10px; background: var(--color-crema); }.ff-evidencias-grid img { width: 100%; height: 100%; object-fit: cover; }.ff-evidencias-grid button { position: absolute; top: 4px; right: 4px; display: grid; place-items: center; width: 24px; height: 24px; border: 0; border-radius: 50%; color: #fff; background: rgba(34,34,59,.82); }
body.modo-compacto .tarjeta,body.modo-compacto .form-tile { padding: 12px; }body.modo-compacto .ff-campo { margin-top: 9px; }body.ocultar-ayudas .ayuda { display: none; }
.preferencias-lista { display: grid; gap: 0; }.preferencia-fila { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 12px; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--color-borde-suave); }.preferencia-fila:last-child { border: 0; }.preferencia-fila span { display: grid; }.preferencia-fila small { color: var(--color-texto-suave); }
.notif-accion { display: inline-flex; align-items: center; gap: 5px; margin-top: 5px; color: var(--color-acento-fuerte); font-size: var(--txt-sec, .78rem); font-weight: 750; }.resumen-visita-check { display: grid; place-items: center; width: 54px; height: 54px; margin: 0 auto 10px; border-radius: 50%; color: #fff; background: var(--color-alerta-bajo); font-size: 1.7rem; }
.reportes-soporte-kpis { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 12px; margin-bottom: 14px; }.reportes-soporte-kpis .tarjeta { display: grid; gap: 2px; margin: 0; color: inherit; text-decoration: none; }.reportes-soporte-kpis strong { font: 800 1.55rem/1 var(--fuente-titulo); color: var(--color-primario); }.reportes-soporte-kpis span { color: var(--color-texto-suave); font-size: .8rem; }.reportes-soporte-filtros { display: grid; grid-template-columns: minmax(170px,.55fr) minmax(250px,1fr) auto auto; align-items: end; gap: 10px; }.reportes-soporte-filtros > div { display: grid; gap: 5px; }.reportes-soporte-lista { display: grid; gap: 14px; margin-top: 14px; }.reporte-soporte { display: grid; gap: 14px; margin: 0; }.reporte-soporte--destacado { outline: 3px solid color-mix(in srgb,var(--color-acento-fuerte) 42%,transparent); }.reporte-soporte > header { display: flex; justify-content: space-between; gap: 12px; align-items: flex-start; }.reporte-soporte h2,.reporte-soporte h3,.reporte-soporte p { margin-top: 0; }.reporte-soporte h2 { margin-bottom: 3px; font-size: 1.18rem; }.reporte-soporte h3 { margin-bottom: 6px; font-size: var(--txt-md, .9rem); }.reporte-soporte-cuerpo { display: grid; grid-template-columns: minmax(0,1fr) 180px; gap: 16px; }.reporte-soporte-captura { position: relative; display: block; min-height: 112px; overflow: hidden; border: 1px solid var(--color-borde-suave); border-radius: 14px; background: var(--color-crema); }.reporte-soporte-captura img { width: 100%; height: 130px; object-fit: cover; }.reporte-soporte-captura span { position: absolute; inset: auto 6px 6px; padding: 5px 7px; border-radius: 8px; color: #fff; background: rgba(34,34,59,.86); font-size: var(--txt-xs, .72rem); font-weight: 700; text-align: center; }.reporte-soporte-diag { border-top: 1px solid var(--color-borde-suave); padding-top: 10px; }.reporte-soporte-acciones { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }.text-break { overflow-wrap: anywhere; }
@media (max-width: 520px) { .sync-item { grid-template-columns: 38px minmax(0,1fr); }.sync-item .btn { grid-column: 2; justify-self: start; }.guia-promotor li { grid-template-columns: 36px minmax(0,1fr); }.guia-promotor b { grid-column: 2; } }
@media (max-width: 760px) { .reportes-soporte-kpis { grid-template-columns: repeat(2,minmax(0,1fr)); }.reportes-soporte-filtros { grid-template-columns: 1fr; }.reporte-soporte-cuerpo { grid-template-columns: 1fr; }.reporte-soporte-captura { max-width: 260px; }.reporte-soporte-acciones .btn { flex: 1 1 180px; } }
@media (max-width: 560px) {
  .chart-acciones > .btn { flex: 1 1 auto; justify-content: center; }
  .chart-datos { width: 100%; }
  .admin-topbar-corte { display: none; }
}

/* ===========================================================================
   Acceso v237 · composición editorial, campos estables y foco en la tarea
   =========================================================================== */
.login-shell {
  display: grid;
  grid-template-columns: minmax(0, 1.08fr) minmax(380px, .92fr);
  gap: 0;
  max-width: 1120px;
  min-height: min(720px, calc(100svh - 132px));
  margin: 22px auto 38px;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--color-borde) 70%, transparent);
  border-radius: 28px;
  background: var(--color-superficie);
  box-shadow: 0 28px 70px rgba(34, 34, 59, .16);
}
.login-brand {
  position: relative;
  min-height: 100%;
  padding: clamp(30px, 4.2vw, 58px);
  overflow: hidden;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  color: #fff;
  background-image:
    linear-gradient(180deg, rgba(15, 16, 35, .08) 30%, rgba(15, 16, 35, .88) 100%),
    url('/assets/img/brand/login-visual-v237.png');
  background-size: cover;
  background-position: 58% center;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.login-brand::before {
  content: "";
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  border: 0;
  transform: none;
  background: linear-gradient(90deg, rgba(15, 16, 35, .6), transparent 68%);
  pointer-events: none;
}
.login-brand::after { height: 4px; }
.login-brand-kicker {
  padding: 8px 12px;
  border: 1px solid rgba(255, 255, 255, .25);
  background: rgba(15, 16, 35, .34);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .14);
  backdrop-filter: blur(10px);
  font-size: .68rem;
  letter-spacing: .1em;
}
.login-brand-body { max-width: 560px; gap: 10px; }
.login-brand-logo { height: 64px; margin: 0 0 10px; }
.login-brand h1 {
  max-width: 15ch;
  margin: 0;
  color: #fff;
  font-size: clamp(2rem, 3.2vw, 3.15rem);
  line-height: .98;
  letter-spacing: -.045em;
  text-wrap: balance;
}
.login-brand p {
  max-width: 46ch;
  color: rgba(255, 255, 255, .82);
  font-size: .98rem;
  line-height: 1.45;
}
.login-brand-points {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: 14px;
}
.login-brand-points li {
  width: auto;
  min-width: 0;
  align-items: flex-start;
  gap: 8px;
  padding: 11px;
  border-color: rgba(255, 255, 255, .18);
  border-radius: 14px;
  background: rgba(15, 16, 35, .42);
  backdrop-filter: blur(10px);
}
.login-brand-points li > i { margin-top: 2px; flex: 0 0 auto; }
.login-brand-points li span { display: grid; min-width: 0; gap: 2px; }
.login-brand-points li strong { color: #fff; font-size: .75rem; line-height: 1.2; }
.login-brand-points li small { color: rgba(255, 255, 255, .68); font-size: .63rem; line-height: 1.3; }

.login-form-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(26px, 4vw, 58px);
  background:
    radial-gradient(circle at 100% 0, rgba(217, 154, 50, .11), transparent 38%),
    var(--color-superficie);
}
.login-form-inner {
  width: 100%;
  max-width: 430px;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.login-form-logo { display: none; width: auto; height: 48px; object-fit: contain; }
.login-form-kicker {
  display: block;
  margin-bottom: 8px;
  color: var(--color-acento-fuerte);
  font-size: var(--txt-xs, .72rem);
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.login-form-inner h2 {
  margin-bottom: 8px;
  font-size: clamp(2rem, 3vw, 2.55rem);
  line-height: 1;
  letter-spacing: -.035em;
}
.login-sub { margin-bottom: 28px; line-height: 1.45; }
.login-form-inner form > label {
  display: block;
  margin: 17px 0 7px;
  font-size: .8rem;
  font-weight: 800;
}
.login-field,
.login-field--pw {
  display: grid !important;
  grid-template-columns: 48px minmax(0, 1fr) !important;
  min-width: 0;
  min-height: 54px;
  overflow: hidden;
  border: 1.5px solid color-mix(in srgb, var(--color-borde) 72%, transparent);
  border-radius: 15px;
  background: var(--color-superficie);
  box-shadow: 0 5px 14px rgba(34, 34, 59, .045);
}
.login-field--pw { grid-template-columns: 48px minmax(0, 1fr) 48px !important; }
.login-field > i { font-size: 1.12rem; color: var(--color-texto-suave); }
.login-field input {
  display: block;
  width: 100%;
  min-width: 0;
  min-height: 54px;
  margin: 0;
  padding: 11px 8px 11px 0;
  border: 0 !important;
  border-radius: 0;
  outline: 0;
  background: transparent !important;
  box-shadow: none !important;
  font-size: 1rem;
}
.login-field input:-webkit-autofill,
.login-field input:-webkit-autofill:hover,
.login-field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--color-texto);
  box-shadow: inset 0 0 0 1000px var(--color-superficie) !important;
  transition: background-color 9999s ease-out;
}
.login-field:focus-within {
  border-color: var(--color-energia);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-energia) 16%, transparent);
}
.login-field .password-toggle {
  display: grid;
  place-items: center;
  align-self: stretch;
  width: 48px;
  min-width: 48px;
  min-height: 52px;
  margin: 0;
  padding: 0;
  border: 0;
  border-left: 1px solid var(--color-borde-suave);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  color: var(--color-texto-suave);
  cursor: pointer;
}
.login-field .password-toggle:hover { color: var(--color-acento-fuerte); background: var(--color-superficie-alt); }
.login-field .password-toggle:focus-visible { outline: 3px solid color-mix(in srgb, var(--color-energia) 28%, transparent); outline-offset: -4px; }
.login-fila-extra { margin-top: 10px; }
.login-forgot { color: var(--color-acento-fuerte); font-size: .8rem; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.login-btn {
  min-height: 54px;
  border-radius: 15px;
  box-shadow: 0 8px 20px rgba(217, 154, 50, .22);
}
.login-seguro { margin-top: 18px; }
.login-foot { margin-top: 18px; padding-top: 18px; }

/* Los nuevos grupos superiores se mantienen en una sola franja desplazable en
   pantallas pequeñas: ninguna opción vuelve al sidebar ni parte el encabezado. */
@media (max-width: 760px) {
  .admin-subtabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    margin-inline: -4px;
    padding: 2px 4px 12px;
    scrollbar-width: thin;
    scroll-snap-type: x proximity;
  }
  .admin-subtab,
  .admin-subtab-menu { flex: 0 0 auto; scroll-snap-align: start; }
}

@media (max-width: 899px) {
  .login-shell {
    grid-template-columns: 1fr;
    max-width: 620px;
    min-height: 0;
    margin: 12px auto 30px;
    border-radius: 24px;
  }
  .login-brand {
    min-height: 220px;
    padding: 22px;
    border-radius: 0;
    background-position: 62% 49%;
  }
  .login-brand-kicker { align-self: flex-start; }
  .login-brand-body { align-items: flex-start; }
  .login-brand-logo,
  .login-brand p,
  .login-brand-points { display: none; }
  .login-brand h1 { display: block; max-width: 12ch; font-size: 2rem; }
  .login-form-panel { padding: 30px 24px 32px; }
  .login-form-inner { max-width: 480px; }
  .login-form-inner > .login-form-logo { display: none; }
}

@media (max-width: 480px) {
  .login-shell { margin: 8px 0 24px; border-radius: 20px; }
  .login-brand { min-height: 156px; padding: 18px; background-position: 65% 52%; }
  .login-brand-kicker { font-size: .59rem; padding: 6px 9px; }
  .login-brand h1 { font-size: 1.5rem; max-width: 13ch; }
  .login-form-panel { padding: 26px 19px 28px; }
  .login-form-inner > .login-form-logo { height: 42px; margin-bottom: 20px; }
  .login-form-inner h2 { font-size: 1.85rem; }
  .login-sub { margin-bottom: 22px; font-size: var(--txt-md, .9rem); }
  .login-form-inner form > label { margin-top: 15px; }
  .login-seguro { font-size: var(--txt-micro, .7rem); }
}

/* Resumen y chrome administrativo v237. */
.admin-contenido > .topbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 14px;
}
.admin-topbar-left,
.admin-topbar-actions { display: flex; align-items: center; gap: 7px; min-width: 0; }
.admin-topbar-left .marca-titulo { min-width: 0; }
.admin-topbar-actions { justify-content: flex-end; }
.admin-topbar-actions .admin-quick-trigger { margin-left: 0; }
.admin-topbar-corte {
  justify-self: center;
  margin-left: 0;
  color: rgba(247, 243, 235, .7);
  font-size: .68rem;
  letter-spacing: .01em;
}
.admin-sidebar a { position: relative; }
.admin-nav-badge {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  margin-left: auto;
  padding: 0 5px;
  border: 2px solid var(--color-texto);
  border-radius: 999px;
  background: #C24418;
  color: #fff;
  font-size: .63rem;
  font-weight: 850;
  line-height: 1;
}
@media (min-width: 980px) and (hover: hover) {
  .admin-shell.rail-activo .admin-sidebar:not(.rail-abierto) .admin-nav-badge {
    position: absolute;
    top: 4px;
    right: 4px;
    min-width: 17px;
    height: 17px;
    margin: 0;
    padding: 0 3px;
    border-color: var(--color-texto);
    font-size: .55rem;
  }
}

.lista-novedades { gap: 8px; }
.lista-novedades li {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  align-items: center;
  gap: 9px;
  min-height: 58px;
  padding: 7px 9px;
  border: 1px solid var(--color-borde-suave);
  border-radius: 13px;
  background: color-mix(in srgb, var(--color-superficie-alt) 54%, transparent);
}
.lista-novedades li > i { display: grid; place-items: center; font-size: 1.1rem; }
.lista-novedades .celda-enlace {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-width: 0;
  padding: 0;
  text-align: left;
  text-decoration: none;
}
.novedad-nombre {
  display: -webkit-box;
  min-height: 2.5em;
  overflow: hidden;
  line-height: 1.25;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.novedad-ciudad { color: var(--color-texto-suave); font-size: var(--txt-sec, .78rem); font-weight: 600; white-space: nowrap; }
.entidad-tooltip { position: relative; }
.entidad-tooltip::after {
  content: attr(data-tooltip);
  position: absolute;
  z-index: 12;
  right: 0;
  bottom: calc(100% + 7px);
  width: max-content;
  max-width: 240px;
  padding: 6px 9px;
  border-radius: 8px;
  background: var(--color-texto);
  color: var(--color-superficie);
  box-shadow: 0 8px 20px rgba(34,34,59,.2);
  font-size: .69rem;
  font-weight: 700;
  line-height: 1.3;
  pointer-events: none;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity .14s ease, transform .14s ease;
}
.entidad-tooltip:hover::after,
.entidad-tooltip:focus-visible::after { opacity: 1; transform: translateY(0); }

.hm-panel-head { align-items: flex-end; gap: 14px; }
.hm-ampliar { flex: 0 0 auto; }
.hm-panel.hm-expandido {
  position: fixed;
  inset: 0;
  z-index: 10050;
  overflow: auto;
  padding: clamp(14px, 2.5vw, 34px);
  background: var(--color-fondo);
  overscroll-behavior: contain;
}
.hm-panel.hm-expandido .hm-panel-head {
  position: sticky;
  top: -1px;
  z-index: 4;
  margin: 0;
  padding: 12px 0 16px;
  background: color-mix(in srgb, var(--color-fondo) 94%, transparent);
  backdrop-filter: blur(12px);
}
.hm-panel.hm-expandido .hm-panel-card { min-height: calc(100vh - 125px); margin: 0; }
.hm-panel.hm-expandido .tabla-scroll { max-height: none; }
body.hm-sin-scroll { overflow: hidden; }

@media (max-width: 760px) {
  .admin-contenido > .topbar { grid-template-columns: minmax(0, 1fr) auto; gap: 8px; }
  .admin-topbar-corte { display: none; }
  .admin-topbar-actions { grid-column: 2; }
  .admin-topbar-left { min-width: 0; }
  .admin-topbar-left .marca-titulo { font-size: 1rem; }
  .admin-topbar-actions .admin-quick-trigger { padding-inline: 7px; }
  .lista-novedades .celda-enlace { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .novedad-ciudad { white-space: normal; }
  .hm-panel-head { flex-direction: row; align-items: center; width: 100%; }
  .hm-panel-head > div { min-width: 0; }
  .hm-panel-head h2 { font-size: 1.2rem; }
  .hm-panel-head p { display: none; }
  .hm-ampliar span { display: none; }
  .hm-panel.hm-expandido { padding: 10px; }
}

/* ===========================================================================
   Sprite semántico TradeUp v239

   Los controles universales (cerrar, volver, buscar, menú, caret) siguen siendo
   vectores: necesitan máxima nitidez a 16–20 px y su forma ya es universal. Los
   contenedores que representan una IDEA —tienda, producto, ruta, formulario,
   persona, inventario, alerta— usan en cambio una ilustración del atlas 5 × 5.
   El <i> permanece en el DOM para conservar el significado y el fallback; sólo
   se oculta visualmente cuando el navegador soporta :has().
   =========================================================================== */
:where(
  .edicion-tool-icono, .form-tile-ic, .empty-icon, .fab-menu-op-ic,
  .us-stat-ic, .notif-card-icono, .ntf-confirm-icono, .config-diag-icono,
  .ncg-ic, .atajo-ico, .ruta-cta-ico, .ci-picker-ic, .ruta-tareas-cta-ic,
  .sd-kpi-ic, .rep-vacio-ic, .rep-tpl-ic, .feed-ubi-ic,
  .feed-ubi-tienda-ic, .proxima-tienda-ic, .afi-form-ico, .ac-perfil-ic,
  .vctx-ic, .tienda-hoy-ic, .ap-calendario-ic, .rot-bandeja-ic,
  .tr-ic, .sd-transito-ic, .perfil-seccion-ic, .sync-item-ic,
  .gamif-nudge-icono, .gamif-mision-icono
):has(> [class*="ph-"]) {
  --ts-sprite-x: 25%;
  --ts-sprite-y: 0%;
  overflow: visible;
  background-color: color-mix(in srgb, var(--color-alerta-fondo, #FFF4DE) 74%, transparent) !important;
  background-image: url('/assets/img/ui/tradeup-semantic-sprite-v239.png') !important;
  background-repeat: no-repeat !important;
  background-size: 500% 500% !important;
  background-position: var(--ts-sprite-x) var(--ts-sprite-y) !important;
  image-rendering: auto;
}
:where(
  .edicion-tool-icono, .form-tile-ic, .empty-icon, .fab-menu-op-ic,
  .us-stat-ic, .notif-card-icono, .ntf-confirm-icono, .config-diag-icono,
  .ncg-ic, .atajo-ico, .ruta-cta-ico, .ci-picker-ic, .ruta-tareas-cta-ic,
  .sd-kpi-ic, .rep-vacio-ic, .rep-tpl-ic, .feed-ubi-ic,
  .feed-ubi-tienda-ic, .proxima-tienda-ic, .afi-form-ico, .ac-perfil-ic,
  .vctx-ic, .tienda-hoy-ic, .ap-calendario-ic, .rot-bandeja-ic,
  .tr-ic, .sd-transito-ic, .perfil-seccion-ic, .sync-item-ic,
  .gamif-nudge-icono, .gamif-mision-icono
):has(> [class*="ph-"]) > [class*="ph-"] {
  opacity: 0 !important;
}

/* Fila 1 · comercio y producto. */
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-storefront,.ph-buildings,.ph-building-office,.ph-tree-structure,.ph-tree-view)) { --ts-sprite-x: 0%; --ts-sprite-y: 0%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-package,.ph-cube,.ph-squares-four,.ph-folder-simple-dashed)) { --ts-sprite-x: 25%; --ts-sprite-y: 0%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-barcode,.ph-scan,.ph-qr-code)) { --ts-sprite-x: 50%; --ts-sprite-y: 0%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-tag,.ph-currency-dollar,.ph-receipt)) { --ts-sprite-x: 75%; --ts-sprite-y: 0%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-camera,.ph-image,.ph-images,.ph-device-mobile-camera)) { --ts-sprite-x: 100%; --ts-sprite-y: 0%; }

/* Fila 2 · trabajo de campo. */
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-map-pin,.ph-map-pin-line,.ph-map-pin-area,.ph-crosshair)) { --ts-sprite-x: 0%; --ts-sprite-y: 25%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> .ph-map-trifold) { --ts-sprite-x: 25%; --ts-sprite-y: 25%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-path,.ph-navigation-arrow,.ph-bus,.ph-flag-checkered)) { --ts-sprite-x: 50%; --ts-sprite-y: 25%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-clipboard-text,.ph-list-checks,.ph-note,.ph-cards-three)) { --ts-sprite-x: 75%; --ts-sprite-y: 25%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-microphone,.ph-waveform)) { --ts-sprite-x: 100%; --ts-sprite-y: 25%; }

/* Fila 3 · análisis, personas, inventario y venta. */
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-chart-bar,.ph-chart-line,.ph-chart-line-up,.ph-trend-up,.ph-ranking,.ph-chart-pie-slice)) { --ts-sprite-x: 0%; --ts-sprite-y: 50%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-users-three,.ph-user-list,.ph-users)) { --ts-sprite-x: 25%; --ts-sprite-y: 50%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-user,.ph-identification-card,.ph-identification-badge)) { --ts-sprite-x: 50%; --ts-sprite-y: 50%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-fill,.ph-ruler,.ph-stack,.ph-archive)) { --ts-sprite-x: 75%; --ts-sprite-y: 50%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-shopping-cart-simple,.ph-shopping-bag-open,.ph-bag,.ph-basket)) { --ts-sprite-x: 100%; --ts-sprite-y: 50%; }

/* Fila 4 · mantenimiento del catálogo. */
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-upload-simple,.ph-file-xls,.ph-download-simple)) { --ts-sprite-x: 0%; --ts-sprite-y: 75%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-pencil-simple,.ph-note-pencil,.ph-pencil,.ph-wrench)) { --ts-sprite-x: 25%; --ts-sprite-y: 75%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-copy-simple,.ph-cards-three)) { --ts-sprite-x: 50%; --ts-sprite-y: 75%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-magic-wand,.ph-sparkle,.ph-text-aa,.ph-sliders)) { --ts-sprite-x: 75%; --ts-sprite-y: 75%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-arrows-merge,.ph-tree-view,.ph-tree-structure)) { --ts-sprite-x: 100%; --ts-sprite-y: 75%; }

/* Fila 5 · tiempo, alertas, soporte y confirmación. */
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-calendar-blank,.ph-calendar-dots,.ph-calendar-x,.ph-cake)) { --ts-sprite-x: 0%; --ts-sprite-y: 100%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-clock,.ph-clock-counter-clockwise,.ph-clock-countdown,.ph-hourglass)) { --ts-sprite-x: 25%; --ts-sprite-y: 100%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-warning,.ph-warning-circle,.ph-wifi-slash,.ph-lock-simple,.ph-x-circle,.ph-trash)) { --ts-sprite-x: 50%; --ts-sprite-y: 100%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-lifebuoy,.ph-info,.ph-question,.ph-phone)) { --ts-sprite-x: 75%; --ts-sprite-y: 100%; }
:where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic,.us-stat-ic,.notif-card-icono,.ntf-confirm-icono,.config-diag-icono,.ncg-ic,.atajo-ico,.ruta-cta-ico,.ci-picker-ic,.ruta-tareas-cta-ic,.sd-kpi-ic,.rep-vacio-ic,.rep-tpl-ic,.feed-ubi-ic,.feed-ubi-tienda-ic,.proxima-tienda-ic,.afi-form-ico,.ac-perfil-ic,.vctx-ic,.tienda-hoy-ic,.ap-calendario-ic,.rot-bandeja-ic,.tr-ic,.sd-transito-ic,.perfil-seccion-ic,.sync-item-ic,.gamif-nudge-icono,.gamif-mision-icono):has(> :is(.ph-check,.ph-check-circle,.ph-target,.ph-medal)) { --ts-sprite-x: 100%; --ts-sprite-y: 100%; }

/* Los accesos grandes ganan presencia; el resto conserva las medidas que cada
   componente ya tenía para no alterar tablas ni flujos móviles. */
.edicion-tool-icono { width: 58px; height: 58px; border-radius: 17px; }
.form-tile-ic { width: 46px; height: 46px; border-radius: 14px; }
.empty-state .empty-icon { width: 66px; height: 66px; border: 0; box-shadow: 0 8px 22px rgba(34,34,59,.12); }

@media (prefers-reduced-motion: reduce) {
  :where(.edicion-tool-icono,.form-tile-ic,.empty-icon,.fab-menu-op-ic) { transition: none !important; }
}

/* ===========================================================================
   Ilustraciones de navegación y respaldos fotográficos · v240

   El atlas 3 × 3 es exclusivo de la app de campo: convierte destinos y ajustes
   personales en objetos reconocibles sin reemplazar los controles universales.
   =========================================================================== */
.app-nav-img,
.perfil-opcion-img {
  --app-sprite-x: 0%;
  --app-sprite-y: 0%;
  display: inline-block;
  flex: 0 0 auto;
  background-image: url('/assets/img/ui/tradeup-app-sprite-v240.png');
  background-repeat: no-repeat;
  background-size: 300% 300%;
  background-position: var(--app-sprite-x) var(--app-sprite-y);
  image-rendering: auto;
}
.app-nav-img--inicio { --app-sprite-x: 0%; --app-sprite-y: 0%; }
.app-nav-img--almacenes { --app-sprite-x: 50%; --app-sprite-y: 0%; }
.app-nav-img--catalogo,
.app-nav-img--reportes { --app-sprite-x: 100%; --app-sprite-y: 0%; }
.app-nav-img--marcas,
.app-nav-img--nuevo { --app-sprite-x: 0%; --app-sprite-y: 50%; }
.app-nav-img--ruta { --app-sprite-x: 50%; --app-sprite-y: 50%; }
.app-nav-img--perfil { --app-sprite-x: 100%; --app-sprite-y: 50%; }
.perfil-opcion-img--foto { --app-sprite-x: 0%; --app-sprite-y: 100%; }
.perfil-opcion-img--telefono { --app-sprite-x: 50%; --app-sprite-y: 100%; }
.perfil-opcion-img--password { --app-sprite-x: 100%; --app-sprite-y: 100%; }

.app-sidebar .app-nav-img { width: 34px; height: 34px; margin: -5px 0; }
.app-sidebar a.activo .app-nav-img { filter: drop-shadow(0 3px 7px rgba(255,90,31,.34)); }
.bottom-nav .app-nav-img { width: 30px; height: 30px; margin: -3px 0 -1px; }
.bottom-nav a.activo .app-nav-img { filter: drop-shadow(0 2px 7px rgba(255,90,31,.75)); transform: translateY(-1px); }

/* Los accesos rápidos y los acordeones reutilizan exactamente las mismas tres
   imágenes: así "Foto", "Teléfono" y "Contraseña" no cambian de cara dentro
   de una sola pantalla. */
.perfil-atajos a > .perfil-opcion-img {
  background-color: color-mix(in srgb, var(--color-alerta-fondo) 76%, transparent);
  background-image: url('/assets/img/ui/tradeup-app-sprite-v240.png');
  background-size: 300% 300%;
  background-position: var(--app-sprite-x) var(--app-sprite-y);
}
.perfil-ajuste-ic.perfil-opcion-img {
  background-color: var(--color-superficie);
  background-image: url('/assets/img/ui/tradeup-app-sprite-v240.png');
  background-size: 300% 300%;
  background-position: var(--app-sprite-x) var(--app-sprite-y);
}
.perfil-ajuste[open] .perfil-ajuste-ic.perfil-opcion-img { background-color: var(--color-alerta-fondo); }

/* Una tienda o producto sin foto ya no parece un dato roto. El respaldo sigue
   diferenciándose de una foto real, pero ocupa el mismo encuadre y no desplaza
   el contenido cuando luego se añade la fotografía. */
.almacen-ficha-foto-btn .sin-foto,
.percha-almacen-foto--vacia {
  background-color: #F7F3EB;
  background-image: url('/assets/img/empty/almacen-placeholder-v240.jpg');
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}
.almacen-ficha-foto-btn .sin-foto > i,
.percha-almacen-foto--vacia > i { opacity: 0; }
.feed-card > .sin-foto {
  background-color: #F7F3EB;
  background-image: url('/assets/img/empty/producto-placeholder-v240.jpg');
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}
.feed-card > .sin-foto > i { opacity: 0; }
.almacen-sin-foto {
  min-height: 190px;
  background-color: #F7F3EB;
  background-image: url('/assets/img/empty/almacen-placeholder-v240.jpg');
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}
.almacen-sin-foto[data-mo="sinfoto"] { background-image: url('/assets/img/empty/producto-placeholder-v240.jpg'); }
.almacen-sin-foto > i { display: none; }
.almacen-sin-foto > span {
  margin-top: auto;
  padding: 5px 9px;
  border-radius: 999px;
  color: #22223B;
  background: rgba(247,243,235,.9);
  box-shadow: 0 2px 8px rgba(34,34,59,.12);
}
.catalogo-modelo-foto.es-icono,
.producto-hero-foto.es-icono { object-fit: cover; padding: 0; background: #F7F3EB; }

/* El saludo comparte lenguaje con el login, pero conserva una zona oscura
   limpia a la izquierda: nombre, fecha y CTA siguen teniendo contraste aun si
   el móvil recorta el extremo derecho de la escena. */
.page-hero--field {
  background-color: #17182A;
  background-image:
    linear-gradient(90deg, rgba(18,20,38,.2) 0 44%, rgba(18,20,38,.12) 64%, rgba(18,20,38,.08) 100%),
    url('/assets/img/brand/inicio-promotor-v240.jpg');
  background-repeat: no-repeat, no-repeat;
  background-position: center, right center;
  background-size: 100% 100%, auto 100%;
}
@media (max-width: 560px) {
  .page-hero--field {
    background-image:
      linear-gradient(90deg, rgba(18,20,38,.42) 0 52%, rgba(18,20,38,.18) 100%),
      url('/assets/img/brand/inicio-promotor-v240.jpg');
    background-size: 100% 100%, cover;
    background-position: center, center;
  }
  .page-hero--field .page-hero-copy { max-width: 74%; }
}

/* ===========================================================================
   CHECK-IN DESDE CASA (registro de precios fuera de horario)
   =========================================================================== */
/* Chip en la tarjeta de visita: misma pieza que "Estas aqui", otro color, para
   que se lea sin depender del icono. */
.ruta-card-remoto {
  display: inline-flex; align-items: center; gap: 5px;
  margin: 0 0 8px;
  font-size: var(--txt-xs, .72rem); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-acento-fuerte);
}
/* El aviso dentro de la barra "Estas en X". */
.vctx-remoto { display: block; margin-top: 2px; color: var(--color-acento-fuerte); font-size: var(--txt-sec, .78rem); }
/* La opcion del menu +: icono en ambar para distinguirla del check-in normal. */
.fab-menu-op-ic--remoto { background: var(--color-alerta-fondo); color: var(--color-acento-fuerte); }
/* Y la fila entera con un filo ambar: no es el check-in de siempre y tiene que
   notarse antes de leer. */
.fab-menu-op--remoto { border-left: 3px solid var(--color-acento); }

/* =========================================================================
   REGALOS A CLIENTES (admin/regalos.php). Estilos propios, sobre los tokens
   del tema: una barra de filtros de una sola fila, formulario en rejilla de
   12 columnas, resumen por tipo con barras y tres gráficos en fila.
   ========================================================================= */
.regalos-hero-acciones { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
.regalos-hero-acciones .btn { width: auto; }
.regalos-btn-hero { background: rgba(255,255,255,.10); color: #fff; border-color: rgba(255,255,255,.45); }
.regalos-btn-hero:hover, .regalos-btn-hero:focus-visible { background: rgba(255,255,255,.18); color: #fff; }
.regalos-volver { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: 6px; }
.regalos-volver:hover { text-decoration: underline; }

.regalos-toolbar { display: grid; grid-template-columns: minmax(220px, 1.6fr) minmax(150px, 1fr) minmax(150px, 1fr) minmax(170px, 1fr) auto; gap: 12px 14px; align-items: end; }
.regalos-toolbar--detalle { grid-template-columns: minmax(150px, 1fr) minmax(150px, 1fr) minmax(170px, 1fr) auto; }
.regalos-toolbar-campo { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.regalos-toolbar-campo label { font-size: var(--txt-sm, .82rem); font-weight: 700; }
.regalos-toolbar-campo input, .regalos-toolbar-campo select { width: 100%; }
.regalos-toolbar-acciones { display: flex; gap: 8px; align-items: center; }
.regalos-toolbar-acciones .btn { width: auto; white-space: nowrap; }
@media (max-width: 1100px) {
  .regalos-toolbar, .regalos-toolbar--detalle { grid-template-columns: 1fr 1fr; }
  .regalos-toolbar-q { grid-column: 1 / -1; }
  .regalos-toolbar-acciones { grid-column: 1 / -1; justify-content: flex-end; }
}
@media (max-width: 640px) { .regalos-toolbar, .regalos-toolbar--detalle { grid-template-columns: 1fr; } }

.regalos-split { display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(280px, 1fr); gap: 14px; align-items: start; }
@media (max-width: 1100px) { .regalos-split { grid-template-columns: 1fr; } }
.regalos-tarjeta-cab { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.regalos-tarjeta-cab h2 { margin-bottom: 2px; }
.regalos-tarjeta-cab h2 .text-suave { font-weight: 600; font-size: .85em; }

.regalos-form-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 12px 14px; }
.regalos-campo { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.regalos-campo label { font-size: var(--txt-sm, .82rem); font-weight: 700; }
.regalos-campo input, .regalos-campo select { width: 100%; }
.regalos-campo.col-3 { grid-column: span 3; }
.regalos-campo.col-4 { grid-column: span 4; }
.regalos-campo.col-5 { grid-column: span 5; }
.regalos-campo.col-6 { grid-column: span 6; }
@media (max-width: 900px) { .regalos-campo.col-3, .regalos-campo.col-4 { grid-column: span 6; } .regalos-campo.col-5 { grid-column: span 12; } }
@media (max-width: 560px) { .regalos-campo { grid-column: span 12 !important; } }
.regalos-input-usd { position: relative; }
.regalos-input-usd > span { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); font-weight: 700; color: var(--color-texto-suave, #6b6b7b); pointer-events: none; }
.regalos-input-usd > input { padding-left: 26px; }
.regalos-form-acciones { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 14px; }
.regalos-form-acciones .btn { width: auto; }

.regalos-tipos-lista { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.regalos-tipo-fila { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: var(--txt-md, .9rem); }
.regalos-tipo-nombre { font-weight: 700; min-width: 0; }
.regalos-tipo-nombre small, .regalos-tipo-monto small { display: block; font-weight: 500; }
.regalos-tipo-monto { text-align: right; font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums; }
.regalos-tipo-barra { height: 6px; border-radius: 3px; background: var(--color-superficie-alt, rgba(0,0,0,.08)); overflow: hidden; margin-top: 6px; }
.regalos-tipo-barra span { display: block; height: 100%; background: var(--color-acento, #E9940A); border-radius: 3px; }

.regalos-graf-3 { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 14px; }
.regalos-graf-3 > .tarjeta:nth-child(3) { grid-column: 1 / -1; }
.regalos-graf-3 > .tarjeta:nth-child(3) .percha-chart-wrap { height: 220px; }
@media (max-width: 1180px) { .regalos-graf-3 { grid-template-columns: 1fr; } }
.regalos-graf-3 .percha-chart-wrap:not([style]), .regalos-graf-2 .percha-chart-wrap:not([style]) { height: 240px; }

.regalos-tabla td, .regalos-tabla th { vertical-align: middle; }
.regalos-tabla .rep-num { font-variant-numeric: tabular-nums; }
.regalos-nowrap { white-space: nowrap; }
.regalos-cliente-link { font-weight: 700; color: inherit; text-decoration: none; }
.regalos-cliente-link:hover, .regalos-cliente-link:focus-visible { text-decoration: underline; color: var(--color-acento-texto, var(--color-acento, #E9940A)); }
.regalos-anular { padding: 4px 8px; }
.regalos-vacio .empty-state .btn { width: auto; }
.regalos-tabla th { white-space: nowrap; }
.regalos-tabla .regalos-detalle { min-width: 170px; }
.regalos-tabla td, .regalos-tabla th { padding-left: 10px; padding-right: 10px; }
.regalos-tabla td:first-child, .regalos-tabla th:first-child { padding-left: 6px; }
.regalos-tabla td:last-child, .regalos-tabla th:last-child { padding-right: 4px; }
.regalos-campo.col-2 { grid-column: span 2; }
@media (max-width: 900px) { .regalos-campo.col-2 { grid-column: span 6; } }
