/* =============================================================================
   Padrón — app.css
   =============================================================================
   Piel de la aplicación sobre el contrato de ng-template. Reglas que se
   respetan en todo el archivo:

     · Color: SÓLO var(--app-*) y var(--theme-*). Cero hex fuera de tokens.css.
       Los tonos derivados salen de color-mix() sobre esos tokens.
     · font-size en `em`. Layout en `rem`. line-height sin unidad.
     · Grillas: repeat(auto-fit, minmax(min(100%, <mín>), 1fr)). Nunca columnas
       fijas por viewport.
     · `dvh` nunca sola: siempre con un `vh` de respaldo antes.
     · Nada se corta ni desborda a partir de 320px.
     · success / warning / error son vocabulario semántico de estado. NO son
       colores de marca y no se usan para teñir identidad.
     · No hay tokens de espaciado, radio ni motion en el contrato: donde hace
       falta una medida va el valor concreto, no una escala paralela.

   ─────────────────────────────────────────────────────────────────────────────
   DOS REGLAS DE ESTA PASADA, Y NO SON COSMÉTICAS
   ─────────────────────────────────────────────────────────────────────────────

   1 · TIPOGRAFÍA CON PISO DE 1em EN TODO LO QUE SE TOCA.
       Safari en iPhone hace zoom al enfocar un campo cuyo font-size computa por
       debajo de 16px, y después deja la página desalineada. Como `em` compone,
       un `1em` dentro de un contenedor de `0.88em` computa 14px y dispara el
       zoom igual. Por eso los contenedores que envuelven controles —`.campo`,
       `.filtros`, `.tabla`, `.dialogo__cuerpo`— se plantan en 1em, y todo lo
       que es control, ícono, botón, chip o fila de tabla vive en 1em.
       Sólo la metadata secundaria baja, y nunca por debajo de 0.8125em (13px):
       ahí no hay ni un control ni un ícono.

   2 · CONTRASTE: EL NARANJA DE `warning` NO SIRVE COMO TINTA.
       `--app-warning` sobre fondo claro llega a 2.4:1 y sobre su propio tinte
       a 3.4:1 — no alcanza para texto. El contrato ya trae la respuesta:
       `--app-on-warning` es NEGRO justamente porque el naranja se usa como
       FONDO, no como tinta. Así que todo lo que antes era texto naranja pasó a
       ser una superficie naranja con tinta negra: 7.9:1, más fuerte y sin
       pedirle al rojo de error que signifique algo que no significa.
   ============================================================================= */

/* --- 1 · Reset y base ----------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--theme-bg);
  color: var(--theme-text);
  font-family: var(--secondary-font);
  font-size: 1em;
  line-height: 1.875;
  -webkit-font-smoothing: antialiased;
}

img, svg, canvas { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }

:focus-visible {
  outline: 0.15rem solid var(--app-primary);
  outline-offset: 0.15rem;
  border-radius: 0.2rem;
}
/* Sobre el fondo oscuro el azul de marca queda en 3:1 justo: el foco se pinta
   con el derivado claro, que llega a 11:1. */
[data-theme='dark'] :focus-visible { outline-color: var(--app-primary-light); }

::selection {
  background: var(--app-primary-light);
  color: var(--app-neutral-dark);
}

/* El texto de sugerencia del navegador es gris claro por defecto y no llega a
   contraste: se fija en el token de texto secundario (5.7:1). */
::placeholder { color: var(--theme-text-secondary); opacity: 1; }

/* --- 2 · Tipografía ------------------------------------------------------- */

h1, h2, h3, h4 {
  font-family: var(--primary-font);
  font-weight: var(--font-semibold);
  line-height: 1.25;
  margin: 0;
  text-wrap: balance;
}

h1 { font-size: 1.6em; letter-spacing: -0.01em; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.05em; }
h4 { font-size: 1em; }

p { margin: 0; text-wrap: pretty; }

/* Open Sans se sirve en 300 y 400 y nada más — es lo que trae ng-template.
   Así que TODO lo que necesita peso va en Poppins, que sí tiene 500, 600 y 700
   servidos. Pedirle negrita a una familia que no la tiene se la inventa el
   navegador, y eso es una falla de tipografía disfrazada de detalle. */
strong, b {
  font-family: var(--primary-font);
  font-weight: var(--font-semibold);
}

.texto-secundario { color: var(--theme-text-secondary); }

/* Piso de la metadata secundaria: 14px. No lleva controles ni íconos. */
.texto-chico { font-size: 0.875em; line-height: 1.6; }

/* Identidad de lote, importes, fechas y todo lo tabular. */
.mono {
  font-family: var(--monospace-font);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.etiqueta {
  font-family: var(--terciary-font);
  font-weight: var(--font-semibold);
  font-size: 0.8125em;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  line-height: 1.5;
  color: var(--theme-text-secondary);
}

/* --- 3 · Íconos ----------------------------------------------------------- */
/* La familia es 'Material Symbols Outlined'. Cualquier nombre usado en el
   marcado tiene que existir en esa familia.

   Los íconos comparten el piso de 1em del texto: a 16px la variante óptica
   correcta de Material Symbols es `opsz 20`, no la de 24. `.ms--chico` se
   conserva como alias para no tocar el marcado, pero ya no achica: un ícono
   por debajo de 16px al lado de texto de 16px se lee como un error de render. */

.ms {
  font-family: 'Material Symbols Outlined';
  font-weight: normal;
  font-style: normal;
  font-size: 1em;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-block;
  white-space: nowrap;
  direction: ltr;
  font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;
  -webkit-font-feature-settings: 'liga';
  font-feature-settings: 'liga';
  -webkit-font-smoothing: antialiased;
  user-select: none;
  flex: none;
}

.ms--relleno { font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 20; }
.ms--chico { font-size: 1em; }
.ms--grande { font-size: 1.5em; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; }

/* --- 4 · Shell: header + sidenav + main ---------------------------------- */

.encabezado {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 30;
  height: var(--header-height);
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding-inline: 1rem;
  background: var(--theme-header-bg);
  color: var(--theme-header-text);
  box-shadow: 0 0.1rem 0.4rem rgb(0 0 0 / 0.18);
}

.encabezado__marca {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-width: 0;
}

.encabezado__marca-nombre {
  font-family: var(--primary-font);
  font-weight: var(--font-bold);
  font-size: 1.15em;
  letter-spacing: 0.01em;
  line-height: 1.2;
}

.encabezado__marca-bajada {
  display: none;
  font-size: 0.8125em;
  line-height: 1.4;
  opacity: 0.9;
}

.encabezado__espacio { flex: 1 1 auto; }

.encabezado__accion {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding-inline: 0.5rem;
  border: 0;
  border-radius: 0.5rem;
  background: transparent;
  color: inherit;
  font-size: 1em;
  transition: background 0.2s ease-in-out;
}

.encabezado__accion:hover { background: rgb(255 255 255 / 0.16); }

.encabezado__usuario {
  display: none;
  align-items: center;
  gap: 0.5rem;
  padding-inline: 0.5rem 0.75rem;
}

.encabezado__avatar {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 2rem;
  display: grid;
  place-items: center;
  background: rgb(255 255 255 / 0.22);
  font-family: var(--primary-font);
  font-size: 0.875em;
  font-weight: var(--font-semibold);
  line-height: 1;
}

.encabezado__usuario-datos { line-height: 1.35; }
.encabezado__usuario-nombre { font-family: var(--primary-font); font-size: 0.9375em; font-weight: var(--font-semibold); }
.encabezado__usuario-rol { font-size: 0.8125em; opacity: 0.9; }

/* Sidenav ------------------------------------------------------------------ */

.lateral {
  position: fixed;
  top: var(--header-height);
  left: 0;
  bottom: 0;
  z-index: 25;
  width: var(--navbar-width);
  max-width: 18rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.75rem 0.6rem 2rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--theme-sidenav-bg);
  color: var(--theme-sidenav-text);
  border-right: 0.0625rem solid var(--theme-border);
  transform: translateX(0);
  transition: transform 0.2s ease-in-out;
}

.lateral__grupo {
  padding: 0.9rem 0.75rem 0.35rem;
}

.lateral__item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 2.75rem;
  padding: 0.4rem 0.75rem;
  border-radius: 0.5rem;
  color: inherit;
  font-family: var(--primary-font);
  font-weight: var(--font-medium);
  font-size: 1em;
  line-height: 1.4;
  transition: background 0.2s ease-in-out, color 0.2s ease-in-out;
}

.lateral__item:hover { background: var(--theme-sidenav-hover); }

.lateral__item[aria-current='page'] {
  background: var(--theme-sidenav-active-bg);
  color: var(--theme-sidenav-active-text);
  font-weight: var(--font-semibold);
}

.lateral__item-contador {
  margin-left: auto;
  font-family: var(--monospace-font);
  font-size: 0.875em;
  color: var(--theme-text-secondary);
}

.lateral__item[aria-current='page'] .lateral__item-contador { color: inherit; }

.lateral__pie {
  margin-top: auto;
  padding: 1rem 0.75rem 0;
  border-top: 0.0625rem solid var(--theme-border);
  font-size: 0.8125em;
  line-height: 1.6;
  color: var(--theme-text-secondary);
}

.velo {
  position: fixed;
  inset: var(--header-height) 0 0 0;
  z-index: 24;
  background: rgb(0 0 0 / 0.42);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease-in-out;
}

/* Main --------------------------------------------------------------------- */

.principal {
  padding: 1.25rem 1rem 4rem;
  margin-top: var(--header-height);
  min-height: calc(100vh - var(--header-height));
  min-height: calc(100dvh - var(--header-height));
}

.principal__ancho { max-width: 90rem; margin-inline: auto; }

/* Estado del shell por ancho ---------------------------------------------- */

@media (max-width: 59.99rem) {
  .lateral { transform: translateX(-101%); box-shadow: 0.2rem 0 1rem rgb(0 0 0 / 0.25); }
  body[data-lateral='abierto'] .lateral { transform: translateX(0); }
  body[data-lateral='abierto'] .velo { opacity: 1; pointer-events: auto; }
}

@media (min-width: 60rem) {
  .encabezado__marca-bajada { display: block; }
  .encabezado__usuario { display: flex; }
  .encabezado__accion--menu { display: none; }
  .principal { padding-inline: 1.75rem; margin-left: 18rem; }
}

@media (min-width: 80rem) {
  .principal { padding-inline: 2.5rem; }
}

/* --- 5 · Grillas ---------------------------------------------------------- */

.grilla { display: grid; gap: 1rem; }
.grilla--cards { grid-template-columns: var(--grid-profile-card); }
.grilla--anchas { grid-template-columns: var(--grid-container-width); }
.grilla--campos { grid-template-columns: var(--forma-input-width); }
.grilla--bloques { grid-template-columns: var(--grid-complete-width); }

/* Maestro–detalle: un listado con su ficha al lado NO es una grilla de cards.
   Repartido mitad y mitad, la tabla se estrangula y un teléfono se parte al
   medio — que es peor que no mostrarlo. La ficha muestra pocas líneas y puede
   ser angosta; el listado se queda con lo que sobra. Debajo de 960px
   (isSmallOrBelow, el corte documentado) se apilan. */
.maestro-detalle { display: grid; gap: 1rem; align-items: start; }

@media (min-width: 60rem) {
  .maestro-detalle {
    grid-template-columns: minmax(0, 1fr) minmax(min(100%, 20rem), 24rem);
    gap: 1.25rem;
  }
}

.pila { display: flex; flex-direction: column; gap: 1rem; }
.pila--apretada { gap: 0.5rem; }
.fila { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.fila--entre { justify-content: space-between; }
.fila--fin { justify-content: flex-end; }

/* --- 6 · Encabezado de página -------------------------------------------- */

.cabecera-pagina {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.25rem;
}

.cabecera-pagina__texto { min-width: min(100%, 18rem); }
.cabecera-pagina__bajada {
  margin-top: 0.15rem;
  color: var(--theme-text-secondary);
  font-size: 1em;
  line-height: 1.6;
  max-width: 62ch;
}

.miga {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.875em;
  color: var(--theme-text-secondary);
  margin-bottom: 0.3rem;
}
.miga a:hover { color: var(--app-primary); text-decoration: underline; }

/* --- 7 · Botones ---------------------------------------------------------- */

.boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  min-height: 2.75rem;
  padding: 0.4rem 1.1rem;
  border: 0.0625rem solid transparent;
  border-radius: 0.5rem;
  background: transparent;
  font-family: var(--primary-font);
  font-weight: var(--font-medium);
  font-size: 1em;
  line-height: 1.4;
  transition: background 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out;
}

.boton[disabled] {
  color: var(--theme-text-disabled);
  border-color: var(--theme-border);
  background: transparent;
  cursor: not-allowed;
}

.boton--primario {
  background: var(--app-primary);
  color: var(--app-on-primary);
  border-color: var(--app-primary);
}
.boton--primario:hover:not([disabled]) { background: var(--app-primary-dark); border-color: var(--app-primary-dark); }

.boton--tonal {
  background: var(--app-primary-container);
  color: var(--app-primary-dark);
  border-color: transparent;
}
.boton--tonal:hover:not([disabled]) { background: var(--app-primary-light); }

.boton--contorno {
  border-color: var(--app-neutral-variant);
  color: var(--theme-text);
}
.boton--contorno:hover:not([disabled]) { border-color: var(--app-primary); color: var(--app-primary); }

.boton--texto { padding-inline: 0.6rem; color: var(--app-primary); }
.boton--texto:hover:not([disabled]) { background: var(--app-primary-container); }

/* Vocabulario semántico: sólo para acciones destructivas reales. */
.boton--destructivo { border-color: var(--app-error); color: var(--app-error); }
.boton--destructivo:hover:not([disabled]) { background: var(--app-error); color: var(--app-on-error); }

.boton--bloque { width: 100%; }
.boton--grande { min-height: 3.25rem; font-size: 1.0625em; }

.boton-icono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  border: 0;
  border-radius: 0.5rem;
  background: transparent;
  color: var(--theme-text-secondary);
  font-size: 1em;
  transition: background 0.2s ease-in-out, color 0.2s ease-in-out;
}
.boton-icono:hover { background: var(--theme-sidenav-hover); color: var(--app-primary); }

/* En oscuro el azul de marca como TINTA cae a 3:1 sobre el fondo de página. Se
   usa el derivado claro, que llega a 11:1, y los rellenos tonales pasan a ser
   un tinte del primario en vez de una superficie clara sobre fondo oscuro. */
[data-theme='dark'] .boton--texto,
[data-theme='dark'] .boton-icono:hover,
[data-theme='dark'] .boton--contorno:hover:not([disabled]),
[data-theme='dark'] .miga a:hover { color: var(--app-primary-light); }

[data-theme='dark'] .boton--contorno:hover:not([disabled]) { border-color: var(--app-primary-light); }

[data-theme='dark'] .boton--texto:hover:not([disabled]) {
  background: color-mix(in oklch, var(--app-primary) 26%, transparent);
}

[data-theme='dark'] .boton--tonal {
  background: color-mix(in oklch, var(--app-primary) 30%, transparent);
  color: var(--app-primary-light);
}
[data-theme='dark'] .boton--tonal:hover:not([disabled]) {
  background: color-mix(in oklch, var(--app-primary) 42%, transparent);
}

/* --- 8 · Superficies ------------------------------------------------------ */

.panel {
  background: var(--theme-bg-elevated);
  border: 0.0625rem solid var(--theme-border);
  border-radius: 0.5rem;
  padding: 1.15rem;
}

.panel--plano { padding: 0; overflow: hidden; }

.panel__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 1rem 1.15rem;
  border-bottom: 0.0625rem solid var(--theme-border);
}

.panel__cuerpo { padding: 1.15rem; }

.panel__pie {
  padding: 0.85rem 1.15rem;
  border-top: 0.0625rem solid var(--theme-border);
  background: var(--theme-bg-surface);
}

/* Indicadores ------------------------------------------------------------- */

.indicador {
  background: var(--theme-bg-elevated);
  border: 0.0625rem solid var(--theme-border);
  border-radius: 0.5rem;
  padding: 1rem 1.15rem;
}

.indicador__valor {
  font-family: var(--monospace-font);
  font-variant-numeric: tabular-nums;
  font-weight: var(--font-semibold);
  font-size: 2em;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.indicador__icono { color: var(--app-neutral); }

.indicador__pie {
  margin-top: 0.35rem;
  font-size: 0.875em;
  line-height: 1.6;
  color: var(--theme-text-secondary);
}

/* El indicador de alerta se PINTA de naranja con tinta negra (7.9:1) en vez de
   escribir el número en naranja sobre blanco (2.4:1, ilegible). Es el único
   bloque relleno de la pantalla: eso lo hace ruidoso sin gritar. */
.indicador--alerta {
  background: var(--app-warning);
  border-color: var(--app-warning);
  color: var(--app-on-warning);
}
.indicador--alerta .indicador__valor,
.indicador--alerta .indicador__pie,
.indicador--alerta .indicador__icono { color: var(--app-on-warning); }

/* --- 9 · Tablas ----------------------------------------------------------- */
/* La densidad la da el padding, no un texto más chico: la tabla se queda en 1em
   porque adentro viven selects y campos, y un control por debajo de 16px hace
   que Safari en iPhone haga zoom al enfocarlo. */

.tabla-marco { overflow-x: auto; }

.tabla {
  width: 100%;
  border-collapse: collapse;
  font-size: 1em;
  line-height: 1.5;
}

.tabla thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--theme-bg-surface);
  color: var(--theme-text-secondary);
  font-family: var(--terciary-font);
  font-weight: var(--font-semibold);
  font-size: 0.8125em;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
  padding: 0.65rem 0.85rem;
  border-bottom: 0.0625rem solid var(--theme-border);
}

.tabla tbody td {
  padding: 0.7rem 0.85rem;
  border-bottom: 0.0625rem solid var(--theme-divider);
  /* ARRIBA, no al medio. Casi todas las celdas de esta app llevan una segunda
     línea (el loteo bajo el lote, el agente bajo el contacto, la observación
     bajo el estado). Centrado, cada celda se acomoda contra su propia altura y
     la primera línea de cada columna cae en una y distinta: la fila se lee
     desalineada aunque las columnas estén perfectas. Alineadas arriba, las
     primeras líneas comparten el mismo borde superior. */
  vertical-align: top;
}

.tabla tbody tr:hover { background: var(--theme-bg-surface); }
.tabla tbody tr:last-child td { border-bottom: 0; }

/* Fila de grupo DENTRO de la tabla (por día, por loteo, por lo que sea).
   Existe para no tener que partir la tabla en una por grupo: cada <table> es su
   propio contexto de layout, así que varias tablas apiladas calculan sus anchos
   por separado y las columnas dejan de alinearse entre grupos. Una sola tabla
   con filas de grupo mantiene un único juego de anchos de arriba a abajo. */
.tabla__grupo th {
  position: static;
  background: var(--theme-bg-surface);
  color: var(--theme-text-secondary);
  font-family: var(--terciary-font);
  font-weight: var(--font-semibold);
  font-size: 0.8125em;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-align: left;
  padding: 0.75rem 0.85rem 0.55rem;
  border-top: 0.0625rem solid var(--theme-border);
  border-bottom: 0.0625rem solid var(--theme-border);
}
.tabla tbody tr.tabla__grupo:hover { background: transparent; }
.tabla tbody tr.tabla__grupo:first-child th { border-top: 0; }

/* Dos columnas de la agenda no se estiran: la hora son siempre cinco
   caracteres y la acción es un botón. El resto reparte lo que queda.
   La hora se alinea a la izquierda: es una etiqueta, no una cantidad, y
   pegada al borde derecho de su columna quedaba lejos del lote. */
.tabla--agenda__hora { width: 5rem; text-align: left; }
.tabla--agenda__accion { width: 1%; white-space: nowrap; }

/* Un control táctil mide 2.75rem y una línea de texto 1.5em: alineados arriba,
   el control queda visiblemente más abajo que el texto de al lado porque su
   caja es más alta. Se lo sube para que compartan el mismo borde superior.
   Vale para el botón de acción, la casilla de tareas y la fila de íconos. */
.tabla tbody td > .boton,
.tabla tbody td > label,
.tabla tbody td > .fila { margin-top: -0.35rem; }

.tabla .num { text-align: right; font-family: var(--monospace-font); font-variant-numeric: tabular-nums; }
.tabla__id { font-family: var(--monospace-font); font-weight: var(--font-semibold); white-space: nowrap; }
.tabla__sec { display: block; font-size: 0.875em; line-height: 1.5; color: var(--theme-text-secondary); }

.tabla tbody tr[data-inactivo='true'] td { opacity: 0.6; }

/* Cualquier control dentro de una tabla arranca de 1em limpio. */
.tabla input,
.tabla select,
.tabla textarea {
  font-size: 1em;
  min-height: 2.75rem;
  padding: 0.4rem 0.6rem;
  border: 0.0625rem solid var(--app-neutral-variant);
  border-radius: 0.4rem;
  background: var(--theme-bg-elevated);
  color: var(--mat-form-field-outlined-input-text-color);
}
.tabla input[type='checkbox'] { min-height: 0; }

/* Debajo de 600px la tabla se apila: cada fila es una ficha y cada celda
   lleva su etiqueta. Los dos ejes siguen visibles sin abrir nada. */
@media (max-width: 37.49rem) {
  .tabla thead { position: absolute; width: 0.0625rem; height: 0.0625rem; overflow: hidden; clip-path: inset(50%); }
  .tabla, .tabla tbody, .tabla tr, .tabla td, .tabla tbody th { display: block; width: 100%; }
  .tabla tr {
    border: 0.0625rem solid var(--theme-border);
    border-radius: 0.5rem;
    padding: 0.6rem 0.75rem;
    margin-bottom: 0.6rem;
    background: var(--theme-bg-elevated);
  }
  /* Apilada, la fila de grupo no es una ficha más: es el rótulo que las separa. */
  .tabla tr.tabla__grupo {
    border: 0;
    border-radius: 0;
    background: transparent;
    padding: 0.85rem 0 0.35rem;
    margin-bottom: 0;
  }
  .tabla tr.tabla__grupo th { border: 0; background: transparent; padding: 0; }
  .tabla tbody td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Envuelve: a 320px un chip de ocupación y su etiqueta no entran en la
       misma línea, y preferimos dos renglones antes que scroll horizontal. */
    flex-wrap: wrap;
    gap: 0.35rem 0.75rem;
    padding: 0.3rem 0;
    border-bottom: 0;
    text-align: left;
  }
  .tabla tbody td::before {
    content: attr(data-col);
    font-family: var(--terciary-font);
    font-weight: var(--font-semibold);
    font-size: 0.8125em;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--theme-text-secondary);
    flex: none;
  }
  .tabla tbody td:empty { display: none; }
  .tabla .num { text-align: right; }
  /* Apilada, la celda centra su contenido: el ajuste de alineación vertical de
     la vista de tabla no corresponde y se anula. */
  .tabla tbody td > .boton,
  .tabla tbody td > label,
  .tabla tbody td > .fila { margin-top: 0; }
}

/* --- 10 · Los dos ejes de estado ----------------------------------------- */
/* Canal 1 · VENTA — chip relleno, va primero en el orden de lectura.
   Canal 2 · OCUPACIÓN — chip contorneado con ícono, trama y texto.
   Nunca se colapsan en un semáforo único, y ninguno de los dos vive detrás de
   un hover, un tooltip ni un segundo click. Los dos se leen sin color: cada
   uno tiene forma e ícono propios. */

.eje {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-height: 2rem;
  padding: 0.15rem 0.7rem;
  border-radius: 2rem;
  font-family: var(--primary-font);
  font-size: 1em;
  font-weight: var(--font-semibold);
  line-height: 1.4;
  white-space: nowrap;
}

/* Canal 1 — venta (derivado de las etapas de las oportunidades) */
.eje-venta { border: 0.0625rem solid transparent; }
.eje-venta[data-estado='DISPONIBLE'] { background: var(--app-primary); color: var(--app-on-primary); }
.eje-venta[data-estado='RESERVADO'] { background: var(--app-tertiary); color: var(--app-on-tertiary); }
.eje-venta[data-estado='VENDIDO'] {
  background: var(--app-neutral-dark);
  color: var(--app-neutral-surface);
}
[data-theme='dark'] .eje-venta[data-estado='VENDIDO'] {
  background: var(--app-neutral-light);
  color: var(--app-neutral-dark);
}

/* Canal 2 — ocupación (observada en el campo, no se deriva de nada) */
.eje-ocupacion {
  border: 0.09rem solid var(--app-neutral-variant);
  background: var(--theme-bg-elevated);
  color: var(--theme-text);
}

.eje-ocupacion[data-situacion='LIBRE'] {
  border-style: solid;
  color: var(--theme-text-secondary);
}

.eje-ocupacion[data-situacion='EN_NEGOCIACION'] {
  border-style: dashed;
  border-color: var(--app-secondary);
  color: var(--app-secondary-dark);
}

.eje-ocupacion[data-situacion='REGULARIZADO'] {
  border-color: var(--app-secondary);
  background: var(--app-secondary-container);
  color: var(--app-secondary-dark);
}

/* En oscuro el teal profundo sobre la superficie elevada cae a 2.2:1: se
   invierte la pareja —tinte de fondo + tinta clara— sin tocar ningún token. */
[data-theme='dark'] .eje-ocupacion[data-situacion='EN_NEGOCIACION'] {
  color: var(--app-secondary-light);
  border-color: var(--app-secondary-light);
}
[data-theme='dark'] .eje-ocupacion[data-situacion='REGULARIZADO'] {
  background: color-mix(in oklch, var(--app-secondary) 32%, transparent);
  border-color: var(--app-secondary-light);
  color: var(--app-secondary-light);
}

/* Las dos formas de ocupación llevan trama: se distinguen sin color y sin
   pedirle al rojo de error que signifique algo que no significa. Un lote
   usurpado no es un fallo del sistema, es un estado normal de la operación. */
.eje-ocupacion[data-situacion='OCUPADO_BUENA_FE'] {
  border-color: var(--app-neutral-variant);
  color: var(--app-neutral-variant-dark);
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 0.2rem,
    color-mix(in oklch, var(--app-neutral-variant) 22%, transparent) 0.2rem 0.4rem
  );
}
[data-theme='dark'] .eje-ocupacion[data-situacion='OCUPADO_BUENA_FE'] { color: var(--app-neutral-variant-light); }

.eje-ocupacion[data-situacion='OCUPADO_USURPADOR'] {
  border-width: 0.13rem;
  border-color: var(--app-neutral-dark);
  color: var(--app-neutral-dark);
  font-weight: var(--font-bold);
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 0.13rem,
    color-mix(in oklch, var(--app-neutral-dark) 30%, transparent) 0.13rem 0.3rem
  );
}
[data-theme='dark'] .eje-ocupacion[data-situacion='OCUPADO_USURPADOR'] {
  border-color: var(--app-neutral-light);
  color: var(--app-neutral-light);
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 0.13rem,
    color-mix(in oklch, var(--app-neutral-light) 30%, transparent) 0.13rem 0.3rem
  );
}

/* El derivado que hace consultable el peligro. Acá manda `warning`, y manda
   como SUPERFICIE: naranja de fondo, tinta negra (`--app-on-warning` existe
   justamente para esto). Escribirlo en naranja sobre claro daba 2.4:1. */
.sello-bloqueo {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 1.9rem;
  padding: 0.1rem 0.6rem;
  border: 0.0625rem solid var(--app-warning);
  border-radius: 0.35rem;
  background: var(--app-warning);
  color: var(--app-on-warning);
  font-family: var(--terciary-font);
  font-weight: var(--font-semibold);
  font-size: 0.8125em;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.5;
  white-space: nowrap;
}

.celda-ejes { display: flex; flex-direction: column; gap: 0.3rem; align-items: flex-start; }

/* --- 11 · Chips genéricos y estados varios ------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 2rem;
  padding: 0.1rem 0.65rem;
  border-radius: 2rem;
  border: 0.0625rem solid var(--theme-border);
  background: var(--theme-bg-surface);
  color: var(--theme-text-secondary);
  font-family: var(--primary-font);
  font-size: 0.9375em;
  line-height: 1.4;
  white-space: nowrap;
}

.chip--etapa { border-color: var(--app-tertiary); color: var(--app-tertiary-dark); background: var(--app-tertiary-container); }
.chip--ok { border-color: var(--app-success); color: var(--result-success-text); background: var(--result-success-bg); }
/* El par timeout (naranja sobre crema) da 3.4:1: la tinta pasa a neutral
   oscuro y el naranja se queda en el borde, que es donde sí funciona. */
.chip--pendiente { border-color: var(--app-warning); color: var(--app-neutral-dark); background: var(--result-timeout-bg); }
.chip--fallo { border-color: var(--app-error); color: var(--result-failure-text); background: var(--result-failure-bg); }

[data-theme='dark'] .chip--etapa {
  background: color-mix(in oklch, var(--app-tertiary) 32%, transparent);
  border-color: var(--app-tertiary-light);
  color: var(--app-tertiary-light);
}
[data-theme='dark'] .chip--pendiente { color: var(--result-timeout-text); }

/* Los chips de filtro son controles, no etiquetas: pasan los 2.75rem porque se
   tocan con el pulgar, en la calle. */
.chip--seleccionable { cursor: pointer; min-height: 2.75rem; padding-inline: 0.85rem; }
.chip--seleccionable[aria-pressed='true'] {
  border-color: var(--app-primary);
  background: var(--app-primary-container);
  color: var(--app-primary-dark);
  font-weight: var(--font-semibold);
}
[data-theme='dark'] .chip--seleccionable[aria-pressed='true'] {
  background: color-mix(in oklch, var(--app-primary) 32%, transparent);
  color: var(--app-primary-light);
}

/* --- 12 · Filtros --------------------------------------------------------- */

.filtros {
  display: grid;
  gap: 0.9rem;
  padding: 1rem 1.15rem;
  border-bottom: 0.0625rem solid var(--theme-border);
  background: var(--theme-bg-elevated);
  font-size: 1em;
}

.filtros__grupo { display: flex; flex-direction: column; gap: 0.45rem; }
.filtros__opciones { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.filtros__resumen {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  font-size: 0.9375em;
  color: var(--theme-text-secondary);
}

/* --- 13 · Formularios ----------------------------------------------------- */
/* `.campo` se planta en 1em: es el envoltorio de todo control de la app y corta
   cualquier composición de `em` que venga de arriba. Sin esto, un campo dentro
   de un contenedor de 0.88em computa 14px y Safari en iPhone hace zoom al
   enfocarlo. */

.campo { display: flex; flex-direction: column; gap: 0.35rem; font-size: 1em; }

.campo__label {
  font-family: var(--primary-font);
  font-size: 0.875em;
  font-weight: var(--font-semibold);
  line-height: 1.5;
  color: var(--theme-text-secondary);
}

.campo__ayuda { font-size: 0.8125em; line-height: 1.6; color: var(--theme-text-secondary); }

.campo__control,
.campo select,
.campo input,
.campo textarea {
  width: 100%;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  border: 0.0625rem solid var(--app-neutral-variant);
  border-radius: 0.4rem;
  background: var(--theme-bg-elevated);
  color: var(--mat-form-field-outlined-input-text-color);
  font-size: 1em;
  line-height: 1.5;
  transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}

.campo textarea { min-height: 6rem; resize: vertical; }

.campo input:focus,
.campo select:focus,
.campo textarea:focus {
  border-color: var(--app-primary);
  box-shadow: 0 0 0 0.0625rem var(--app-primary);
  outline: none;
}
[data-theme='dark'] .campo input:focus,
[data-theme='dark'] .campo select:focus,
[data-theme='dark'] .campo textarea:focus {
  border-color: var(--app-primary-light);
  box-shadow: 0 0 0 0.0625rem var(--app-primary-light);
}

.campo--error input,
.campo--error select,
.campo--error textarea { border-color: var(--app-error); }
.campo__error { font-size: 0.8125em; line-height: 1.6; color: var(--app-error); }
[data-theme='dark'] .campo__error { color: var(--result-failure-text); }

.campo--grande input { min-height: 3.5rem; font-size: 1.15em; }

/* Interruptor de dos posiciones */
.interruptor { display: inline-flex; align-items: center; gap: 0.6rem; min-height: 2.75rem; cursor: pointer; font-size: 1em; }
.interruptor input { position: absolute; opacity: 0; pointer-events: none; }
.interruptor__pista {
  width: 2.6rem;
  height: 1.5rem;
  border-radius: 2rem;
  /* El gris claro del apagado quedaba en 1.9:1 contra el fondo: un control
     tiene que llegar a 3:1 para que se vea que está ahí. */
  background: var(--app-neutral);
  position: relative;
  transition: background 0.2s ease-in-out;
  flex: none;
}
.interruptor__pista::after {
  content: '';
  position: absolute;
  top: 0.2rem;
  left: 0.2rem;
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 2rem;
  background: var(--theme-bg-elevated);
  transition: transform 0.2s ease-in-out;
}
.interruptor input:checked + .interruptor__pista { background: var(--app-primary); }
.interruptor input:checked + .interruptor__pista::after { transform: translateX(1.1rem); }
.interruptor input:focus-visible + .interruptor__pista { outline: 0.15rem solid var(--app-primary); outline-offset: 0.15rem; }

/* --- 14 · Estados de la superficie: vacío, cargando, error --------------- */

.estado {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  text-align: center;
  padding: 3rem 1.5rem;
  color: var(--theme-text-secondary);
}

/* El gris de outline daba 2.9:1 y un ícono que comunica necesita 3:1. */
.estado__icono { color: var(--app-neutral); }
.estado__titulo { font-family: var(--primary-font); font-weight: var(--font-semibold); font-size: 1.05em; color: var(--theme-text); }
.estado__texto { max-width: 46ch; font-size: 1em; line-height: 1.6; }

.estado--error { border-top: 0.19rem solid var(--app-error); }
.estado--error .estado__icono { color: var(--app-error); }
[data-theme='dark'] .estado--error .estado__icono { color: var(--result-failure-text); }

.esqueleto {
  height: 1rem;
  border-radius: 0.25rem;
  background: linear-gradient(
    90deg,
    var(--theme-bg-surface) 0%,
    color-mix(in oklch, var(--app-neutral-light) 45%, transparent) 50%,
    var(--theme-bg-surface) 100%
  );
  background-size: 200% 100%;
  animation: esqueleto-brillo 1.4s ease-in-out infinite;
}
.esqueleto + .esqueleto { margin-top: 0.6rem; }
.esqueleto--corto { width: 40%; }
.esqueleto--medio { width: 68%; }

@keyframes esqueleto-brillo {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

/* --- 15 · Aviso en línea -------------------------------------------------- */

.aviso {
  display: flex;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border: 0.0625rem solid var(--theme-border);
  border-radius: 0.5rem;
  background: var(--theme-bg-surface);
  font-size: 1em;
  line-height: 1.6;
}

.aviso__titulo { font-family: var(--primary-font); font-weight: var(--font-semibold); display: block; }

.aviso--traba { border-color: var(--app-warning); background: color-mix(in oklch, var(--app-warning) 12%, transparent); }
/* El ícono de la traba es un disco naranja con glifo negro (7.9:1) en vez de
   un glifo naranja sobre claro (2.4:1). */
.aviso--traba > .ms {
  background: var(--app-warning);
  color: var(--app-on-warning);
  border-radius: 2rem;
  padding: 0.25rem;
  align-self: flex-start;
}

.aviso--dato { border-color: var(--app-primary); background: var(--app-primary-container); color: var(--app-primary-dark); }
.aviso--dato .ms { color: var(--app-primary); }
[data-theme='dark'] .aviso--dato {
  background: color-mix(in oklch, var(--app-primary) 24%, transparent);
  color: var(--app-primary-light);
}
[data-theme='dark'] .aviso--dato .ms { color: var(--app-primary-light); }

.aviso--hueco { border-style: dashed; border-color: var(--app-neutral-variant); }

/* --- 16 · Paso a paso (import) ------------------------------------------- */

.pasos { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; padding: 1rem 1.15rem; border-bottom: 0.0625rem solid var(--theme-border); }

.paso { display: flex; align-items: center; gap: 0.5rem; font-family: var(--primary-font); font-size: 1em; color: var(--theme-text-secondary); }

.paso__numero {
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 2rem;
  display: grid;
  place-items: center;
  border: 0.0625rem solid var(--app-neutral-variant);
  font-family: var(--monospace-font);
  font-size: 0.875em;
  flex: none;
}

.paso[data-estado='actual'] { color: var(--app-primary); font-weight: var(--font-semibold); }
.paso[data-estado='actual'] .paso__numero { background: var(--app-primary); border-color: var(--app-primary); color: var(--app-on-primary); }
.paso[data-estado='hecho'] .paso__numero { background: var(--app-primary-container); border-color: var(--app-primary); color: var(--app-primary-dark); }
.paso[data-estado='omitido'] { opacity: 0.65; }
.paso[data-estado='omitido'] .paso__numero { border-style: dashed; }

[data-theme='dark'] .paso[data-estado='actual'] { color: var(--app-primary-light); }

/* --- 17 · Embudo / pipeline ---------------------------------------------- */

.embudo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17.5rem), 1fr));
  gap: 0.85rem;
  align-items: start;
}

.columna-etapa {
  background: var(--theme-bg-surface);
  border: 0.0625rem solid var(--theme-border);
  border-radius: 0.5rem;
  display: flex;
  flex-direction: column;
  min-height: 8rem;
}

.columna-etapa__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.7rem 0.85rem;
  border-bottom: 0.0625rem solid var(--theme-border);
  border-top: 0.19rem solid var(--app-tertiary);
  border-radius: 0.5rem 0.5rem 0 0;
}

.columna-etapa[data-cierre='ganada'] .columna-etapa__cabecera { border-top-color: var(--app-success); }
.columna-etapa[data-cierre='perdida'] .columna-etapa__cabecera { border-top-color: var(--app-neutral); }

.columna-etapa__titulo { font-family: var(--primary-font); font-weight: var(--font-semibold); font-size: 0.9375em; letter-spacing: 0.02em; }
.columna-etapa__cuerpo { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.6rem; }

.ficha-oportunidad {
  display: block;
  background: var(--theme-bg-elevated);
  border: 0.0625rem solid var(--theme-border);
  border-radius: 0.5rem;
  padding: 0.7rem 0.8rem;
  transition: border-color 0.2s ease-in-out, transform 0.2s ease-in-out;
}
.ficha-oportunidad:hover { border-color: var(--app-primary); transform: translateY(-0.0625rem); }
.ficha-oportunidad__lote { font-family: var(--monospace-font); font-weight: var(--font-semibold); font-size: 0.9375em; }
.ficha-oportunidad__contacto { display: block; font-size: 0.875em; color: var(--theme-text-secondary); line-height: 1.5; }
.ficha-oportunidad__monto { font-family: var(--monospace-font); font-size: 0.9375em; margin-top: 0.25rem; }

/* --- 18 · Matriz del cruce ----------------------------------------------- */
/* El tablero no muestra dos totales sueltos: muestra el cruce completo. La
   pregunta que el producto existe para contestar —cuántos disponibles están
   ocupados— es una celda, no una resta mental. */

.matriz { width: 100%; border-collapse: separate; border-spacing: 0.25rem; font-size: 1em; }
.matriz th {
  font-family: var(--terciary-font);
  font-weight: var(--font-semibold);
  font-size: 0.8125em;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--theme-text-secondary);
  text-align: left;
  padding: 0.3rem 0.4rem;
  line-height: 1.5;
}
.matriz thead th { text-align: center; }

.matriz__celda {
  border: 0.0625rem solid var(--theme-border);
  border-radius: 0.4rem;
  background: var(--theme-bg-surface);
  text-align: center;
  padding: 0.5rem 0.3rem;
  font-family: var(--monospace-font);
  font-variant-numeric: tabular-nums;
  font-weight: var(--font-semibold);
  font-size: 1.05em;
}

/* Un cero sigue siendo información: va en el gris secundario (5.7:1), no en el
   de deshabilitado (2.5:1). */
.matriz__celda[data-vacia='true'] { color: var(--theme-text-secondary); font-weight: var(--font-regular); }

/* La celda que hace perder plata se PINTA. Naranja de fondo, tinta negra. */
.matriz__celda[data-peligro='true'] {
  border: 0.13rem solid var(--app-warning);
  background: var(--app-warning);
  color: var(--app-on-warning);
}

.matriz__nota { display: block; font-family: var(--secondary-font); font-size: 0.8125em; font-weight: var(--font-regular); letter-spacing: 0.04em; text-transform: uppercase; }

/* --- 19 · Línea de tiempo ------------------------------------------------- */

.linea-tiempo { display: flex; flex-direction: column; gap: 0; }

.hito { display: grid; grid-template-columns: 2rem 1fr; gap: 0.75rem; padding-bottom: 1.1rem; position: relative; }
.hito:not(:last-child)::before {
  content: '';
  position: absolute;
  left: 0.97rem;
  top: 2rem;
  bottom: 0;
  width: 0.0625rem;
  background: var(--theme-border);
}
.hito__marca {
  width: 2rem;
  height: 2rem;
  border-radius: 2rem;
  display: grid;
  place-items: center;
  background: var(--app-primary-container);
  color: var(--app-primary-dark);
  z-index: 1;
}
[data-theme='dark'] .hito__marca {
  background: color-mix(in oklch, var(--app-primary) 30%, transparent);
  color: var(--app-primary-light);
}
.hito__titulo { font-family: var(--primary-font); font-weight: var(--font-semibold); font-size: 1em; line-height: 1.5; }
.hito__meta { font-size: 0.875em; color: var(--theme-text-secondary); font-family: var(--monospace-font); }
.hito__detalle { font-size: 0.9375em; line-height: 1.6; color: var(--theme-text-secondary); margin-top: 0.15rem; }

/* --- 20 · Pestañas -------------------------------------------------------- */

.pestanas { display: flex; gap: 0.25rem; border-bottom: 0.0625rem solid var(--theme-border); overflow-x: auto; }

.pestana {
  min-height: 2.75rem;
  padding: 0.4rem 0.9rem;
  border: 0;
  border-bottom: 0.19rem solid transparent;
  background: transparent;
  color: var(--theme-text-secondary);
  font-family: var(--primary-font);
  font-weight: var(--font-medium);
  font-size: 1em;
  white-space: nowrap;
  transition: color 0.2s ease-in-out, border-color 0.2s ease-in-out;
}
.pestana:hover { color: var(--app-primary); }
.pestana[aria-selected='true'] { color: var(--app-primary); border-bottom-color: var(--app-primary); }

[data-theme='dark'] .pestana:hover,
[data-theme='dark'] .pestana[aria-selected='true'] { color: var(--app-primary-light); }
[data-theme='dark'] .pestana[aria-selected='true'] { border-bottom-color: var(--app-primary-light); }

/* --- 21 · Diálogo y avisos flotantes ------------------------------------- */

.dialogo {
  width: min(100% - 2rem, 34rem);
  border: 0;
  border-radius: 1rem;
  padding: 0;
  background: var(--theme-bg-elevated);
  color: var(--theme-text);
  font-size: 1em;
}
.dialogo::backdrop { background: rgb(0 0 0 / 0.55); }
.dialogo__cabecera { padding: 1.25rem 1.25rem 0.5rem; }
.dialogo__cuerpo { padding: 0.5rem 1.25rem 1rem; font-size: 1em; line-height: 1.7; }
.dialogo__pie { display: flex; justify-content: flex-end; gap: 0.5rem; flex-wrap: wrap; padding: 0.85rem 1.25rem 1.25rem; }

.bandeja-avisos {
  position: fixed;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: min(100% - 2rem, 30rem);
  pointer-events: none;
}

.aviso-flotante {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.8rem 1rem;
  border-radius: 0.5rem;
  background: var(--app-neutral-dark);
  color: var(--app-neutral-surface);
  font-size: 1em;
  line-height: 1.5;
  box-shadow: 0 0.3rem 1rem rgb(0 0 0 / 0.3);
  animation: aviso-entra 0.2s ease-in-out;
}
[data-theme='dark'] .aviso-flotante { background: var(--theme-bg-surface); color: var(--theme-text); border: 0.0625rem solid var(--theme-border); }

@keyframes aviso-entra {
  from { opacity: 0; transform: translateY(0.5rem); }
  to { opacity: 1; transform: none; }
}

/* --- 22 · Acceso (login) -------------------------------------------------- */

.acceso {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: 1fr;
  background: var(--theme-bg);
}

.acceso__relato {
  display: none;
  flex-direction: column;
  justify-content: space-between;
  gap: 2rem;
  padding: 3rem;
  background: var(--app-primary);
  color: var(--app-on-primary);
}

.acceso__panel { display: grid; place-items: center; padding: 2rem 1.25rem; }
.acceso__caja { width: min(100%, 24rem); font-size: 1em; }

@media (min-width: 60rem) {
  .acceso { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .acceso__relato { display: flex; }
}

/* --- 23 · Utilidades ------------------------------------------------------ */

.oculto { display: none !important; }

/* Un teléfono, un DNI o una identidad de lote partidos en dos renglones dejan
   de leerse como un número: o entran enteros, o el contenedor scrollea. */
.no-corta { white-space: nowrap; }

.solo-lectores {
  position: absolute;
  width: 0.0625rem;
  height: 0.0625rem;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.grafico { width: 100%; height: 17rem; }

.separador { border: 0; border-top: 0.0625rem solid var(--theme-border); margin: 1rem 0; }

/* --- 24 · Interruptor de avisos del prototipo ---------------------------
   Con los avisos apagados desaparecen los carteles que hablan DEL PROTOTIPO:
   los huecos que el contrato todavía no cubre y la nota de credenciales del
   acceso. No tocan al producto — son andamiaje, y el andamiaje se saca para
   mostrarle la obra a alguien. Se apaga desde Configuración, no desde un panel
   flotante de diseñador. */

[data-avisos='off'] .aviso--hueco,
[data-avisos='off'] [data-aviso-prototipo] { display: none !important; }

/* --- 24 · Licencia --------------------------------------------------------
   Tres estados, tres tratamientos. La barra sólo existe en dos de ellos:
   una licencia al día no es una notificación, y avisar de lo que está bien es
   la forma más rápida de que dejen de leerse los avisos.

   Gracia usa naranja de superficie con tinta negra (7.8:1). Vencida usa la
   superficie neutra oscura: no es un error del usuario ni un fallo del sistema
   —es una cuenta por pagar— y pintarla de rojo sería acusar a quien trabaja.
   -------------------------------------------------------------------------- */

.barra-licencia {
  position: fixed;
  top: var(--header-height);
  right: 0;
  left: 0;
  z-index: 28;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  min-height: 3.5rem;
  padding: 0.6rem 1rem;
  border-bottom: 0.0625rem solid var(--theme-border);
  line-height: 1.5;
}

.barra-licencia__texto { flex: 1 1 min(100%, 24rem); }

.barra-licencia[data-estado='gracia'] {
  background: var(--app-warning);
  color: var(--app-on-warning);
  border-bottom-color: var(--app-warning);
}
.barra-licencia[data-estado='gracia'] .boton--contorno {
  border-color: var(--app-on-warning);
  color: var(--app-on-warning);
}

.barra-licencia[data-estado='vencida'] {
  background: var(--app-neutral-dark);
  color: var(--app-neutral-surface);
  border-bottom-color: var(--app-neutral-dark);
}
[data-theme='dark'] .barra-licencia[data-estado='vencida'] {
  background: var(--theme-bg-surface);
  color: var(--theme-text);
}

body[data-barra-licencia='si'] .principal { margin-top: calc(var(--header-height) + 3.5rem); }
body[data-barra-licencia='si'] .lateral { top: calc(var(--header-height) + 3.5rem); }
body[data-barra-licencia='si'] .velo { top: calc(var(--header-height) + 3.5rem); }

@media (min-width: 60rem) {
  .barra-licencia { left: 18rem; }
  body[data-barra-licencia='si'] .lateral { top: var(--header-height); }
}

/* Un control bloqueado por licencia no desaparece: se ve, se entiende por qué
   no se puede y no se puede. Esconderlo dejaría a la persona buscándolo. */
[data-licencia='vencida'] [aria-disabled='true'],
.bloqueado-por-licencia {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

/* Estado de la licencia, en su pantalla */
.estado-licencia {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.3rem 0.9rem;
  border-radius: 2rem;
  font-family: var(--primary-font);
  font-weight: var(--font-semibold);
  min-height: 2.25rem;
}
.estado-licencia[data-estado='VIGENTE'] { background: var(--result-success-bg); color: var(--result-success-text); border: 0.0625rem solid var(--app-success); }
.estado-licencia[data-estado='GRACIA'] { background: var(--app-warning); color: var(--app-on-warning); }
.estado-licencia[data-estado='VENCIDA'] { background: var(--app-neutral-dark); color: var(--app-neutral-surface); }
[data-theme='dark'] .estado-licencia[data-estado='VENCIDA'] { background: var(--app-neutral-light); color: var(--app-neutral-dark); }

/* Asientos: la barra de ocupación. El techo se ve siempre, no cuando ya chocaste. */
.asientos { display: flex; flex-direction: column; gap: 0.5rem; }
.asientos__cifra {
  font-family: var(--monospace-font);
  font-variant-numeric: tabular-nums;
  font-weight: var(--font-semibold);
  font-size: 2em;
  line-height: 1.1;
}
.asientos__barra {
  display: flex;
  gap: 0.25rem;
}
.asientos__silla {
  flex: 1 1 0;
  height: 0.6rem;
  border-radius: 2rem;
  background: var(--app-neutral-light);
}
.asientos__silla[data-ocupada='true'] { background: var(--app-primary); }
.asientos[data-lleno='true'] .asientos__silla[data-ocupada='true'] { background: var(--app-warning); }

/* La clave se pega, no se tipea: campo ancho, monoespaciado y con aire. */
.campo-clave input {
  font-family: var(--monospace-font);
  font-size: 1.15em;
  letter-spacing: 0.12em;
  text-align: center;
  min-height: 3.5rem;
}

.paso-activacion {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--primary-font);
  color: var(--theme-text-secondary);
}
.paso-activacion[data-estado='actual'] { color: var(--app-primary); font-weight: var(--font-semibold); }
[data-theme='dark'] .paso-activacion[data-estado='actual'] { color: var(--app-primary-light); }
.paso-activacion[data-estado='hecho'] { color: var(--app-success); }

/* Módulo incluido / no incluido. El que no está no se esconde: es lo que se
   puede vender, y esconderlo lo vuelve invendible. */
.modulo {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.75rem 0;
  border-top: 0.0625rem solid var(--theme-divider);
}
.modulo:first-child { border-top: 0; }
.modulo[data-incluido='false'] { opacity: 0.75; }
.modulo[data-incluido='false'] .modulo__nombre { text-decoration: none; }
.modulo__nombre { font-family: var(--primary-font); font-weight: var(--font-semibold); line-height: 1.5; }
.modulo__detalle { font-size: 0.875em; line-height: 1.6; color: var(--theme-text-secondary); }

/* --- 25 · Movimiento reducido -------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
