/* =========================================================================
   GlassFlow · app de oficina — implementación de CANTO (DIS-002 rev. B).

   Esto NO es la hoja de la app de campo. La de obra es superficie oscura,
   toque de 56px y una cosa por pantalla, porque se mira con guantes y a pleno
   sol. Esta es la densidad de un ERP: fila de 32px, cifras tabulares, cabecera
   fija y recuento a la vista. Son los mismos tokens y la misma marca; lo que
   cambia es la densidad y el contraste, que es exactamente lo que dice el
   capítulo 05 de DIS-002.

   LAS CINCO REGLAS, y dónde se cumplen aquí:

     1. El color NO se pone en el lateral. La comprobación de revisión que pide
        DIS-002 es buscar `border-left` en esta hoja: **ninguno lleva color**.
        Los dos que hay —y los pocos `border-top` y `border-right`— son
        separadores de `1px solid var(--hairline)`, el mismo filete que en los
        otros tres lados, nunca una franja de color.

        Aquí ponía hasta el 2-9-2026 que `border-left` «no aparece ni una vez», y
        no era verdad desde hacía tiempo: hay dos, los dos de filete. Se corrige
        porque una comprobación que se sabe falsa deja de hacerse, y entonces el
        día que entre un `border-left` de color no lo caza nadie.
     2. El material es el acento: botón primario, filtro activo, fila
        seleccionada, mes en curso, foco. Nada más lleva verde.
     3. Un solo acento. No hay azul en ninguna parte de este fichero.
     4. El color nunca es la única señal: todo estado lleva glifo SVG y texto.
     5. Densidad primero: `--fila: 32px`.

   Prohibido y ausente: sombras, degradados, zebra, webfonts, segundo acento,
   acciones que solo aparecen en `:hover`.

   ============ EL REDISEÑO DEL 9-9-2026 (pedido por Fernando) ============

   Tres de las cinco reglas de arriba se rompen aquí a propósito, y hay que
   decirlo así de claro porque el resto de esta hoja las sigue citando como si
   siguieran enteras:

     · Regla 1 («el color no va en el lateral»): el lateral pasa a fondo
       oscuro. Sigue sin llevar `border-left` de color ni franja — lo que
       cambia es el fondo entero, no un adorno lateral.
     · «Prohibido: webfonts»: Space Grotesk (titulares y cifras) y Public Sans
       (cuerpo) sustituyen a la pila de sistema. Se autoalojan (`fuentes/`) y
       no se enlazan a Google Fonts en cada carga — un ERP interno no necesita
       una petición nueva a un tercero por cada pantalla.
     · Regla 3 («un solo acento»): sigue habiendo uno, y sigue siendo
       `--canto`. No se ha tocado #0e6e62 ni se ha añadido un segundo acento
       — «acento teal» ya era la marca (ver `canto.css`).

   Lo que NO se toca: `docs/diseno/mockups/estilo-canto.html`, el documento
   normativo de DIS-002. Sigue describiendo el sistema de antes de hoy, y
   decidir si se actualiza —o si esto se revierte— es de Raúl. Tampoco se toca
   `canto.css`: sus tokens los cargan también instalador, medición y
   plataforma, y ninguna de las tres pasa a lateral oscuro ni a Space Grotesk
   con este cambio. Todo lo de aquí abajo vive solo en esta hoja. */

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  /* Un solo fichero para todo el rango: es la variable de Google, no tres
     estáticas — el navegador interpola el peso que le pidas dentro de 500-700. */
  font-weight: 500 700;
  font-display: swap;
  src: url('fuentes/space-grotesk.woff2') format('woff2');
}
@font-face {
  font-family: 'Public Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fuentes/public-sans.woff2') format('woff2');
}

:root {
  /* Los tokens de Canto están en `/canto.css`, que esta página carga antes que
     esta hoja. Aquí abajo solo lo que es de la oficina y de nadie más. */

  /* El rojo pulsado. Existe por `.btn.peligro`, que es el único sitio donde el
     rojo es acción y no estado — el porqué está escrito donde se usa. */
  --rojo-h: #a3251c;

  /* Relleno de gráfico. Extensión de Canto: DIS-002 no define fondos de barra,
     y un neutro no es un segundo acento. #8A8D93 da 3.3:1 sobre blanco y 3.1:1
     sobre panel, que es el mínimo de objeto gráfico. El acento se reserva para
     el mes en curso: es la forma «énfasis», una serie en acento y el resto en
     gris de contexto. */
  --barra: #8a8d93;
  /* El gris pulsado. Es al `--barra` lo que `--canto-h` al acento: la misma
     tinta un escalón más oscura, para que pasar el ratón por una barra la
     señale sin cambiar lo que su color dice. */
  --barra-h: #6e7176;

  /* Las dos familias del rediseño, con la pila de sistema de Canto como
     reserva: si el `.woff2` no llega a tiempo o falla, se lee en la fuente de
     siempre y no en la de otra aplicación cualquiera del sistema operativo. */
  --sf-cuerpo: 'Public Sans', var(--sf);
  --sf-titular: 'Space Grotesk', var(--sf);
}

* {
  box-sizing: border-box;
}
html {
  -webkit-text-size-adjust: 100%;
  scrollbar-gutter: stable;
}
body {
  margin: 0;
  background: var(--panel);
  color: var(--tinta);
  font-family: var(--sf-cuerpo);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
/* FE-01: se respeta la preferencia del sistema. */
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}
/* `hidden` tiene que esconder de verdad.
   El atributo solo vale un `display: none` de la hoja del navegador, así que
   CUALQUIER `display` de aquí le gana: un `.btn` con `hidden` puesto se queda a
   la vista, porque `.btn` declara `display: inline-flex`. Se vio en un botón que
   solo tenía que salir tras editar algo y salía siempre. El `!important` es el
   remedio que recomienda la propia especificación para este caso. */
[hidden] {
  display: none !important;
}
/* FE-01: el foco NUNCA se quita sin alternativa. Va en el acento. */
:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 2px;
}
button {
  font: inherit;
  color: inherit;
  border: 0;
  background: none;
  cursor: pointer;
}

/* ==================== EL OTRO ZOOM QUE NADIE PIDE ====================

   El de la letra pequeña se cura con 16px en la media query del final. Este es el
   segundo: en un móvil, **dos toques seguidos rápidos amplían la página**. Y esta
   aplicación es una tabla densa de filas de 32px que se pulsan una detrás de
   otra, así que pasa constantemente — se abre una obra, se cierra, se abre la de
   al lado, y a la tercera la pantalla se ha ido al 200 % sin que nadie lo haya
   pedido. De paso, el navegador deja de esperar 300ms a ver si llega un segundo
   toque, así que la fila responde en cuanto se levanta el dedo.

   `manipulation` quita el doble toque **y nada más**: el pellizco de dos dedos
   sigue funcionando en toda la página, que es lo que FE-01 exige y lo que
   `user-scalable=no` rompería (crítico). Va solo en lo que se pulsa, no en el
   `body`: sobre el texto de un informe el doble toque para acercar sigue siendo
   útil, y ahí no hay nada que se dispare por accidente. */
button,
a[href],
summary,
label,
tr.pulsa {
  touch-action: manipulation;
}
svg {
  display: block;
}
.oculta {
  display: none !important;
}
.mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
/* Toda cifra es tabular. No es estética: las columnas de importes tienen que
   cuadrar en vertical (DIS-002, prohibido nº 9). */
.num {
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.ico {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
}
/* Para lectores de pantalla y nada más. */
.solo-lector {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ======================================================= ENTRAR / EMPRESA */

/* `100dvh` con `100vh` de respaldo, y en un móvil se nota: `100vh` mide la
   ventana SIN la barra de direcciones, que sí está. La caja se centraba en un
   alto mayor que el visible, así que el formulario de entrar quedaba por debajo
   del pliegue y la página se desplazaba para no enseñar nada. Es el mismo par que
   ya usa `.lateral`. */
.portal {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
}
.portal-caja {
  width: 100%;
  max-width: 380px;
}
.marca {
  font-family: var(--sf-titular);
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.022em;
  margin: 0 0 4px;
}
.marca em {
  font-style: normal;
  color: var(--canto);
}
.portal .bajada {
  color: var(--subtinta);
  margin: 0 0 22px;
  font-size: 13.5px;
}
.campo {
  display: block;
  margin-bottom: 14px;
}
/* FE-01: la etiqueta es un `<label for>` de verdad. El placeholder no es una
   etiqueta: desaparece al escribir y con él desaparece qué se pedía. */
.campo > .k {
  display: block;
  font-size: 12px;
  color: var(--subtinta);
  margin-bottom: 5px;
}
.campo input {
  width: 100%;
  height: var(--toque);
  padding: 0 12px;
  font: inherit;
  color: var(--tinta);
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
}
.campo input:focus-visible {
  border-color: var(--canto);
}
/* El área de texto de un `.campo`, y esto es un arreglo de los que no se ven
   hasta que se abre la pantalla en un móvil.

   No había regla. `.ficha textarea` existía y estaba bien —hairline por los
   cuatro lados, esquina de control, fondo blanco—, pero las dos áreas del
   dictado (`#dictar-texto` y `#dictar-md`) NO viven dentro de una ficha: viven
   en la pantalla de dictar, dentro de un `.campo` a secas. Así
   que se quedaban con el estilo del navegador: **monospace de 13,33px, borde gris
   #767676 y esquina recta.** Dos cosas mal a la vez —un tipo de letra que no es
   el del sistema y un borde que no es el hairline de Canto (DIS-002 regla 1)— y
   encima por debajo de 16px, que es lo que hacía que Safari de iPhone ampliara la
   pantalla al tocar el campo. El zoom se cura en la media query de abajo; la
   pinta, aquí.

   Se escribe con las mismas propiedades que `.ficha textarea` a propósito: es el
   mismo componente en otro sitio, y dos declaraciones distintas para la misma
   caja es como acaban pareciendo de dos aplicaciones. */
.campo textarea {
  width: 100%;
  min-height: 74px;
  padding: 9px 11px;
  font: inherit;
  color: var(--tinta);
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  resize: vertical;
}
.campo textarea:focus-visible {
  border-color: var(--canto);
}

/* ---------- QUÉ LE FALTA A ESTE CAMPO ----------

   El motivo va DEBAJO DEL CAMPO y no debajo del botón, que es el cambio de
   paradigma del 3-9-2026: antes se apagaba «Guardar» y se escribía al pie una
   frase con todo lo que faltaba, y quien la leía tenía que traducirla a cajas y
   buscarlas entre doce. Ahora el botón se pulsa siempre y contesta aquí.

   Tres señales y no una, que es la regla 4 de Canto: el rojo, el triángulo de
   aviso y el texto. Con el monitor en gris siguen dos, y para quien va a oídas
   está el `aria-describedby` que lo lee al entrar en el campo (FE-01).

   **Sin caja rosa**, al revés que `.porques li.mal`: aquí pueden encenderse
   cinco a la vez en la misma pantalla, y cinco recuadros de fondo convierten un
   formulario en un semáforo. La caja se reserva para el rechazo que llega de
   fuera —el 409 del servidor—, que es uno y viene solo.

   `--rojo` sobre `--blanco` da **5.5:1** y sobre `--panel` **5.05:1**, medido y
   no mirado: AA de sobra a 12,5px por los dos lados. */
.campo-mal {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 5px 0 0;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--rojo);
  font-weight: 500;
}
/* El glifo alineado con la primera línea del texto y no con su base: a 12,5px y
   con dos renglones, `baseline` lo dejaba flotando medio píxel alto. */
.campo-mal .ico {
  align-self: flex-start;
  margin-top: 2px;
}
/* El borde del control que falla. Es señal de refuerzo, no la señal: la que
   manda es la frase de debajo, y por eso esto no vive solo en ningún sitio.
   Se marca con `aria-invalid` y no con una clase porque el atributo tiene que
   estar puesto de todas formas —lo lee el lector de pantalla— y dos maneras de
   decir lo mismo acaban discrepando. */
.campo input[aria-invalid='true'],
.campo textarea[aria-invalid='true'],
.campo select[aria-invalid='true'],
.campo-linea input[aria-invalid='true'],
.campo-linea select[aria-invalid='true'],
.presu-lineas input[aria-invalid='true'],
.presu-lineas select[aria-invalid='true'] {
  border-color: var(--rojo);
}
/* Al enfocarlo manda el acento, como en cualquier otro campo: el foco dice
   «estás aquí» y ese sitio es único; lo que está mal sigue dicho debajo. */
.campo input[aria-invalid='true']:focus-visible,
.campo textarea[aria-invalid='true']:focus-visible,
.campo select[aria-invalid='true']:focus-visible,
.campo-linea input[aria-invalid='true']:focus-visible,
.campo-linea select[aria-invalid='true']:focus-visible,
.presu-lineas input[aria-invalid='true']:focus-visible,
.presu-lineas select[aria-invalid='true']:focus-visible {
  border-color: var(--canto);
}
/* Dentro de una celda de la tabla de líneas: la columna es estrecha, así que el
   motivo va a 11,5px como el segundo control de la celda y sin el hueco de 5px,
   que ahí es una fila entera de tabla. Se corta la palabra antes que ensanchar la
   columna y empujar la tabla a hacer scroll horizontal. */
.presu-lineas .campo-mal {
  margin-top: 4px;
  font-size: 11.5px;
  overflow-wrap: anywhere;
}

/* Un grupo que falla —el interruptor de dos botones, las casillas de «qué
   lleva»— no tiene borde que teñir, así que el motivo se separa un poco más para
   que se lea colgando del grupo y no del control siguiente. */
.seg[aria-invalid='true'] + .campo-mal,
fieldset[aria-invalid='true'] + .campo-mal {
  margin-top: 7px;
}

/* ============================================================== ARMAZÓN */

.app {
  display: grid;
  /* El ancho del carril, en una variable, porque ahora lo necesitan DOS reglas:
     esta retícula y el `width` del lateral, que está fuera del flujo. Si se
     escribieran los 216px dos veces, el día que alguien cambie uno el lienzo se
     solapa con la barra o queda una franja de fondo entre las dos. */
  --carril: 216px;
  grid-template-columns: var(--carril) minmax(0, 1fr);
  /* El mismo par que `.portal` y `.lateral`, por el mismo motivo. */
  min-height: 100vh;
  min-height: 100dvh;
}

/* Filete de 1px a la derecha, del color del hairline. No es una franja de
   color: es el mismo separador que en todo el sistema.

   ANCHO Y ALTO FIJOS, y esto es una corrección deliberada. Antes el lateral era
   una celda de la retícula sin altura propia: se estiraba hasta donde llegara
   el lienzo, así que en Panel general medía una pantalla y en Posventa —con la
   tabla larga— medía tres, con el pie y «cerrar sesión» caídos al final del
   documento. La misma barra cambiaba de tamaño según la pantalla en la que
   estuvieras. Ahora ocupa el alto de la ventana, se queda pegada arriba al
   hacer scroll y es idéntica en las tres pantallas. `100dvh` porque en el
   navegador de un móvil `100vh` cuenta la barra de direcciones que no está. */
.lateral {
  /* `fixed` y no `sticky`, y el cambio es de este commit.

     `sticky` solo se queda quieto en el eje en el que se le dice y DENTRO de su
     bloque contenedor. Bastaba con que algo del lienzo —una tabla ancha con
     texto largo de verdad— hiciera la página más ancha que la ventana para que
     al deslizar en horizontal la barra se fuera con ella: en ese eje seguía
     siendo contenido normal. Fuera del flujo no puede pasar, venga el
     desplazamiento por donde venga.

     El hueco lo sigue guardando la columna de 216px de la retícula, que ahora es
     un carril vacío: por eso el ancho sale de `--carril` y no de un número
     escrito aquí. */
  position: fixed;
  top: 0;
  left: 0;
  width: var(--carril);
  height: 100vh;
  height: 100dvh;
  /* Si la ventana es muy baja, manda el contenido antes que el recorte: la
     navegación se desplaza dentro del propio lateral en vez de perderse. */
  overflow-y: auto;
  background: var(--blanco);
  border-right: 1px solid var(--hairline);
  padding: 16px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.lateral .marca {
  padding: 0 8px;
  margin-bottom: 18px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* El símbolo de Occam, a la izquierda del nombre del producto y siempre a la
   vista: es la casa de la que sale la vertical, y las dos cosas se leen juntas.

   Va en `currentColor` a tinta y no en el acento del material: es la marca, no
   algo que se pueda pulsar, y en Canto el verde es solo de lo accionable
   (DIS-002 regla 2). Tamaño en `em` para que siga al tipo de la marca y no haya
   dos escalas que mantener. */
.simbolo-casa {
  display: block;
  width: 1.15em;
  height: 1.15em;
  flex: none;
  color: var(--tinta);
  transition:
    width 180ms ease,
    height 180ms ease;
}
/* El logotipo del cliente. Va DEBAJO de la marca del producto: el software es
   GlassFlow y la empresa es de quien lo usa, en ese orden.

   El azul del logotipo de Orenga no rompe la regla 3 de DIS-002 —un solo
   acento—: la regla habla del color de la interfaz, y una marca ajena es
   contenido, igual que lo sería una foto de obra. Ni esta hoja ni ningún
   componente toman ese azul, y ninguna acción se pinta con él.

   `max-height` acota los logotipos cuadrados; los apaisados, como el de Orenga,
   los acota antes `max-width` y quedan al ancho del carril. Con las dos cotas
   el navegador escala manteniendo la proporción, así que ningún cliente puede
   descolocar el lateral con el logotipo que traiga. */
.logo-cliente {
  display: block;
  width: auto;
  /* Alto fijo por los SVG sin tamaño propio (V024); `contain` no lo deforma. */
  height: 44px;
  max-width: 100%;
  object-fit: contain;
  object-position: left center;
  margin: -6px 8px 18px;
}
/* Aquí había una pastilla verde con la vertical de la empresa —«glassflow»—, al
   lado de un nombre de marca que ya dice «GlassFlow». Era la misma palabra dos
   veces, y en el acento del material, que Canto reserva para lo accionable
   (DIS-002 regla 2): un adorno con el color de los botones. Fuera. Qué vertical
   es se lee en el nombre del producto, que es donde tiene que estar.
   El dato sigue llegando en la membresía; el día que haga falta enseñarlo será
   donde signifique algo, no pegado a su propio nombre. */
.grupo {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--subtinta);
  padding: 16px 8px 6px;
}
/* `<button>` y no un div con onclick (FE-01). La navegación es teclado-completa
   porque son botones nativos. */
/* ==================== LOS GRUPOS DE LA NAVEGACIÓN ====================

   El rótulo es texto pequeño en tinta apagada y el separador es el filete de
   1px. Nada de franja de color al lado del grupo, ni fondo, ni pastilla: el
   color del material se reserva para lo accionable y para lo vivo, y un título
   de sección no es ninguna de las dos cosas (DIS-002, reglas 1 a 3).

   Los grupos se separan con `margin` y no con `border` cuando el lateral está
   desplegado: con el rótulo delante, la línea sobra y suma ruido. Plegado no hay
   rótulo, así que ahí sí entra el filete — es lo único que queda para decir que
   hay un corte. */
.nav-grupo {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.nav-grupo + .nav-grupo {
  margin-top: 16px;
}
.nav-sec {
  padding: 0 8px 5px;
  font-size: 10.5px;
  font-weight: 600;
  /* El espaciado es lo que hace legible una mayúscula sostenida a 10px. Sin él
     se lee como una palabra apretada y no como un rótulo. */
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--subtinta);
}

.nav-it {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 34px;
  padding: 0 8px;
  border-radius: var(--r-ctrl);
  font-size: 13.5px;
  color: var(--tinta);
  text-align: left;
}
.nav-it .ico {
  width: 15px;
  height: 15px;
  color: var(--subtinta);
}
.nav-it:hover {
  background: var(--panel);
}
/* La sección activa: acento y peso. No una barra de color al lado. */
.nav-it.on {
  background: var(--canto-s);
  color: var(--canto-h);
  font-weight: 600;
}
.nav-it.on .ico {
  color: var(--canto);
}
.nav-it .cuenta {
  margin-left: auto;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  color: var(--subtinta);
}
/* El recuento de avisos graves. Rojo porque es estado, no porque sea un
   botón — y va acompañado del texto «graves» en el `title` y del glifo de la
   propia fila (regla 4). */
.nav-it .cuenta.grave {
  color: var(--rojo);
  font-weight: 600;
}
.lateral .pie {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}
.lateral .quien {
  padding: 0 8px 10px;
}
.lateral .quien .n {
  font-size: 13px;
  font-weight: 600;
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lateral .quien .e {
  font-size: 11.5px;
  color: var(--subtinta);
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ============ EL LATERAL OSCURO (rediseño del 9-9-2026) ============

   Todo lo de arriba se queda igual —la estructura, `fixed`, el ancho por
   `--carril`, el filete de 1px— y esto solo REDEFINE los tokens de color
   dentro de `.lateral`. No se toca ni una regla de las de arriba: `.nav-it`,
   `.plegar`, `.grupo`, `.simbolo-casa`, `.quien .n/.e` y hasta el filete entre
   grupos de `.app.plegado` leen `var(--tinta)`, `var(--subtinta)` y
   `var(--hairline)`, así que las tres heredan el valor oscuro sin que haga
   falta reescribir ninguna. `color: var(--tinta)` va explícito aquí porque
   `.marca .nombre` y `.quien .n` no declaran color propio: lo heredaban del
   `<body>` claro, y sin esta línea seguirían leyéndolo de ahí en vez de del
   lateral.

   `--canto`, `--canto-s` y `--canto-h` NO se tocan: la fila activa sigue
   siendo la pastilla clara de siempre (`.nav-it.on`), que sobre fondo oscuro
   se lee como lo que es, la marca puesta encima del resto.

   El hueco de hover no sale de un token —`--panel` es del lienzo claro y
   redefinirlo aquí se llevaría por delante cualquier otro uso que tenga
   dentro del lateral el día que aparezca uno— así que es un valor propio,
   `--lat-hover`, sin más alcance que estas dos reglas. */
.lateral {
  --tinta: #e9ecec;
  --subtinta: #8d9598;
  --hairline: #242c2e;
  --lat-hover: #1c2426;
  background: #12181a;
  color: var(--tinta);
}
.lateral .nav-it:hover,
.lateral .plegar:hover {
  background: var(--lat-hover);
}
/* Sobre el fondo oscuro del `hover`, el verde oscuro de la selección perdía
   contraste. En escritorio la sección activa conserva el verde claro de su
   pastilla también al pasar el ratón, tanto en el rótulo como en el glifo. */
@media (min-width: 861px) {
  .lateral .nav-it.on:hover,
  .lateral .nav-it.on:hover .ico {
    color: var(--canto-s);
  }
}

/* La SEGUNDA columna, dicha a mano, y no es opcional: desde que el lateral es
   `fixed` ya no ocupa la primera, así que la colocación automática metía el
   lienzo en el carril de 216px —la pantalla entera doblada en una columna
   estrecha y medio tapada por la barra— y dejaba la columna ancha vacía.
   En móvil no estorba: allí `.app` es `display: block` y esto no significa nada. */
.lienzo {
  grid-column: 2;
  min-width: 0;
  padding: 20px 26px 60px;
}
/* Volver con un botón de volver: la pantalla entra desde la izquierda (la pone y
   la quita `deslizarSiSeVuelve`, en `rutas.js`). Mientras entra, el lienzo recorta
   en horizontal para que no pase por encima del lateral; `clip` y no `hidden`,
   que no convierte el lienzo en contenedor de desplazamiento y deja en paz los
   `sticky` de dentro. Solo durante la animación: una tabla ancha puede ensanchar
   la página a propósito. Con `prefers-reduced-motion` no se pone. */
.entra-atras {
  animation: pagina-atras 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.lienzo:has(> .entra-atras) {
  overflow-x: clip;
}
@keyframes pagina-atras {
  from {
    transform: translateX(-100%);
  }
}
/* Tres columnas, y la de en medio es la de los actos.

   Rejilla y no `space-between` (12-9-2026, decisión de Raúl): repartiendo el
   hueco sobrante, el grupo de botones acababa donde lo dejara el título, y el
   título mide distinto en cada pantalla — «Calendario», «Bandeja de avisos»,
   «Incidencias»—, así que el mismo botón bailaba más de cien píxeles al cambiar
   de sección. Con `1fr auto 1fr` los dos lados miden igual por definición y el
   grupo cae en el eje de la pantalla, caiga lo que caiga a los lados.

   Lo de dentro lo reparte el `gap` de `.centro`: con un botón, queda en el eje;
   con dos, uno a cada lado a la misma distancia; con tres, el de en medio en el
   eje. No hace falta nada más — centrar un grupo es centrar sus botones. */
.cabecera {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 20px;
}
/* El título es lo único que puede ceder: los botones no se encogen (`.btn` con
   `flex: none` más abajo) y la campana mide lo que mide. */
.cabecera > :first-child {
  min-width: 0;
}
.cabecera > .der {
  justify-self: end;
}
.cabecera h1 {
  font-family: var(--sf-titular);
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -0.022em;
  margin: 0;
}
.cabecera .sub {
  font-size: 13px;
  color: var(--subtinta);
  margin: 2px 0 0;
}
.cabecera .der {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
}

/* ============ EL ACTO QUE ARRANCA CADA PANTALLA, EN LA CABECERA ============

   Desde el 9-9-2026 sube un peldaño más la convención de Raúl del 28-8-2026
   («un botón de dar de alta va centrado en la página y en la línea de la
   salida», antes en `.barra-pantalla`, ver su historia más abajo): en vez de
   una segunda fila propia de cada pantalla, separada del título por un
   filete, el acto vive en la MISMA fila que el título y la campana.

   `#cabecera-acciones` es un único contenedor para las siete pantallas que
   llevan acto —Panel, Proyectos, Clientes, la ficha de un cliente,
   Proveedores, Instaladores y Sentinel—, y dentro cada una tiene su propio
   `.acciones-de[data-vista]`, oculto salvo el de la pantalla activa
   (`sincronizarAccionesCabecera` en `rutas.js`). Son los mismos nodos de
   siempre —mismo id, mismo permiso puesto por `app.js`, misma pintada donde
   la había— reubicados; no hay un segundo botón «Nuevo cliente» en ninguna
   parte.

   Y el acto vuelve a centrarse EN LA PÁGINA desde el 12-9-2026, como hacía
   `.barra-pantalla` con su rejilla de tres columnas. Estuvo unos días centrado
   solo entre el título y la campana —que miden distinto en cada pantalla— y el
   resultado era un botón que cambiaba de sitio al cambiar de sección. Quien lo
   centra ahora es la rejilla de `.cabecera`, arriba; aquí dentro solo se reparten
   los botones del grupo, que es para lo que queda el `justify-content`. */
.cabecera .centro {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-width: 0;
}
.cabecera .centro > .acciones-de {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Y los grupos de dentro, con el mismo hueco.

   Instaladores es la única pantalla que reparte sus actos en dos cajas —las dos
   altas en `instaladores-acciones` y el interruptor en `instaladores-fin`, para
   que «Recuperar equipos» no salga revuelto con ellas— y esas cajas son `div`
   sueltos: el `gap` de la fila los separa a ellos, no a los botones que llevan
   dentro. «Nuevo equipo» y «Nuevo instalador» se tocaban por eso, sin un pixel
   entre los dos rectángulos verdes. Se arregla en el contenedor y no botón a
   botón: cualquier grupo que se añada mañana nace ya separado. */
.cabecera .centro > .acciones-de > div {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Igual que en `.barra-pantalla`: un botón no se encoge por mucho que apriete
   la fila. Lo que cede es el título, que ya lleva su propia elipsis por el
   `min-width: 0` de la celda. */
.cabecera .centro .btn {
  flex: none;
}
/* El margen inferior de `.btn.volver` es de cuando iba solo encima del
   contenido; en la cabecera, en línea con el título, sobra. */
.cabecera .centro .btn.volver {
  margin-bottom: 0;
}

/* ==================================================== LA CAMPANA DE POSVENTA

   El botón de la cabecera que abre lo que hay que atender. Cuadrado y del alto
   de `.filtro` para que las dos piezas de la derecha se alineen por la base sin
   trucos, con el filete de 1px por los CUATRO lados y sin sombra, como todo.

   `position: relative` no es opcional: es el ancla del recuento, que va absoluto
   pegado a la esquina. Sin él, el número se posicionaría contra el primer
   antepasado con posición —que aquí es el documento— y acabaría en un rincón de
   la pantalla.                                                              */

/* Las dos comparten TODO lo visual y no una copia cada una: son el mismo botón
   con dos trabajos —un recuento rojo y un panel colgando— y lo único que de
   verdad cambia es la medida, que va abajo. El día que cambie el filete o el
   verde de «abierto», cambia en las dos a la vez, que es el punto. */
.campana,
.sentinel {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 28px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--blanco);
  color: var(--subtinta);
  cursor: pointer;
}
.campana:hover,
.sentinel:hover {
  background: var(--panel);
}
/* Abierta, lleva el acento: es un control activo, y eso en Canto es verde
   (DIS-002 regla 2). El borde sigue siendo de 1px por los cuatro lados. */
.campana[aria-expanded='true'],
.sentinel[aria-expanded='true'] {
  border-color: var(--canto);
  background: var(--canto-s);
  color: var(--canto);
}
.campana .ico {
  width: 15px;
  height: 15px;
}

/* El recuento.

   Rojo porque habla de ESTADO —hay algo pendiente— y no de acción: es la misma
   licencia que el `.cuenta.grave` del lateral y la misma que las píldoras de
   gravedad (DIS-002 regla 3). Y no es la única señal: es un NÚMERO, o sea texto,
   con el glifo de la campana al lado, y el botón lleva la frase entera en su
   `aria-label` (regla 4, FE-01).

   Sólido y con letra blanca —los dos declarados juntos, que es la regla de esta
   hoja— porque a 10.5px sobre `--rojo-s` el rosa no se distingue del blanco de
   la cabecera a un metro de la pantalla. Blanco sobre `--rojo` da 5.5:1.

   `min-width` con `padding` lateral: un «3» sale redondo y un «128» se estira en
   vez de recortarse. Cifras tabulares para que no baile al pasar de 9 a 10. */
.campana-n,
.sentinel-n {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 9px;
  background: var(--rojo);
  color: #fff;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1;
}

/* =============================================== EL SENTINEL, EN LA CABECERA

   El robot que está siempre a la vista y cierra los ojos cuando no queda ningún
   pedido por cursar (decisión de Raúl, 30-9-2026; hasta entonces desaparecía).
   Del botón hacia dentro es la campana: mismo filete de 1px por los cuatro
   lados, mismo verde cuando está abierto, mismo recuento rojo en la esquina —
   todo eso se comparte arriba y no se copia.

   ---------- DÓNDE: AL LADO DE LA CAMPANA, Y NO FLOTANDO ----------

   Flotó durante un tiempo: `position: fixed`, centrado sobre el lienzo, para que
   se leyera con la página desplazada hasta la fila doscientos. El precio era que
   iba por encima de todas las pantallas y de lo que hubiera debajo, y ese precio
   no salía (decisión de Raúl). Ahora va donde la campana y como la campana: en
   la derecha de la cabecera, los dos del mismo tamaño y con el mismo hueco.

   El contenedor se queda —ya no flota, pero sigue siendo el ancla del bocadillo,
   que cuelga en absoluto para no empujar la cabecera al aparecer. */
.sentinel-flota {
  position: relative;
  display: inline-flex;
}

/* La caja mide lo que la campana —se hereda de la regla que comparten— porque
   ahora van uno al lado del otro: dos botones hermanos de tamaños distintos se
   leen como un descuido. El glifo va un punto por encima del de la campana: es
   una cara, y a 15px se emborrona. */
.sentinel .ico {
  width: 16px;
  height: 16px;
}

/* ---------------- EL BOCADILLO DE CADA HORA ----------------

   Pequeño, blanco, letras negras y poco texto (decisión de Raúl, 27 ago. 2026).
   Sale cada hora mientras queden pedidos sin cursar, se pulsa para ir a la cola
   y se cierra con su ×.

   **Sin acento y sin color de estado.** Es lo único de esta hoja que habla sin
   que nadie lo haya pulsado, así que tiene que pesar lo mínimo: en cuanto lleve
   verde promete algo que se puede hacer AQUÍ, y en cuanto lleve rojo dice que
   algo va mal. Ni una cosa ni la otra — es un recordatorio. El rojo ya está en
   el recuento del robot, que es de donde sale el bocadillo (Canto, regla 2).

   El pico es un cuadrado de 8px girado con dos filetes: el mismo `1px solid
   var(--hairline)` de la caja, no una sombra ni un degradado. Es lo que hace que
   la frase se lea como dicha POR EL ROBOT y no como un cartel que ha aparecido
   al lado.                                                                  */
.bocadillo {
  /* Absoluto y colgando del botón: en el flujo de la cabecera, aparecer cada
     hora empujaría la campana de sitio. `right: 0` lo alinea con el borde
     derecho del robot, que es donde apunta el pico. */
  position: absolute;
  top: calc(100% + 9px);
  right: 0;
  z-index: 40;
  display: flex;
  align-items: flex-start;
  gap: 6px;
  /* 260px, MEDIDO en el banco: a 230 la frase «Recuerda: 2 pedidos por
     confirmar» partía en dos justo antes de la última palabra, porque el hueco
     útil son 230 menos el relleno y menos los 26 de la ×. Es el tope de una
     línea y pico, no de un párrafo: que el texto sea corto es parte de la
     decisión, y este número es lo que lo sostiene. */
  max-width: 260px;
  padding: 7px 7px 7px 11px;
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
}
.bocadillo::before {
  content: '';
  position: absolute;
  top: -5px;
  right: 15px;
  width: 8px;
  height: 8px;
  background: var(--blanco);
  border-top: 1px solid var(--hairline);
  border-left: 1px solid var(--hairline);
  transform: rotate(45deg);
}
/* La frase. Es un `<button>` de verdad —lleva a la cola de pedidos— y no un
   `<div>` con un oyente: enfocable, activable con Enter y anunciado como botón
   sin una línea de ARIA (FE-01). Lo que se le quita es la ropa de botón, porque
   dentro de un bocadillo de 230px un botón con filete es una caja dentro de otra. */
.bocadillo-txt {
  flex: 1 1 auto;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 12.5px;
  line-height: 1.35;
  text-align: left;
  color: var(--tinta);
  cursor: pointer;
}
.bocadillo-txt:hover {
  text-decoration: underline;
}
/* La ×, arriba a la derecha del bocadillo (decisión de Raúl). Cuadrada y del
   tamaño del glifo: es la salida de un aviso de dos líneas, no una acción. */
.bocadillo-x {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: var(--r-ctrl);
  background: none;
  color: var(--subtinta);
  cursor: pointer;
}
.bocadillo-x:hover {
  background: var(--panel);
  color: var(--tinta);
}
.bocadillo-x .ico {
  width: 11px;
  height: 11px;
}

/* ---------------- LA NOTIFICACIÓN QUE ACABA DE LLEGAR ----------------

   Arriba y al centro, encima de la pantalla, hasta que su barra se llena y se
   mete en la campana (`aviso-flotante.js`). Blanca, con el filete de siempre por
   los cuatro lados y sin sombra: lo que la separa de lo de debajo es el filete.
   El color lo pone la píldora de gravedad, con su glifo y su palabra.

   El contenedor no recoge clics: ocupa el ancho de la ventana y taparía la
   cabecera aunque esté vacío. */
.flotante {
  position: fixed;
  top: 12px;
  left: 16px;
  right: 16px;
  z-index: 60;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.flotante-tarjeta {
  pointer-events: auto;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  width: min(420px, 100%);
  margin: 0;
  padding: 10px 14px 13px;
  overflow: hidden;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
  color: var(--tinta);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.flotante-tarjeta:hover {
  border-color: var(--subtinta);
}
.flotante-cab {
  display: flex;
  align-items: center;
  gap: 8px;
}
.flotante-tit {
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.35;
}
.flotante-tarjeta .sub {
  font-size: 12px;
  color: var(--subtinta);
}
/* La barra del tiempo, pegada al filete de abajo y de lado a lado. Gris y no
   verde: no se pulsa, cuenta. */
.flotante-barra {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: var(--subtinta);
  transform: scaleX(0);
  transform-origin: left center;
}

/* ============================================================ SUPERFICIES */

.sup {
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
}
.sup > h2 {
  font-size: 13px;
  font-weight: 600;
  margin: 0;
  padding: 12px 16px;
  border-bottom: 1px solid var(--hairline);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.sup > h2 .nota {
  font-weight: 400;
  font-size: 11.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
}
.sup > .cuerpo {
  padding: 14px 16px;
}

/* ============ LA TARJETA DE LA FASE EN CURSO ============

   De las siete tarjetas de una obra, la que cuenta lo que está pasando ahora
   mismo. Qué tarjeta es cada fase lo decide `TARJETA_DE_FASE` en `util.js`, y
   dos de las siete no son ninguna: se quedan como están.

   Tres señales, y ninguna es solo el color (regla 4 de Canto, FE-01): el filete
   pasa al acento **por los cuatro lados**, la cabecera se tiñe, y dentro va
   escrito «Aquí está la obra». En blanco y negro las dos últimas siguen ahí.

   Y lo que NO se hace: ni franja de color en el borde —ni izquierda ni superior,
   que es la regla 1 y el aviso de plantilla—, ni sombra, ni tamaño distinto. La
   tarjeta destacada es la misma tarjeta; lo único que cambia es de qué color es
   su filete. */
.sup.en-fase {
  border-color: var(--canto);
}
.sup.en-fase > h2 {
  background: var(--canto-s);
  border-bottom-color: var(--canto);
  /* Las esquinas de arriba, o el tinte asoma por fuera del redondeo de la
     tarjeta. Es 1px menos que `--r-sup` porque va por dentro del filete. */
  border-radius: calc(var(--r-sup) - 1px) calc(var(--r-sup) - 1px) 0 0;
}
.sup.en-fase > h2 .nota {
  color: var(--canto);
  font-weight: 600;
}
/* Las superficies de una rejilla se estiran a la altura de la más alta —eso es lo
   que las alinea— y lo que se estiraba era la CAJA, no el contenido: el gráfico de
   obra por mes se quedaba con sus 168px de barras y 150 de blanco debajo, al lado
   de una tarjeta de fases más alta. Con la superficie en columna y el cuerpo
   quedándose el hueco, el contenido llega hasta abajo y las barras usan el alto
   que hay. No cambia nada donde las dos tarjetas ya median lo mismo. */
.rejilla > .sup,
.panel-dos > .sup {
  display: flex;
  flex-direction: column;
}
/* El cuerpo se queda el hueco Y es a su vez una columna flex. Lo segundo hace
   falta: con el cuerpo como bloque normal, un `height: 100%` dentro no resuelve
   —la altura del cuerpo viene de `flex-grow`, que para un porcentaje cuenta como
   indefinida— y el gráfico se quedaba en su mínimo con el blanco debajo. Como
   columna flex, el hijo crece con `flex` y no depende de porcentajes. */
.rejilla > .sup > .cuerpo,
.panel-dos > .sup > .cuerpo {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* Y el gráfico crece con el cuerpo en vez de quedarse en su altura fija. Los
   168px pasan a ser el mínimo: es lo que necesita para que una barra corta siga
   siendo una barra y no una raya.
   La tabla equivalente que va detrás no estorba: `.solo-lector` está fuera del
   flujo. */
.cols {
  height: auto;
  min-height: 168px;
  flex: 1 1 auto;
}
.rejilla {
  display: grid;
  /* `min(320px, 100%)` y no 320px pelado: el suelo de la pista no puede ser más
     ancho que el sitio que hay. En una pantalla de 320px —el SE sigue por ahí—
     las tarjetas se quedaban en 320 dentro de un lienzo de 292 y se salían. */
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 16px;
  margin-bottom: 16px;
}
.ancho {
  grid-column: 1 / -1;
}
/* La ficha del cliente: las tarjetas de arriba se reparten el ancho entero. El
   suelo de cada pista es la parte que le toca —un tercio con tres— y nunca menos
   de 320px, así que caben como mucho las que hay y en estrecho bajan solas a una
   columna. */
.rejilla.arriba-2 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, max(320px, (100% - 16px) / 2)), 1fr));
}
.rejilla.arriba-3 {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, max(320px, (100% - 32px) / 3)), 1fr));
}

.contacto-cita {
  margin-top: 14px;
  padding: 12px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--panel);
}
.contacto-cita > p {
  margin: 0 0 8px;
}
.contacto-cita-acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

#v-pedidos .correos-desconocidos {
  margin-top: 32px;
  border-color: var(--tinta);
}
#v-pedidos .correos-desconocidos > h2 {
  font-size: 20px;
}

/* --- cifras --- */
/* Los dos actos que arrancan el recorrido —dar de alta a quien paga y abrirle
   la obra— vivieron aquí, encima de las cifras, del 21-8-2026 al 9-9-2026.
   Desde entonces están en la cabecera de la aplicación con el resto de la
   convención: ver `.cabecera .centro` (arriba del todo de esta hoja) y
   `accionesPanel` en `panel.js`. */

.cifras {
  display: grid;
  /* 240px y no 150: desde que la variación va al mismo cuerpo que la cifra, una
     tarjeta estrecha no la aguanta. «−199 726,89 €» a 24px pide ~200px de ancho
     útil, y en una columna de 150 se salía de la caja. Es el precio de que el
     número en rojo se lea como un número y no como un pie de foto. */
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
}

/* ============ EN LA PORTADA, TRES POR FILA ============

   Decisión de Raúl del 21 ago. 2026, y el `auto-fit` de arriba ya no servía: con
   nueve tarjetas repartía cinco en la primera fila y cuatro en la segunda, con un
   hueco al final que se lee como una tarjeta que falta.

   Tres columnas fijas dan tres filas de tres, y además agrupan solas: la obra y
   el dinero arriba, lo que se habla y las colas en medio, con quién se trabaja
   abajo. El orden lo pone `cifras()` en `app.js` — aquí solo se decide cuántas
   caben.

   ---------- POR QUÉ VA ACOTADO A `#v-panel` ----------

   `.cifras` es la misma rejilla en cuatro pantallas: aquí, y encima de las tablas
   de Proyectos, Márgenes y Clientes. Las dos primeras llevan tres tarjetas y les
   daría igual; **Clientes lleva UNA**, y con tres columnas fijas se quedaría a un
   tercio de ancho con dos huecos al lado. Es el mismo problema que `cifras()`
   describe para Márgenes cuando tenía una sola tarjeta, y allí se arregló
   añadiendo dos — aquí no hay dos que añadir, así que la regla se queda donde hace
   falta.

   `minmax(0, 1fr)` y no `1fr`: un pie largo sin `minmax(0, …)` no deja encogerse a
   la pista y las tres columnas se van de ancho. */
#v-panel .cifras {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* La cifra del panel es un `<button>` **en la portada**, donde lleva a la
   pantalla en la que vive lo que cuenta; y un `<div>` en esa misma pantalla,
   donde ya no hay a dónde ir. Mismo aspecto, distinta naturaleza.

   Botón nativo y no un div con onclick, así que se llega con el tabulador y se
   activa con Enter sin escribir una línea de JS (FE-01).

   `text-align:left` y `width:100%` porque el navegador centra el contenido de
   un botón, y una rejilla de KPIs centrados deja de tener columna por la que
   leer. Y `display:block` para que las cifras no cuadren en vertical por
   casualidad sino porque están en la misma rejilla. */
.cifra {
  /* Rejilla de dos columnas desde el 16-9-2026 (decisión de Raúl): el rótulo, la
     cifra y el pie en la izquierda, y la variación en la derecha a la altura de
     la cifra. Antes iba debajo del pie y le costaba a cada tarjeta una cuarta
     línea de alto —treinta y pico píxeles por nueve tarjetas en la portada—, que
     es justo lo que empujaba la agenda fuera de la primera pantalla. Al lado no
     pierde nada: sigue al mismo cuerpo que la cifra y sigue leyéndose con ella.

     `minmax(0, 1fr)` en la primera: sin el 0, un pie largo no deja encoger la
     columna y la píldora se sale por la derecha. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 10px;
  width: 100%;
  text-align: left;
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  padding: 13px 15px;
}
/* El hover va SOLO en la variante pulsable: un fondo que reacciona al ratón
   sobre algo que no hace nada es prometer un clic que no existe.

   Y nada aparece solo en el hover —la flecha, la cifra y el pie están
   siempre—; lo único que cambia es el fondo (prohibido nº 6 de DIS-002). */
button.cifra:hover {
  background: var(--panel);
}
.cifra .lab {
  grid-column: 1;
  font-size: 12px;
  color: var(--subtinta);
  margin: 0;
}
.cifra .val {
  grid-column: 1;
  font-family: var(--sf-titular);
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 4px 0 0;
  font-variant-numeric: tabular-nums;
}
.cifra .val em {
  font-style: normal;
  font-size: 14px;
  color: var(--subtinta);
}
.cifra .pie {
  grid-column: 1;
  font-size: 11.5px;
  color: var(--subtinta);
  margin: 3px 0 0;
}

/* --- la tendencia del KPI ---

   Tres señales para un solo dato, que es lo que pide la regla 4: la FLECHA dice
   hacia dónde se movió, el COLOR si eso es bueno o malo, y el TEXTO cuánto. Las
   tres se leen sin las otras dos. Bajo daltonismo rojo-verde el verde y el rojo
   son el mismo gris, y una tarjeta cuya única diferencia fuera ese gris no
   diría nada — por eso la flecha nunca se va.

   El verde y el rojo de aquí son `--verde` y `--rojo`, los semánticos, no el
   `--canto`: hablan de estado, no de acción. El acento del material se queda
   donde tiene que estar, que es el propio botón cuando recibe el foco.          */
.tend {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  /* Al MISMO tamaño que `.cifra .val`, no al de un pie de tarjeta. La
     variación es tan noticia como la cifra —«56,1 %» dice dónde estás y
     «−43,9 pts» dice que te estás cayendo—, y en 11,5px la segunda se leía
     como una nota al pie. Mismo cuerpo, mismo peso y mismo `letter-spacing`
     que la cifra para que las dos hagan una sola línea de lectura. */
  font-family: var(--sf-titular);
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  padding: 1px 9px;
  border-radius: var(--r-pill);
  background: var(--panel);
  color: var(--subtinta);
  white-space: nowrap;
  margin-top: 6px;
}
/* La variación de una tarjeta va en la segunda columna, a la altura de la cifra
   y pegada al borde derecho. El `margin-top` de arriba es el que la separaba del
   pie cuando iba debajo; aquí sobra. La regla es `.cifra > .tend` para que no
   alcance a las píldoras que viven dentro de una ficha o de una tabla. */
.cifra > .tend {
  grid-column: 2;
  grid-row: 2;
  justify-self: end;
  align-self: center;
  margin-top: 0;
}
/* En `em`: el glifo crece con el número en vez de quedarse de adorno. */
.tend .ico {
  width: 0.62em;
  height: 0.62em;
}
.tend.bien {
  background: var(--verde-s);
  color: var(--verde);
}
.tend.mal {
  background: var(--rojo-s);
  color: var(--rojo);
}
/* Sin comparación posible. No es un estado malo, es que no se sabe: gris, glifo
   neutro y el motivo escrito en el `title` (FE-02 — el estado vacío también se
   diseña).

   Y en cuerpo pequeño, al contrario que las variaciones: lo que se pidió grande
   son los NÚMEROS en rojo y verde. «Sin comparación» es una frase, y a 24px
   gritaba justo la tarjeta que no tiene nada que contar. */
.tend.sinDato {
  background: transparent;
  padding-left: 0;
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0;
}
.tend.sinDato .ico {
  width: 11px;
  height: 11px;
}
/* La misma píldora dentro de una fila rótulo/valor de una ficha.

   Los 24px de arriba están medidos contra `.cifra .val`, que es la cifra grande
   de una tarjeta: allí la variación es tan noticia como el número. En una fila
   de `.datos` la noticia es el valor de la fila, que va a 12,5px, y la píldora
   entraba al doble de cuerpo y con su `margin-top` de titular — en la ficha de
   un proveedor, «Sin cambio» se salía de su carril y empujaba el filete de la
   fila siguiente. Mismo componente y mismo color, al cuerpo del sitio donde
   cae. */
.ficha .dato .tend {
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0;
  margin-top: 0;
}

/* ---------- LA AGENDA DE HOY Y LO VENDIDO, EN UNA FILA ----------

   Decisión de Raúl del 8 sep. 2026: tres cuartos para lo de hoy y un cuarto para
   el gráfico. Es la proporción escrita — `3fr 1fr` y no `auto-fit`, que repartiría
   por el ancho disponible y no por lo que importa cada cosa.

   **Cada una mide lo que necesita** (Raúl, 8 sep. 2026, corrigiendo el mismo día):
   `align-items: start` en vez del `stretch` de serie de la rejilla. Igualarlas
   estiraba la del gráfico hasta el alto de la agenda —que trae doce renglones de
   calendario— y lo que crecía debajo de las barras era blanco. Un gráfico de doce
   pares de barras se lee igual de bien en 190px, y la fila deja de tener un hueco
   vacío del alto de media pantalla.

   La cadena de columna flex que hay debajo se queda: con alto natural no estira
   nada, y es la que hace que la rejilla del calendario llene su tarjeta.

   Apiladas por debajo de 1000px: ahí el cuarto se queda en 250px y el gráfico de
   doce pares de barras no se lee, así que a todo el ancho una debajo de otra. */
.panel-dos {
  display: grid;
  /* `minmax(0, …)` en las dos y no `3fr 1fr` a secas: un `fr` es
     `minmax(auto, 1fr)`, y ese `auto` es el min-content de la tarjeta. La agenda
     lleva dentro la rejilla del calendario, que tiene su propio suelo de 760px,
     así que la pista crecía hasta ese suelo y empujaba la fila fuera del lienzo:
     medido, 1210px de documento en una ventana de 1024. Con el mínimo en cero la
     tarjeta puede encogerse y la tabla se desplaza dentro de su caja, que es lo
     que `.cal { overflow-x: auto }` está ahí para hacer. */
  grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);
  align-items: start;
  gap: 16px;
  margin-bottom: 16px;
}
/* El botón que abre el gráfico en grande ocupa el cuerpo entero de su tarjeta,
   así que es un eslabón más de la cadena: sin esto se queda en su alto natural y
   el estirón muere en él. */
.panel-dos > .sup > .cuerpo > .grafico-abrir {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}
/* ---------- LA TARJETA DE HOY: LA REJILLA DEL CALENDARIO DENTRO ----------

   El relleno lo pone `.cal`, que trae el suyo de la pantalla del calendario. Sin
   esto se sumarían los dos y la tabla saldría hundida dentro de la tarjeta.

   La rejilla se queda el hueco que sobra, que es lo que hace que esta tarjeta
   pueda medir lo mismo que el gráfico de al lado sin dejar blanco debajo de la
   tabla: la cadena viene de `.panel-dos > .sup > .cuerpo`, ahí arriba. */
.panel-dia > .cuerpo {
  padding: 0;
}
.panel-dia .cal {
  flex: 1 1 auto;
}
/* El titular del día, en el cuerpo y no en el `h2`: el `h2` dice qué es la
   tarjeta y la fecha, y esto es la cifra de lo que hay hoy. Mismo cuerpo y misma
   línea de base que la cabecera que tenía la tarjeta anterior. */
.panel-dia .dia-cab {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin: 0;
  padding: 12px 16px 0;
}
.panel-dia .dia-cab .val {
  font-size: 20px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* El filtro de la agenda, al otro lado del titular. Si no cabe, baja de línea. */
.panel-dia .dia-filtro {
  margin-left: auto;
}
/* La salida al calendario. Lleva su relleno porque el cuerpo ya no tiene. */
.panel-dia > .cuerpo > .acciones {
  padding: 0 16px 16px;
}
/* Doce pares de barras en un cuarto del lienzo: el hueco de 8px entre columnas
   se comía más ancho que las propias barras. La identidad del dato no depende de
   esto —cada columna lleva su mes en el `aria-label` y en la línea de solo
   lectura—, pero verlo tiene que seguir valiendo para algo. */
.panel-dos .panel-ventas .cols {
  gap: 4px;
}

@media (max-width: 1000px) {
  .panel-dos {
    /* Y apiladas igual: en un móvil de 390px esta sola columna medía 662 —el
       min-content de la agenda— y era lo que hacía que la portada entera se
       pudiera desplazar en horizontal, con la barra de arriba cortada a la
       derecha porque ella sí mide lo que la ventana. */
    grid-template-columns: minmax(0, 1fr);
  }
  .panel-dos .panel-ventas .cols {
    gap: 8px;
  }
}

/* ============ LA PÍLDORA DE «EN CURSO» ============

   Lo que queda del anticipo del calendario, que fue la tarjeta de la portada
   entre el 21 ago. y el 8 sep. de 2026. Ese día la portada pasó a enseñar la
   rejilla del calendario entera (`.panel-dia`), y con la lista de renglones se
   fueron sus diez clases: `.anticipo`, `.anticipo-cab`, `.anticipo-filas`,
   `.anticipo-fila`, `.anticipo-franja`, `.anticipo-quien`, `.anticipo-obra`,
   `.anticipo-equipo`, `.anticipo-mas` y `.anticipo-vacio`, más su media query.

   ---------- Y SE SIGUE LLAMANDO `anticipo` Y NO `hoy` ----------

   Se escribió como `.hoy` y reventó el calendario entero. La rejilla de la semana
   marca la columna del día con `clases.push('hoy')` sobre un `<td>` y un `<th>`, y
   sus reglas van acotadas —`.cal td.hoy`, `.cal th.hoy`—; un `.hoy` a secas es
   (0,1,0) y le caía encima igual: la columna del viernes salía con fondo blanco,
   filete, esquinas de 14px y relleno.

   No se arregla acotando esto a `#v-panel`: la lección es que `hoy` ya significa
   algo en esta hoja de seis mil líneas, compartida por doce pantallas. Lo cuida el
   test `una clase no es dos componentes a la vez` de `herramientas/canto.test.ts`,
   y por eso el nombre sobrevive a la tarjeta que lo estrenó.

   ---------- QUÉ ES ----------

   Lo que está pasando ahora mismo, en el acento del material: es lo vivo de la
   pantalla (DIS-002 regla 2). Con glifo y texto, nunca el color solo. */
.anticipo-curso {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 500;
  padding: 3px 8px;
  border-radius: var(--r-pill);
  background: var(--canto-s);
  color: var(--canto-h);
  white-space: nowrap;
}
.anticipo-curso .ico {
  width: 11px;
  height: 11px;
}

/* --- estados: glifo + texto + color, en ese orden de importancia ---

   Peso 600 y un filete del mismo color que el texto, del 9-9-2026: la píldora
   ya llevaba fondo e icono coloreados y bastaba de sobra para AA, pero al lado
   de las cifras en Space Grotesk se leía floja. El filete no es un color
   nuevo: es el mismo semántico de siempre, un paso más definido. */
.est {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 600;
  padding: 2px 8px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: var(--panel);
  color: var(--subtinta);
  white-space: nowrap;
}
.est .ico {
  width: 11px;
  height: 11px;
}
.est.ok {
  background: var(--verde-s);
  border-color: var(--verde);
  color: var(--verde);
}
.est.aviso {
  background: var(--ambar-s);
  border-color: var(--ambar);
  color: var(--ambar);
}
.est.mal {
  background: var(--rojo-s);
  border-color: var(--rojo);
  color: var(--rojo);
}
.est.curso {
  background: var(--canto-s);
  border-color: var(--canto);
  color: var(--canto-h);
}

/* --- botones --- */
.btn {
  border-radius: var(--r-ctrl);
  padding: 0 15px;
  height: var(--toque);
  font-size: 13.5px;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
}
/* Un enlace vestido de botón llega con el subrayado y el azul (o el morado de
   visitado) del navegador. Ese azul no existe en Canto: no hay más acento que
   el del material (DIS-002 regla 3). Se le quitan los dos y se queda con la
   pinta del botón que ya lleva puesta. */
a.btn {
  text-decoration: none;
  color: var(--tinta);
}
/* ============ FONDO OSCURO, LETRA BLANCA. SIEMPRE ============

   La regla no se cumple sola, y esta hoja tenía un sitio donde se rompía. El
   `background` y el `color` de un fondo oscuro se declaran **juntos y en todas
   las reglas que pinten ese fondo**, aunque el color se heredara de la regla de
   al lado: separarlos es dejar que una regla futura mueva uno de los dos y deje
   texto ilegible sobre verde. */
.btn.pri {
  background: var(--canto);
  color: #fff;
}
/* ---------- Y `:not([disabled])` no es defensivo: es el arreglo ----------

   `.btn.pri:hover` son tres selectores de clase —(0,3,0)— y `.btn[disabled]` es
   una clase más un atributo —(0,2,0)—. **El hover GANA al deshabilitado**, por
   especificidad y no por orden, así que estar escrito después no lo salvaba: al
   pasar el ratón por encima de un botón primario deshabilitado, el fondo se
   volvía `--canto-h` y el color se quedaba en el `--subtinta` gris del
   deshabilitado. Verde oscuro con letra gris: ilegible.

   Es el reflejo de la trampa de las media queries, que no añaden especificidad:
   aquí el pseudo-selector sí la añade, y eso convierte un estado visual en un
   ganador silencioso.

   Se veía en tres botones de la aplicación y ninguno era nuevo —«Entrando…» del
   login, el de enviar del agente, y el de confirmar un presupuesto—; el último es el
   que lo destapó porque es el único que nace deshabilitado y se queda así hasta
   que eliges obra y proveedor, en vez de estarlo medio segundo. */
.btn.pri:hover:not([disabled]) {
  background: var(--canto-h);
  color: #fff;
}
.btn.sec {
  background: var(--blanco);
  border: 1px solid var(--hairline);
}
/* Con la misma guarda, y aquí el motivo no es el contraste sino la mentira: un
   botón deshabilitado que se ilumina al pasar por encima dice que se puede
   pulsar. Se nota menos porque `--panel` es justo el fondo del deshabilitado, y
   se arregla igual. */
.btn.sec:hover:not([disabled]) {
  background: var(--panel);
}
/* ============ EL BOTÓN SECUNDARIO ELEGIDO, DENTRO DE UN `.seg` ============

   Faltaba, y es un arreglo además de una regla nueva: el selector de canal de la
   hoja del hueco ya ponía `class="btn sec chico on"` sobre el elegido y **no
   había ninguna regla `.on` para un `.btn`**, así que las dos mitades se veían
   exactamente iguales. La elección solo la anunciaba `aria-pressed`, que es
   invisible: quien mira con los ojos no podía saber si estaba llamando o
   escribiendo, y eso es la regla 4 de Canto al revés —no es que el color fuera
   la única señal, es que no había ninguna.

   Se resuelve como el filtro activo, con las mismas cuatro declaraciones y por
   el mismo motivo: es lo elegido, y lo elegido lleva el acento del material
   (regla 2). El borde sigue siendo de 1px por los cuatro lados. */
.btn.sec.on {
  border-color: var(--canto);
  color: var(--canto-h);
  background: var(--canto-s);
  font-weight: 600;
}
/* ============ LA SALIDA SECUNDARIA DE UN PORTAL ============

   «He olvidado la contraseña» y «Volver a entrar». Es un `<button>` porque no
   navega a ningún sitio —cambia de portal dentro de la misma página— y se ve
   como un enlace porque es lo que es: la salida de al lado, no la acción.

   Sin fondo, sin borde y en `--subtinta`: el único acento de la pantalla sigue
   siendo el botón verde de arriba (regla 2 de Canto). Si esto llevara el canto,
   habría dos cosas compitiendo por ser lo que se pulsa, y la que gana no es la
   que hay que pulsar.

   `min-height: var(--toque)` y no el subrayado a secas: es un objetivo de dedo
   en una pantalla que también se abre desde el móvil (FE-01). */
.btn-enlace {
  display: block;
  width: 100%;
  min-height: var(--toque);
  margin: 2px 0 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--subtinta);
  font: inherit;
  font-size: 13px;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.btn-enlace:hover {
  color: var(--tinta);
}
/* ============ EL BOTÓN QUE DESTRUYE ============

   Es una DESVIACIÓN CONSCIENTE de Canto y hay que decirlo antes de usarlo. La
   regla 3 dice que el color del material es el único color de acción, y el
   comentario de la paleta dice que el rojo «solo habla de estado, nunca de
   acción». Aquí el rojo es acción.

   El motivo no es estético. El acento verde significa «esto se puede pulsar», y
   con él puesto, «Guardar» y «Eliminar» se ven exactamente igual: dos botones
   con el mismo color diciendo lo mismo. La regla existe para que no haya color
   decorativo ni un segundo acento, y esto no es ninguna de las dos cosas — es la
   única señal que separa lo que se deshace de lo que no. Un borrado no tiene
   Ctrl+Z, así que la advertencia va antes de la pulsación o no va.

   Sólido y no `--rojo-s`: el rosa pálido sobre blanco es justo lo que hacía que
   un botón de eliminar pasara desapercibido, que es el problema que esto arregla.
   Blanco sobre `--rojo` da 5.5:1 (AA de texto normal, y de sobra para 13.5px
   semibold).

   Y no va solo: como todo en Canto, el color nunca es la única señal (regla 4).
   Con él van su glifo y el verbo escrito, y el verbo dice lo que pasa —«Dar por
   resuelta», «Anular el parte»—, nunca un «Aceptar» genérico.

   ---------- QUÉ CUENTA COMO DESTRUIR EN ESTA APLICACIÓN ----------

   No lo que se borra, porque aquí **no se borra nada**: se archiva. Es una
   decisión documentada y viva —está escrita en el glifo `g-archivar` de
   `index.html`, «la caja que se cierra, no una papelera»— y este botón no la
   toca. Por eso no hay glifo de papelera y no debe añadirse: prometería algo que
   el sistema no hace.

   Lo que sí es destructivo es lo IRREVERSIBLE DESDE LA APLICACIÓN, que aquí son
   otras cosas: dar una incidencia por resuelta (no se reabre), anular un parte
   firmado, cancelar una visita ya agendada, tirar un dictado sin guardar.

   Y **devolver una obra a la fase anterior**, que se añadió el 18-08-2026 y es el
   que mejor explica por qué el criterio es este y no «borra filas». Rebobinar no
   borra nada —las visitas, el presupuesto y los pedidos se quedan donde están— y
   por eso iba en `.sec`. Pero no tiene deshacer y **no deja rastro**: el esquema
   todavía no guarda historial de fases, así que después no hay ninguna fila que
   diga de dónde venía la obra ni quién la movió. Un cambio sin vuelta y sin
   testigo es exactamente lo que este botón existe para advertir. Ver
   `retrocesoDeFase` y `retrocederFase` en `app.js`.

   ---------- Y LAS BAJAS, DESDE EL 18-08-2026 ----------

   Aquí decía que archivar un proveedor NO llevaba este botón, porque se deshace
   con «Volver a darlo de alta» y pintar de rojo lo reversible enseña a temer una
   acción que no tiene consecuencias. **Raúl decidió lo contrario el 18-08-2026:
   toda baja del programa va en rojo con letra blanca**, la esté o no.

   Así que el criterio de este botón ya no es uno solo, son dos: lo irreversible
   desde la aplicación, y **dar de baja algo** —un proveedor, un equipo—, aunque
   se pueda deshacer. Lo segundo es una desviación consciente de lo primero, y se
   escribe aquí para que quien añada una baja nueva sepa que le toca `.peligro` y
   no lo deduzca al revés leyendo el párrafo de arriba.

   ---------- Y BORRAR, CANCELAR Y DAR POR PERDIDO, DESDE EL 8 SEP. 2026 ----------

   Decisión de Raúl: **un botón que borra, que cancela o que da algo por perdido
   va en rojo**, como el de rebobinar la fase. Estaban repartidos —«Eliminar» y
   «Dar de baja» en rojo, «Cancelar el montaje», «Anular el pedido», «Borrar» una
   dirección y los dos descartes de la bandeja en `.sec`— y con el mismo color que
   «Editar» no se distinguía lo que se deshace de lo que no.

   El criterio de arriba lo cubre casi entero; esto lo cierra y quita el juicio de
   cada caso: si el verbo es borrar, cancelar, descartar, anular o dar por
   perdido, es `.peligro`.

   Con dos excepciones que Raúl fijó el mismo día: «Cancelar la visita» y «Dar la
   obra por perdida», las dos salidas de la preventa en la ficha de obra, siguen
   en `.sec`. Son la salida de emergencia de una fase a la que se viene a hacer
   otra cosa —medir— y en rojo se convierten en lo primero que se ve de la fila;
   el aviso lo sigue dando la hoja que abren, que sí es `peligro`.

   Lo que sigue SIN rojo: quitar un filtro, quitar un vínculo, el «Volver a
   ponerlo en marcha» que deshace una baja —ese devuelve algo, no lo quita— y el
   «Cancelar» de un formulario, que no cancela nada del negocio: cierra la hoja
   sin tocar lo que hay. Siguen siendo `.sec`. */
.btn.peligro {
  background: var(--rojo);
  color: #fff;
  border: 1px solid var(--rojo);
}
/* Misma guarda `:not([disabled])` que `.pri`, por lo mismo y para lo mismo: el
   hover son tres clases (0,3,0) y le gana a `.btn[disabled]` (0,2,0) por
   especificidad, no por orden. Sin esto, pasar el ratón por un «Eliminar»
   deshabilitado lo pintaría de rojo con la letra gris del deshabilitado. */
.btn.peligro:hover:not([disabled]) {
  background: var(--rojo-h);
  color: #fff;
  border-color: var(--rojo-h);
}
.btn.ancho {
  width: 100%;
}
/* Cerrar sesión: la letra roja sobre el botón secundario, no el rojo sólido de
   `.peligro`. Salir no destruye nada —lo único que se pierde es la sesión de
   este navegador—, así que no lleva la advertencia de lo irreversible; el rojo
   es aquí lo que lo separa del resto de una pantalla entera de ajustes. El
   glifo va en `currentColor` y se tiñe con la letra. */
.btn.sec.salir {
  color: var(--rojo);
}
.btn.sec.salir:hover:not([disabled]) {
  color: var(--rojo-h);
  border-color: var(--rojo-h);
}
/* El botón pequeño: 32px, la misma altura que una fila de tabla (DIS-002 regla
   5). Es para las acciones de cabecera de una ficha —«ver en grande», «cerrar»—,
   que acompañan a lo que se está mirando y no son LA acción de la pantalla; esa
   sigue siendo un botón de 44px.

   Los 44px de `--toque` son el mínimo de área táctil, así que esto no se usa en
   la única acción de una pantalla ni en nada que solo exista en móvil: aquí van
   dos atajos de escritorio que tienen su equivalente en el botón de volver y en
   la tecla Esc. */
.btn.chico {
  height: 32px;
  padding: 0 11px;
  font-size: 12.5px;
  gap: 6px;
}
.btn.chico .ico {
  width: 12px;
  height: 12px;
}
.btn[disabled] {
  background: var(--panel);
  color: var(--subtinta);
  border: 1px solid var(--hairline);
  cursor: not-allowed;
}
.btn .ico {
  width: 14px;
  height: 14px;
}

/* --- aviso en línea (FE-02: dice qué pasó y qué hacer) --- */
.nota-aviso {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  border-radius: var(--r-sup);
  padding: 11px 13px;
  font-size: 13px;
  background: var(--ambar-s);
  border: 1px solid #ebd5ac;
  color: #54350a;
}
.nota-aviso .ico {
  width: 14px;
  height: 14px;
  color: var(--ambar);
  margin-top: 2px;
}
.nota-aviso.malo {
  background: var(--rojo-s);
  border-color: #f0c4c0;
  color: #7c1d16;
}
.nota-aviso.malo .ico {
  color: var(--rojo);
}
/* El tercer estado del cartel: «Guardado.» (ADR-0020, pantalla de Ajustes).

   Verde de ESTADO y no el acento del material, aunque el token sea el mismo tono
   de familia: esto no se puede pulsar, dice que algo ha cambiado (Canto, regla
   2). Y como los otros dos, el color no va solo — delante lleva `#g-ok` y detrás
   la palabra (regla 4). */
.nota-aviso.bien {
  background: var(--verde-s);
  border-color: #bfe3cd;
  color: #10502a;
}
.nota-aviso.bien .ico {
  color: var(--verde);
}

/* ---- las seis casillas del código, cuando se quita el segundo factor ----

   La pieza es la común (`/codigo.js` y su `.codigo` en Canto); lo de aquí es
   dónde se planta. Alineadas a la izquierda y no centradas: dentro de una
   tarjeta ancha, seis cajas en mitad del hueco no se leen como la continuación
   del formulario que son. */
.mfa-casillas-k {
  font-size: 12px;
  color: var(--subtinta);
  margin: 0 0 5px;
}
.mfa-casillas {
  margin-bottom: 14px;
}
.mfa-casillas.codigo {
  justify-content: flex-start;
}

/* ============================================ LA ZONA DE SOLTAR UN FICHERO

   Dos pantallas la usan y es la misma pieza en las dos: la bandeja de presupuestos
   —donde `.soltar-caja` le pone el hueco y el filete que la separa de la tabla—
   y la acción «Subir el presupuesto» de la fase de una obra, con `.chica`.

   Sustituye al botón suelto, y la forma es la que tiene esto en todas partes por
   un motivo: un rectángulo punteado se lee como «aquí cabe algo», que es
   exactamente lo que hay que decir antes de que nadie arrastre nada.

   El punteado es la ÚNICA excepción al filete continuo de la hoja, y es
   deliberada: un borde discontinuo no dibuja una superficie —dice que ese hueco
   está esperando su contenido—, así que no compite con las tarjetas ni con las
   tablas. Sigue siendo 1px y sigue siendo igual por los cuatro lados: aquí no
   hay ninguna franja de color en un lateral (Canto, regla 1).

   Encendida —mientras el fichero está en el aire encima— pasa al acento por los
   cuatro lados y tiñe el fondo. Es «lo vivo» de la regla 2, el mismo verde que
   usa la columna del equipo cuando acepta una tarjeta, y dura lo que dura el
   arrastre. Sin sombras y sin animación: el cambio es instantáneo. */
.soltar-caja {
  padding: 12px 16px;
  border-bottom: 1px solid var(--hairline);
}
.soltar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 20px 16px;
  border: 1px dashed var(--hairline);
  border-radius: var(--r-sup);
  background: var(--panel);
  text-align: center;
  cursor: pointer;
}
.soltar:hover {
  border-color: var(--canto);
}
/* La zona encendida. El filete pasa a continuo además de al acento: en blanco y
   negro el punteado que se cierra sigue diciendo que la cosa se puede soltar,
   así que el color no es la única señal (Canto, regla 4). */
.soltar.recibe {
  border-style: solid;
  border-color: var(--canto);
  background: var(--canto-s);
}
.soltar-ico {
  width: 18px;
  height: 18px;
  color: var(--subtinta);
}
.soltar.recibe .soltar-ico {
  color: var(--canto);
}
.soltar-tit {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--tinta);
}
.soltar .k {
  margin: 0;
  max-width: 46ch;
  font-size: 11.5px;
  color: var(--subtinta);
}
.soltar .btn {
  margin-top: 4px;
}

/* ---- la misma zona, dentro de una celda de «Qué se hace en esta fase» ----

   Más apretada: ahí compite con otras tres acciones en una fila, y con el
   relleno de la bandeja la tarjeta de la fase crecía media pantalla. Lo que no
   cambia es nada de lo que la hace legible — el punteado, el acento al recibir y
   el botón dentro son los mismos, porque es la misma pieza y se aprende una vez. */
.soltar.chica {
  padding: 12px 10px;
  gap: 4px;
}
.soltar.chica .soltar-ico {
  width: 15px;
  height: 15px;
}
.soltar.chica .soltar-tit {
  font-size: 12.5px;
}

/* --- los tres estados de FE-02: cargando, vacío, error --- */
.estado-caja {
  padding: 28px 16px;
  text-align: center;
  color: var(--subtinta);
  font-size: 13px;
}
.estado-caja .t {
  display: block;
  color: var(--tinta);
  font-weight: 600;
  font-size: 14px;
  margin-bottom: 4px;
}
/* El glifo del vacío que es buena noticia (la bandeja al día). Verde de estado
   —«esto está bien»— y no el acento del material, que en Canto es solo de lo
   accionable, y aquí no hay nada que pulsar (regla 3). Va encima del título y no
   en línea: el hueco es una columna centrada. */
.estado-caja .marca {
  display: block;
  margin-bottom: 8px;
  color: var(--verde);
}
/* `margin: 0 auto` y no el `text-align: center` de la caja: los SVG de esta
   hoja son `display: block`, y un bloque no lo centra el alineado del texto. */
.estado-caja .marca .ico {
  width: 22px;
  height: 22px;
  margin: 0 auto;
}

/* ================================================================ TABLAS */

.tabla {
  /* 14px, el mismo cuerpo que el resto de la aplicación, y no los 13px de antes.
     La densidad de DIS-002 la pone la ALTURA de la fila —32px, cifras
     tabulares—, no una letra pequeña: a 13px la tabla se leía como una nota al
     pie de sí misma, y es la pantalla en la que se pasa el día quien la usa. */
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  overflow-x: auto;
}
.tabla table {
  width: 100%;
  border-collapse: collapse;
}

/* ---------- El reparto de las columnas ----------

   Anchos escritos para las columnas angostas —el código, la fase, el importe— y
   una sola sin ancho propio, la 2ª en las cuatro tablas: la del cliente y la
   obra, que es la que tiene texto de largo imprevisible y la que se queda con
   todo lo que sobre.

   Los anchos van aquí y no en `app.js` a propósito: son maqueta, no dato. El
   `nth-child` asume el orden de `cabeceras` en `app.js`; si ahí se mueve una
   columna, hay que moverla aquí.

   ============ POR QUÉ YA NO HAY `table-layout: fixed` ============

   Porque con reparto fijo un ancho escrito es un TOPE, y lo que no cabe se
   recorta: una razón social larga salía cortada con puntos suspensivos y no
   había forma de leerla entera sin abrir la ficha. Con el reparto automático el
   mismo ancho pasa a ser una preferencia —una columna nunca baja de su
   contenido— así que las angostas siguen midiendo lo que se les dijo y la
   elástica se queda el resto. Cuando no cabe, la elástica resume su texto
   (`.resumible`, más abajo) antes de que la tabla tenga que deslizarse.

   Lo que en su día se reprochó al automático —columnas de 300px medio vacías—
   pasaba SIN anchos escritos. Con ellos puestos no vuelve, y se ha comprobado a
   1200 y 1500px.

   El margen no se pierde al deslizar: el desplazamiento ocurre dentro de
   `.tabla`, que va por dentro del relleno de 16px de `.sup > .cuerpo`, y cada
   celda lleva sus 12px propios.

   Ya no hay `min-width` en estas cuatro tablas (26-9-2026). Era el suelo que
   evitaba que la columna elástica se quedara en 0px con el reparto fijo; con el
   automático no baja de su palabra más larga, y el suelo solo servía para
   empujar fuera de la vista las columnas del dinero y de los días cuando la
   ficha se abre al lado. */
/* Proyectos: código · OBRA / CLIENTE · fase · confirmado. */
#proyectos-tabla th:nth-child(1) {
  width: 116px;
}
#proyectos-tabla th:nth-child(3) {
  width: 190px;
}
#proyectos-tabla th:nth-child(4) {
  width: 150px;
}
/* Márgenes: código · OBRA / CLIENTE · venta · material · margen € · margen %.
   Los 132px del dinero son los que hacen que «100 487,00 €» NO se corte: un
   importe con puntos suspensivos no es un importe, es un número inventado. Y por
   eso esta tabla no se estrecha más: cuando no cabe, `.tabla` la deja
   desplazarse en horizontal en vez de recortar las cifras. */
#margenes-tabla th:nth-child(1) {
  width: 116px;
}
#margenes-tabla th:nth-child(3),
#margenes-tabla th:nth-child(4),
#margenes-tabla th:nth-child(5) {
  width: 120px;
}
#margenes-tabla th:nth-child(6) {
  width: 100px;
}
/* Bandeja: gravedad · AVISO · regla · abierto. */
#avisos-tabla th:nth-child(1) {
  width: 136px;
}
#avisos-tabla th:nth-child(3) {
  width: 210px;
}
#avisos-tabla th:nth-child(4) {
  width: 108px;
}
/* Posventa: gravedad · INCIDENCIA · origen · fotos · abierta. */
#incidencias-tabla th:nth-child(1) {
  width: 136px;
}
#incidencias-tabla th:nth-child(3) {
  width: 156px;
}
#incidencias-tabla th:nth-child(4) {
  width: 92px;
}
#incidencias-tabla th:nth-child(5) {
  width: 108px;
}
/* La salida a la pantalla entera debajo de una tabla que el panel ha recortado
   —«Ver las 14 pendientes» al pie de la bandeja—. Solo separación: el botón ya
   trae su aspecto de `.btn.sec.ancho`. Los 12px existen para que no se lea como
   una fila más de la tabla, que es lo que parecía pegado a la última. */
.sup > .cuerpo > .tras-tabla {
  margin-top: 12px;
}

/* Cabecera fija: en una tabla densa, perder los encabezados al bajar es
   perder de qué es cada columna (DIS-002 regla 5). */
.tabla thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
  text-align: left;
  font-size: 11.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--subtinta);
  font-weight: 600;
  padding: 0 12px;
  height: 28px;
  border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
}
.tabla th.num,
.tabla td.num {
  text-align: right;
}
/* Sin `max-width` por celda: lo único que se recorta es lo marcado con
   `.resumible` (abajo). Una cifra, una píldora o un botón, nunca; si aun así no
   cabe, la columna crece y la tabla se desliza dentro de `.tabla`.

   `nowrap` se queda: la fila mide 32px por DIS-002 (regla 5) y un nombre partido
   en dos líneas rompe la densidad de toda la tabla. */
.tabla td {
  padding: 0 12px;
  height: var(--fila);
  border-bottom: 1px solid var(--panel);
  white-space: nowrap;
}
/* ---------- LA ZEBRA ----------

   Pedida expresamente el 9-8-2026. Va contra lo que decía aquí antes —«sin
   zebra: la franja gris compite con el dato»— y contra la lista de prohibiciones
   de DIS-002, donde «zebra en tablas» está escrita al lado de las sombras y los
   degradados. Queda anotado dónde estaba la regla para que quien la busque
   encuentre la decisión y no una contradicción.

   Tres cuidados al ponerla, que es donde una zebra se estropea:

   1. `:not(.act)` — la fila seleccionada lleva el acento del material y ese
      color manda sobre la franja. Sin la exclusión, la zebra le gana por peso
      de selector y la selección desaparece en las filas pares.
   2. El hover va DESPUÉS y con el mismo peso, para ganarla por orden. Si no, la
      fila par no reacciona al pasar por encima y la tabla parece muerta.
   3. El gris del hover es más oscuro que el de la banda. Con el de antes
      —#fafbfb, más claro que la propia banda— pasar por una fila par la
      aclaraba: el hover restaba en vez de sumar. */
.tabla tbody tr:nth-child(even):not(.act) td {
  background: var(--panel);
}
.tabla tbody tr:not(.act):hover td {
  background: #ebebef;
}
.tabla .nom {
  font-weight: 600;
}
/* El código y la segunda línea suben con el cuerpo de la tabla: siguen siendo
   secundarios —mono y gris el uno, gris el otro—, pero 11,5px al lado de 14 era
   otra letra, no la misma en menor jerarquía. */
.tabla .cod {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--subtinta);
}
/* La segunda línea de una celda: el cliente bajo la obra en Proyectos y
   Márgenes, el elemento bajo el tipo de incidencia. Por sí sola no se recorta;
   cuando es texto largo lleva además `.resumible`. */
.tabla .sub {
  color: var(--subtinta);
  font-size: 12.5px;
}
/* ---------- LO QUE SE RESUME CUANDO NO CABE (Raúl, 26-9-2026) ----------

   Un asunto, un motivo, una descripción, y en las listas el nombre de la
   columna elástica: texto largo que empujaba fuera de la vista las columnas de
   la derecha —la del botón, casi siempre—. Con sitio se lee entero; sin sitio se
   queda en un renglón que acaba en «…».

   Se puede partir por palabras pero solo se enseña el primer renglón. Así, para
   el reparto automático de la tabla, esta columna puede encoger hasta su palabra
   más larga, y es la que cede el sitio: las demás no se pueden partir.

   El texto entero sale al posar el ratón: lo pone `app.js`. */
.tabla .resumible {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  overflow: hidden;
  white-space: normal;
}
/* Fila seleccionada: tinte y peso, jamás una barra lateral. */
.tabla tr.act td {
  background: var(--canto-s);
}
.tabla tr.act .nom {
  color: var(--canto-h);
}
.tabla tr.act:hover td {
  background: var(--canto-s);
}
/* Fila pulsable. `tabindex` + `role` no bastarían solos, pero aquí la fila es
   una `<tr>` con un `<button>` dentro que ocupa la celda del nombre; el cursor
   es la pista visual. Ver `app.js`. */
.tabla tr.pulsa {
  cursor: pointer;
}
.tabla .celda-btn {
  all: unset;
  /* Igual que en `.th-btn`: tras `all: unset` el `box-sizing` vuelve a
     `content-box`. Aquí no hay padding, así que hoy no desborda nada — se pone
     por lo mismo que se pone allí, para que el día que lleve padding no se
     repita el corte de la derecha. */
  box-sizing: border-box;
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  color: inherit;
  display: block;
  width: 100%;
}
.tabla .celda-btn:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}
/* ---------- La estrella de Proyectos (V035) -------------------------------
   Marcapáginas de una persona. Dos dibujos y no dos colores —hueca y maciza—,
   porque el color nunca es la única señal (Canto, regla 4). El acento del
   material solo cuando está puesta: es lo vivo, y lo apagado es gris.

   La columna es estrecha y su `<th>` no tiene rótulo visible; el nombre está en
   un `.solo-lector` para que la tabla no anuncie una columna en blanco. */
.tabla th:has(.solo-lector),
.tabla td:has(.estrella) {
  width: 34px;
  padding-left: 10px;
  padding-right: 0;
}

.tabla .estrella {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  /* 28px dentro de una fila de 32: no llega a los 44 de `--toque` y es la misma
     excepción que `.btn.chico` —un atajo de escritorio que tiene su equivalente
     en el teclado—. La fila de 32px es la regla 5 de Canto y no se rompe por
     esto; lo que sí se hace es dejar el área pulsable a todo el alto de la
     celda, que es lo que se acierta con el ratón. */
  width: 28px;
  height: 28px;
  border-radius: 6px;
  /* El gris del texto secundario y no el del hairline: una estrella hueca en
     #d2d2d7 sobre blanco no se ve, y una acción que no se ve no existe. Apagada
     pero legible; el acento entra solo cuando está puesta. */
  color: var(--subtinta);
}

.tabla .estrella .ico {
  width: 15px;
  height: 15px;
}

.tabla .estrella.on {
  color: var(--canto);
}

.tabla .estrella:hover {
  background: var(--canto-s);
  color: var(--canto);
}

.tabla .estrella:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}

/* La raya que separa el bloque de marcadas del resto de la lista. Va en la
   ÚLTIMA marcada y no en cada una: lo que se dibuja es el corte, no un marco.
   Un hairline como todos los demás, ni una franja de color (Canto, regla 1). */
.tabla tr.corte-fav > td {
  border-bottom: 1px solid var(--tinta);
}

/* Sin leído: peso, no color. El color aquí sería un cuarto significado. */
.tabla tr.nuevo .nom {
  font-weight: 700;
}

/* Los dos botones de una fila de Tesorería que sigue pendiente («Cargar
   factura», «No aplica»): en fila, pegados, sin envolver. La celda ya lleva
   `white-space: nowrap` de `.tabla td`, así que solo hace falta el hueco
   entre los dos. */
.tabla .fila-acciones {
  display: inline-flex;
  gap: 6px;
}

/* El botón de volver —al inicio o a la lista—. Arriba a la izquierda y separado
   del contenido: es la salida, y una salida pegada a lo que se está leyendo se
   pulsa sin querer.

   `width: max-content` porque `.btn` no lo trae y un botón de bloque ocuparía la
   fila entera; queda al ancho de su texto, que es lo que se espera de una
   migaja. */
.btn.volver {
  width: max-content;
  margin-bottom: 16px;
}
/* `.proyecto-barra` vivió aquí del 8-9-2026 al 9-9-2026: la salida de la
   pantalla y la salida de la OBRA («Dar la obra por perdida»), en una línea
   propia encima de la ficha. Las dos suben a la cabecera con el resto de la
   convención — ver `.cabecera .centro` y `#cabecera-acciones` en
   `index.html`, grupo `data-vista="proyecto"`. */

/* ================================================== EL LATERAL PLEGADO

   Plegado el lateral mide 56px y solo enseña los glifos. Tres cosas que no son
   obvias y que estaban mal en el primer intento:

     · **El ancho lo manda la retícula, no el lateral.** `.app` es un grid de
       `216px | resto`; encoger solo el `<nav>` habría dejado un hueco blanco de
       160px. La clase va en `.app` y cambia la columna.
     · **`overflow: hidden` en las entradas**, no `display:none` en el texto: al
       plegar, el texto se recorta contra el borde en vez de desaparecer de
       golpe, y la transición se ve como un cajón que se cierra.
     · **El glifo no se mueve ni un píxel** entre los dos estados. Si el icono
       saltara al plegar, la barra parecería otra barra.                        */

/* Plegado, el carril mide 56px: se cambia la variable y con ella se mueven a la
   vez la columna de la retícula y el ancho del lateral. */
.app.plegado {
  --carril: 56px;
}
/* Desplegar y plegar se animan (Raúl, 14-9-2026).

   Lo que se anima es `--carril`, y por eso va registrada: una variable sin tipo
   no se puede interpolar y saltaría de 56 a 216 de golpe. Moviéndose ella se
   mueven a la vez la columna de la retícula y el ancho del lateral, y no se abre
   hueco entre los dos.

   Plegar pasa por `plegando` (lo pone y lo quita `app.js`): el carril encoge con
   la maqueta desplegada todavía puesta, y la de plegado entra al acabar. La
   transición va en el estado desplegado y no en `.app` porque CSS usa la del
   estado al que se llega, y el paso final a `plegado` no tiene que moverse: el
   carril ya mide 56. Mientras encoge, nada parte renglón —la marca bajaría el
   nombre debajo del símbolo y el lateral crecería— ni saca barras de
   desplazamiento: se recorta contra el borde. */
@property --carril {
  syntax: '<length>';
  inherits: true;
  initial-value: 216px;
}
.app:not(.plegado) {
  transition: --carril 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.app.plegando {
  --carril: 56px;
}
.app.plegando .lateral,
.app.desplegando .lateral {
  overflow: hidden;
  white-space: nowrap;
}
.app.plegando .lateral .marca,
.app.desplegando .lateral .marca {
  flex-wrap: nowrap;
}
.app.plegando .simbolo-casa,
.app.desplegando .simbolo-casa {
  width: 28px;
  height: 28px;
}
/* El contenido no cambia de golpe mientras se mueve el carril. Al cerrar se
   desvanece desde el primer fotograma; al abrir permanece transparente hasta
   que `app.js` quita `desplegando`, ya pasada la mitad del recorrido. El
   desplazamiento de 3px basta para acompañar al cajón sin hacer que los rótulos
   parezcan entrar desde otra pantalla. */
.lateral .marca .nombre,
.lateral .logo-cliente,
.lateral .nav-sec,
.lateral .nav-it .txt,
.lateral .nav-it .cuenta,
.lateral .quien {
  opacity: 1;
  transform: translateX(0);
  transition:
    opacity 130ms ease,
    transform 180ms ease;
}
.app.plegando .lateral .marca .nombre,
.app.plegando .lateral .logo-cliente,
.app.plegando .lateral .nav-sec,
.app.plegando .lateral .nav-it .txt,
.app.plegando .lateral .nav-it .cuenta,
.app.plegando .lateral .quien,
.app.desplegando .lateral .marca .nombre,
.app.desplegando .lateral .logo-cliente,
.app.desplegando .lateral .nav-sec,
.app.desplegando .lateral .nav-it .txt,
.app.desplegando .lateral .nav-it .cuenta,
.app.desplegando .lateral .quien {
  opacity: 0;
  transform: translateX(-3px);
}
.app.plegado .lateral {
  padding: 16px 8px;
}
/* El texto se va; el glifo se queda donde estaba. */
.app.plegado .nav-it .txt,
.app.plegado .nav-it .cuenta,
.app.plegado .plegar .txt,
.app.plegado .lateral .quien,
.app.plegado .logo-cliente,
/* El rótulo del grupo también: en un carril de 56px no cabe «OPERACIONES» y
   recortarlo deja «OPERA», que no es un rótulo sino un error. El grupo sigue
   agrupando —lo dice el filete de abajo— y el nombre sigue en el árbol de
   accesibilidad por el `aria-labelledby` del `role="group"` (FE-01). */
.app.plegado .nav-sec {
  display: none;
}
/* Sin rótulo, el corte lo marca el filete. Es el hueco de 1px de Canto, el mismo
   que separa el pie del lateral: no se inventa aquí un separador nuevo. */
.app.plegado .nav-grupo + .nav-grupo {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--hairline);
}
/* Plegado se queda solo el símbolo de Occam, sin el nombre. Recortar «GlassFlow»
   contra el borde dejaba «Glass» o menos, y de ahí no se lee ni el producto ni la
   casa; el símbolo sí se reconoce y es el mismo en las tres Flow. El nombre no se
   borra, se pasa a `.solo-lector`: sigue en el árbol de accesibilidad (FE-01). */
.app.plegado .lateral .marca {
  overflow: hidden;
  white-space: nowrap;
  margin-bottom: 14px;
  justify-content: center;
}
.app.plegado .lateral .marca .nombre {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
/* 28px y no el 1,15em de la marca desplegada: en el carril de 56px hay 40 útiles,
   y a 24px el arco se leía como un glifo más de los de navegar en vez de como la
   marca de la casa. */
.app.plegado .simbolo-casa {
  width: 28px;
  height: 28px;
}
.app.plegado .nav-it {
  justify-content: center;
  padding: 0;
}
.app.plegado .plegar {
  justify-content: center;
}
/* FE-01: plegado, el nombre de la sección desaparece de la pantalla pero NO del
   accesible. `title` da la pista al ratón y el texto sigue en el DOM para el
   lector de pantalla: `display:none` sobre el `<span>` sí lo quitaría del árbol,
   así que el nombre accesible se pone explícito desde `app.js` con
   `aria-label`. Aquí solo se recorta lo que se ve. */

/* El botón de plegar, arriba del todo del lateral. */
.plegar {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 30px;
  padding: 0 8px;
  margin-bottom: 6px;
  border-radius: var(--r-ctrl);
  font-size: 11.5px;
  color: var(--subtinta);
}
.plegar:hover {
  background: var(--panel);
}
/* El botón de menú NO existe en escritorio: allí la navegación está siempre a la
   vista en su carril, y un botón para abrir lo que ya está abierto es ruido.
   La media query de móvil lo enciende. */
.menu-movil {
  display: none;
  align-items: center;
  gap: 7px;
  min-height: var(--toque);
  padding: 0 12px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  font-size: 13.5px;
  /* Los dos colores del LATERAL, que es donde vive este botón: ahí dentro
     `--tinta` es #e9ecec. Con el fondo blanco que tenía antes —de cuando la
     barra era clara— quedaba texto casi blanco sobre blanco, y la única forma de
     encontrar el menú era saber que estaba ahí. */
  color: var(--tinta);
  background: transparent;
}
.menu-movil .ico {
  width: 16px;
  height: 16px;
}
.plegar .ico {
  width: 14px;
  height: 14px;
}

/* --- barra de filtros. Una fila, encima de la tabla ---

   Más compacta desde el 9-9-2026 (Fernando): dos barras apiladas —cortes y
   buscador, como en Proyectos— se comían casi 100px antes de la primera fila
   de la tabla. El padding baja de 9px a 6px y los propios filtros se
   encogen (ver `.filtro` y `.filtro.grande`), sin perder el filete que
   separa cada barra ni el toque de 44px de los botones de verdad — esto
   sigue siendo un chip de filtro, no un control de formulario. */
.barra {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--hairline);
  flex-wrap: wrap;
}
/* Una barra sin nada dentro no pinta: son 19px de blanco y un filete que se
   leen como una fila vacía de la tabla. La de Incidencias que trae el filtro de
   proveedor (`#incidencias-filtro-prv`) vive así casi siempre — se rellena solo
   cuando se llega desde la ficha de un proveedor. */
.barra:empty {
  display: none;
}
.barra-separada {
  margin: 14px 0 6px;
}
.lista-empresas {
  display: grid;
  gap: 8px;
}
.filtro {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  padding: 4px 9px;
  min-height: 25px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  color: var(--subtinta);
  background: var(--blanco);
}
.filtro:hover {
  background: var(--panel);
}
/* Filtro activo: acento. Es «lo vivo», que es justo para lo que existe. */
.filtro.on {
  border-color: var(--canto);
  color: var(--canto-h);
  background: var(--canto-s);
  font-weight: 600;
}

/* --- filtros grandes, los de cabecera de sección ---

   Los cortes de Proyectos y las bandas de Márgenes son la primera decisión de
   la pantalla, no un ajuste fino, así que tienen un punto más de tamaño que
   el filtro normal: 30px de alto y 12.5px de texto, contra los 25/11.5 de
   abajo (hasta el 9-9-2026 eran 36/13 — ver la cabecera de `.barra`). Siguen
   siendo el mismo componente —mismo borde de 1px por los cuatro lados, mismo
   radio— solo que a otra escala.                                              */
.filtro.grande {
  font-size: 12.5px;
  padding: 5px 12px;
  min-height: 30px;
  font-weight: 500;
}
.filtro.grande .ico {
  width: 13px;
  height: 13px;
}

/* --- y el color de esos filtros ---

   ATENCIÓN, esto es la regla 3 de Canto y conviene no romperla por accidente:
   **un solo acento en todo el sistema**, y ámbar/verde/rojo hablan SOLO de
   estado. Por eso aquí el color no es decoración ni una forma de distinguir un
   botón de otro: cada filtro coloreado nombra un estado que ya tiene ese color
   en el resto de la aplicación.

     · Las bandas de margen bajo → ámbar, el mismo de la píldora `.est.aviso` y
       del pedido que bloquea la planificación.
     · «En pérdida» → rojo, el mismo de `.est.mal`.
     · «Del 30 % arriba» → verde semántico, el mismo de `.est.ok`.

   La barra de proyectos NO lleva ninguno, y «Esperando material» tampoco aunque
   nombre un estado: los cuatro botones de esa barra son cortes de la misma
   lista —qué obras miro— y uno solo en ámbar entre tres grises se lee como una
   advertencia sobre el botón, no como el corte que hace. Cuatro filtros del
   mismo rango se pintan igual; el glifo es el que los distingue.

   «Activos», «En comercial» y «Todos» tampoco, por lo mismo y además porque no
   son estados. Pintarlos de azul y morado para que se distingan sería
   inventarse un segundo y un tercer acento, que es lo que DIS-002 prohíbe en su
   lista de prohibiciones.

   El estado ACTIVO de cualquiera de ellos sigue siendo el acento del material
   (`.filtro.on`, arriba): el color de reposo dice de qué habla el filtro, y el
   acento dice cuál está puesto. Son dos informaciones y no se pisan.           */
.filtro.aviso {
  border-color: #e2c48a;
  color: var(--ambar);
  background: var(--ambar-s);
}
.filtro.aviso:hover {
  background: #f9e8c9;
}
.filtro.malo {
  border-color: #eeb1ab;
  color: var(--rojo);
  background: var(--rojo-s);
}
.filtro.malo:hover {
  background: #fbdcd8;
}
.filtro.bueno {
  border-color: #a8d6ba;
  color: var(--verde);
  background: var(--verde-s);
}
.filtro.bueno:hover {
  background: #d5edde;
}
/* Puesto, cualquiera de ellos vuelve al acento: el verde del material gana al
   color del estado, porque «cuál está activo» es la pregunta más urgente de la
   barra. Va después en la hoja, así que gana por orden y sin `!important`. */
.filtro.on,
.filtro.aviso.on,
.filtro.malo.on,
.filtro.bueno.on {
  border-color: var(--canto);
  color: var(--canto-h);
  background: var(--canto-s);
  font-weight: 600;
}
.filtro .ico {
  width: 11px;
  height: 11px;
}
.barra .cuenta {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
}

/* Fondo `--panel` para una barra que se lee como fila subordinada de la de
   arriba y no como otra pantalla. Queda en pie para el pie de la bandeja de
   presupuestos y las demás barras encadenadas. */
.margen-barra {
  background: var(--panel);
}
/* El rótulo suelto de una barra —«De quién:», «Fase:», la pista del buscador—
   al cuerpo de los filtros que acompaña. Estaba solo en `.margen-barra`, y en
   las barras sin fondo —la de Incidencias— esos rótulos salían al cuerpo del
   texto corrido, más grandes que los botones de al lado. */
.barra > .k {
  font-size: 11.5px;
  color: var(--subtinta);
  font-weight: 600;
}
/* El par etiqueta + campo en línea. La etiqueta es un `<span>` dentro de un
   `<label>`, así que el clic en «desde» enfoca el campo sin `for` que mantener. */
.campo-linea {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--subtinta);
}
.campo-linea input {
  width: 62px;
  min-height: 25px;
  padding: 3px 7px;
  font: inherit;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--tinta);
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
}

/* --- cabecera ordenable ---

   La cabecera entera es un `<button>`, no un `<th>` con `onclick`: se llega con
   el tabulador y se activa con Enter (FE-01). El glifo va SIEMPRE, también sin
   ordenar —apagado— porque una acción que solo se descubre pulsando por
   casualidad no existe.

   `aria-sort` en el `<th>` es lo que hace que un lector de pantalla anuncie
   «ordenado ascendente»; sin él, el triángulo no le dice nada a nadie.        */
.tabla th.ord {
  padding: 0;
}
.tabla .th-btn {
  all: unset;
  /* `box-sizing` OTRA VEZ, después del `all: unset`, y no es redundante: `all`
     devuelve TODAS las propiedades a su valor inicial, y el inicial de
     `box-sizing` es `content-box` — o sea que este botón se saltaba el
     `* { box-sizing: border-box }` de la primera regla de la hoja. Con
     `width: 100%` y `padding: 0 12px`, eso hacía cada cabecera ordenable 24px
     MÁS ANCHA que su columna: las de en medio se metían debajo de la siguiente
     —invisible, porque la cabecera tiene fondo— y la última, «Confirmado», se
     salía por la derecha del tablero y aparecía cortada. Era el recorte de la
     derecha, y no el reparto de columnas. */
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  padding: 9px 12px;
  font: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
}
.tabla th.num .th-btn {
  justify-content: flex-end;
}
.tabla .th-btn:hover {
  color: var(--tinta);
}
.tabla .th-btn:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}
.tabla .th-btn .ico {
  width: 10px;
  height: 10px;
}
/* Ordenada por esta columna: acento. Es «lo vivo», igual que el filtro activo. */
.tabla th[aria-sort]:not([aria-sort='none']) .th-btn {
  color: var(--canto-h);
  font-weight: 700;
}

/* El buscador. Ocupa lo que le dejen, al contrario que los campos de margen,
   que son dos números cortos. */
.campo-linea.busca {
  flex: 1 1 240px;
  max-width: 420px;
}
.campo-linea.busca input {
  width: 100%;
  text-align: left;
}
.campo-linea.busca .ico {
  width: 12px;
  height: 12px;
  color: var(--subtinta);
}

/* ============================================ LA HOJA DE UNA INCIDENCIA

   La ficha en grande, superpuesta a la pantalla de la que se viene. Un
   `<dialog>` nativo: el fondo oscurecido, la trampa de foco, Esc y devolver el
   foco al salir vienen hechos, y lo de debajo queda inerte sin tocar nada.

   Sin sombra —Canto no tiene ninguna—: lo que la separa del fondo es el filete
   de 1px de siempre y el oscurecido de detrás. Es la misma superficie que el
   resto, solo que encima.                                                   */

.hoja {
  /* Sin relleno propio: lo lleva `.hoja-cuerpo`. Es lo que hace que un clic que
     llegue al `<dialog>` sea, sin ambigüedad, un clic en el fondo. */
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--panel);
  color: var(--tinta);
  width: min(1180px, calc(100vw - 48px));
  max-width: none;
  /* `dvh` con respaldo: con `100vh` la hoja se dimensiona contra una ventana más
     alta que la visible, así que el final de una ficha larga queda por debajo de
     la barra de direcciones y parece que la hoja está cortada. */
  max-height: calc(100vh - 48px);
  max-height: calc(100dvh - 48px);
  overflow: auto;
  /* Sobrescribe el `margin: auto` del navegador solo en vertical: la hoja se
     centra a lo ancho y arranca arriba, que es donde se está mirando. */
  margin: 24px auto;
  /* Al llegar al final de la hoja, la rueda NO sigue moviendo la página de
     debajo. Es la mitad del encierro; la otra mitad la pone `.con-hoja`. */
  overscroll-behavior: contain;
}

/* El fondo: oscurecido y **desenfocado**.

   El desenfoque no es adorno —Canto prohíbe sombras y degradados, y esto no es
   ninguna de las dos—: es lo que dice, sin escribirlo, que lo de detrás sigue
   ahí pero ya no se toca. Con solo el velo gris, la tabla de debajo se leía
   perfectamente y quien mira sigue intentando pulsarla.

   El velo baja del 38 % al 28 %: con el desenfoque encima, más oscuro apaga la
   pantalla en vez de retirarla al fondo. `-webkit-` para Safari, que todavía lo
   pide. Donde no haya `backdrop-filter` queda el velo solo, que es justo lo que
   había antes: se degrada a lo anterior, no a nada. */
.hoja::backdrop {
  background: rgb(29 29 31 / 28%);
  -webkit-backdrop-filter: blur(7px);
  backdrop-filter: blur(7px);
}

/* ---------- LA CAMPANA NO DESENFOCA, Y ES LA EXCEPCIÓN ----------

   Decisión de Raúl del 21 ago. 2026. Las hojas de alta sí desenfocan —son un
   formulario, y el desenfoque es lo que dice que lo de detrás ya no se toca— y la
   campana no: no es un sitio donde se hace algo, es un panel que se asoma para
   leer seis filas y se baja. Desenfocar la aplicación entera para eso la retira
   más de lo que el gesto vale.

   Y el velo vuelve al 38 %. No es un número al azar: es el que tenía antes de que
   existiera el desenfoque, y la razón está tres líneas arriba —el 28 % se eligió
   PORQUE el desenfoque ya retiraba el fondo, y sin él un velo tan claro deja la
   tabla de debajo perfectamente legible y a quien mira intentando pulsarla—.
   Quitar el desenfoque sin devolver el velo sería quedarse con la mitad de cada
   decisión.

   Dos clases, (0,2,0), así que le gana a `.hoja::backdrop` esté donde esté. Las
   dos propiedades del desenfoque se apagan explícitamente: `none` y no borrarlas,
   porque lo que se hereda aquí es la regla de arriba. */
.hoja.campana-hoja::backdrop {
  background: rgb(29 29 31 / 38%);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Las dos barras de la hoja «¿De quién es?»: el toggle y el buscador. Pegadas
   bajo la cabecera, separadas por el mismo filete que todo lo demás, y sin
   relleno lateral propio —lo pone la hoja— para que la lista de debajo empiece
   en la misma vertical que los botones. */
.destino-barra {
  padding: 9px 14px;
  border-bottom: 1px solid var(--hairline);
}

/* Las obras de un destino, sangradas bajo su nombre: es la misma relación que
   las piezas bajo su ventana en la incidencia rápida del móvil. */
.destino-obras {
  margin: 5px 0 0 14px;
  display: grid;
  gap: 5px;
  /* Cada obra ocupa lo que mide su nombre, no la fila entera: son botones de
     una lista, y estirados se leen como cajas de un formulario. */
  justify-items: start;
}

/* Las dos vistas de la campana: la misma barra de filtros que Sentinel, pegada
   debajo de la cabecera de la hoja y separada de la lista por el mismo filete.
   La cifra de cada una va dentro del propio botón —«Notificaciones 4»— y no en
   una pastilla aparte: es el número DE esa lista, y a 11.5px una pastilla más
   sería un adorno en una barra de 28px de alto. */
.campana-vistas {
  padding: 9px 14px;
  border-bottom: 1px solid var(--hairline);
}
.filtro-n {
  margin-left: 5px;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
}

/* Lo que el aviso propone, dentro de su fila de la campana.

   En línea y con reparto, no apilado como en la ficha: aquí son como mucho dos
   botones chicos y la hoja mide 720px, así que caben al lado. `flex-wrap` es
   para el estrechamiento de móvil, donde la hoja pasa a ocupar el ancho de la
   pantalla y dos etiquetas largas —«Reclamar al proveedor» y «Corregir la fecha
   de entrega»— no caben en una línea.

   Los 6px de arriba lo separan del subtítulo de la regla sin abrir la fila: la
   campana es una lista de seis, y cada píxel de alto que se añade aquí se
   multiplica por seis. */
.campana-hacer {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}

/* Y la fila que los lleva deja de medir 32px.

   `.tabla td` fija el alto de fila y centra el contenido, que es lo correcto para
   una fila de una línea y deja esta mal de las dos maneras: la pastilla de
   gravedad y los días quedan flotando a media altura de un bloque de 70px, y el
   título del aviso siguiente arranca pegado al último botón del anterior, así que
   parece suyo. Alineado arriba, cada aviso se lee de una pieza: pastilla, título
   y botones empiezan en la misma horizontal.

   `white-space: normal` porque el título ya no va solo en su línea: con dos
   botones debajo, «Segunda medición por agendar» en una hoja estrecha tiene que
   poder partirse en vez de ensanchar la tabla. */
tr[data-campana-aviso] td {
  height: auto;
  padding-top: 10px;
  padding-bottom: 10px;
  vertical-align: top;
  white-space: normal;
}

/* ============ LA HOJA DE LA CAMPANA: ARRIBA Y A LA DERECHA ============

   Es la misma superficie que las demás hojas, solo que no se centra: se pega al
   borde de arriba y al de la derecha, debajo del botón que la abre. Eso es lo
   que la hace legible como «el panel de la campana» siendo un `<dialog>` modal
   —y por ser uno trae la trampa de foco, el Esc y la vuelta del foco al botón
   (FE-01), que un panel flotante a mano se deja siempre.

   `margin-left: auto` es lo que la manda a la derecha: sobrescribe el `auto`
   horizontal de `.hoja`, que era lo que la centraba.

   El ancho va con `min()` y NO con una media query, igual que `.hoja.estrecha` y
   por lo mismo: `@media (max-width: 620px)` pone `.hoja { width: auto }` y esta
   regla —dos clases, (0,2,0)— le gana por especificidad estando donde esté. Con
   `min()` la medida sale bien en las dos anchuras y no hay dos sitios que
   mantener sincronizados. Los 40px de hueco cubren la barra de desplazamiento,
   que `100vw` cuenta y el hueco disponible no tiene. */
.hoja.campana-hoja {
  /* ---------- 720, Y LOS DOS NÚMEROS ANTERIORES ESTABAN MAL ----------

     Fue 520, luego 620 con la nota «a 620 caben las cuatro con holgura», y **no
     cabían**: medido en el banco el 21 ago. 2026 con la campana abierta, la tabla
     pide 674px y el hueco visible dentro de una hoja de 620 es 586 — se desbordaba
     88px, así que había que arrastrarla de lado para ver los días que lleva
     abierta una incidencia, que es justo la columna por la que se mira. Las cuatro
     columnas medían 92 + 404 + 103 + 76.

     Los 34px de diferencia entre la hoja y el hueco son el relleno de
     `.hoja-cuerpo` más los dos filetes. 674 + 34 = 708, y se redondea a 720 para
     que un nombre de cliente algo más largo tampoco empuje. Más largo todavía sí
     empuja, y para eso `.tabla` se desliza, que es lo que hace en toda la app.

     La lección de las dos veces: este número se mide con la hoja abierta, no se
     estima. `#campana-cuerpo .tabla` sabe decir su `scrollWidth`. */
  /* Los 72px de hueco NO son estética, son la barra de desplazamiento: `100vw`
     la cuenta y el ancho disponible no, así que con 40 la hoja medía 25px más de
     lo que cabía junto a su margen derecho y `margin-left: auto` se iba a −1px —
     la hoja pegada al borde IZQUIERDO, que es lo contrario de lo que se pide
     aquí. Medido en el banco a 390px. Es la misma trampa que ya está escrita en
     la regla de `.hoja` en móvil, y por eso ahí se resuelve con `width: auto`. */
  width: min(720px, calc(100vw - 72px));
  /* 26px por la derecha y no 16: es el `padding` del lienzo, así que el filete
     derecho de la hoja cae en la misma vertical que la campana que la abrió y
     que el borde de la cabecera. */
  margin: 14px 26px 14px auto;
  max-height: calc(100vh - 28px);
  max-height: calc(100dvh - 28px);
}

/* ---------------- LA HOJA DEL SENTINEL: DEBAJO DEL ROBOT ----------------

   La misma superficie que la de la campana y por los mismos motivos, con dos
   diferencias: **es más ancha** y se centra como cualquier otra hoja en vez de
   pegarse a un borde.

   ---------- 980, Y LOS 720 DE ANTES SE CORTABAN ----------

   Medido en pantalla el 28 ago. 2026, con los cinco casos dentro: las cinco
   columnas más el botón piden unos 790px de tabla, y el hueco útil de una hoja de
   720 son 686. O sea que la columna del botón salía cortada por la mitad y había
   que arrastrar la tabla de lado para llegar a «Confirmar» — que es la única cosa
   que se hace desde este panel. Es exactamente el mismo error que la hoja de la
   campana cometió dos veces, con el mismo remedio: se mide con la hoja abierta.

   980 y no 800: con un nombre de cliente largo la columna de la obra crece, y
   pantalla hay de sobra donde esto se usa.

   ---------- PEGADA A LA DERECHA, DEBAJO DEL BOTÓN ----------

   Estuvo centrada mientras el robot flotaba en medio del lienzo. Con el robot en
   la cabecera se cuelga como la de la campana, y por el mismo motivo: el filete
   derecho de la hoja cae en la misma vertical que el botón que la abrió y que el
   borde de la cabecera. Los 26px son el relleno del lienzo.                  */
.hoja.sentinel-hoja {
  width: min(980px, calc(100vw - 48px));
  margin: 14px 26px 14px auto;
  max-height: calc(100vh - 28px);
  max-height: calc(100dvh - 28px);
}
/* Y tampoco desenfoca, por lo mismo que la campana: es un panel que se asoma
   para leer cuatro filas. Dos clases, (0,2,0), así que le gana a
   `.hoja::backdrop` esté donde esté; las dos propiedades del desenfoque se
   apagan explícitamente porque lo que se hereda aquí es la regla de arriba. */
.hoja.sentinel-hoja::backdrop {
  background: rgb(29 29 31 / 38%);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Con una hoja levantada, la página de debajo no se desliza.

   El `<dialog>` modal ya la deja inerte —no se puede pulsar ni tabular dentro—
   pero el navegador la sigue desplazando con la rueda, y mover por detrás lo
   que no se puede tocar es de las cosas que hacen dudar de si el modal está
   abierto de verdad.

   `scrollbar-gutter: stable` en la raíz reserva el hueco de la barra sin medirlo
   ni escribir estilos inline. En un móvil la barra superpuesta no ocupa sitio. */
.con-hoja {
  overflow: hidden;
}
/* La barra de la hoja: quién es y por dónde se sale. Pegada arriba mientras se
   recorre una ficha larga —la salida no se va con el desplazamiento— y con el
   filete de 1px por abajo, igual que la cabecera de una superficie. */
.hoja-barra {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 12px 16px;
  background: var(--panel);
  border-bottom: 1px solid var(--hairline);
}
.hoja-tit {
  margin: 0;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.hoja-sub {
  margin: 2px 0 0;
  font-size: 12.5px;
  color: var(--subtinta);
}
.hoja-acciones {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.hoja-cuerpo {
  padding: 16px;
}
/* ---------- EL AVISO DE LA HOJA VA AL PIE, DEBAJO DEL BOTÓN ----------

   Estuvo entre la barra y el contenido hasta el 28 ago. 2026, y ahí no se leía:
   `.hoja` entera hace scroll y `.hoja-barra` es sticky, pero el aviso NO — se
   va con el contenido. Medido en la hoja de alta de cliente a 1440×780: la caja
   quedaba en y=0 y el botón «Dar de alta» en y=1102, con 730px visibles. O sea
   que se pulsaba Guardar abajo del todo, el fallo se pintaba mil cien píxeles
   más arriba, y en la pantalla no pasaba nada.

   Ahora es el último hijo de la hoja, así que sale justo debajo de la acción que
   lo ha provocado (FE-02, y la regla de Raúl del 28 ago. 2026: si hay un botón
   de confirmar, el error sale debajo del botón y no arriba del todo). El margen
   es el mismo por los cuatro lados para que quede en la columna del cuerpo.   */
.hoja > .nota-aviso {
  margin: 0 16px 16px;
}
/* La línea que explica por qué ha saltado la hoja sola al entrar en una obra. Esa
   sí va arriba: no contesta a ningún botón, es el estado con el que se llega. */
.hoja > .nota-aviso.hoja-obra {
  align-items: center;
  margin: 16px 16px 0;
}
.hoja-obra .btn {
  margin-left: auto;
}

/* ================================================= LISTA + FICHA AL LADO

   Se llamaba `.avisos-dos` y ahora es `.dos-paneles`: la usan la bandeja y la
   posventa, que son la misma forma —una lista densa a la izquierda, la ficha de
   lo seleccionado a la derecha— y duplicar la rejilla con otro nombre es como
   acaban separándose dos pantallas que deberían moverse juntas.              */

.dos-paneles {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: 16px;
  align-items: start;
  /* La rejilla se mueve cuando en Márgenes se abre una obra. 160ms es lo justo
     para que se lea como que la ficha empuja a la tabla y no como que la página
     ha cambiado sola. Bajo `prefers-reduced-motion` no hay transición: eso lo
     apaga la regla del principio de la hoja (FE-01). */
  transition: grid-template-columns 160ms ease;
}

/* ---------- Una columna vacía no ocupa sitio ----------

   Cuando en la columna de la ficha no hay NADA, la rejilla vuelve a una sola
   columna y la lista ocupa la pantalla entera. Es lo que pasa en la bandeja al
   día y en una posventa sin nada pendiente: las dos vacían ese hueco
   (`innerHTML = ''`) y hasta ahora seguía reservado, así que el cartel de «la
   bandeja está al día» quedaba en una caja que se cortaba a media pantalla con
   340px de blanco a la derecha sin nada dentro.

   La condición se escribe donde de verdad está —«¿hay algo en esa columna?»— y no
   como una clase que cada pantalla tenga que acordarse de poner y quitar: son
   cuatro sitios que la ponen, y el día que uno se olvide el hueco vuelve sin que
   nadie sepa por qué (FE-02).

   `:empty` es exacto: sin un solo nodo hijo, ni texto. Con la ficha dentro —o con
   el cartel de «pulsa una fila», que es contenido y sí merece su columna— no
   entra. Y la transición de `grid-template-columns` de arriba hace que el hueco se
   cierre y se abra como un movimiento, no como un salto. */
.dos-paneles:has(.panel-ficha-caja:empty) {
  grid-template-columns: minmax(0, 1fr);
}

/* Con una obra abierta, la ficha manda: se lleva 460px y la tabla se estrecha
   hasta donde haga falta.

   `minmax(0, 1fr)` en la primera columna y no `1fr` a secas es lo que hace que
   estrecharse sea posible: con `1fr`, el contenido mínimo de la tabla —una fila
   de importes que no parte— pone un suelo al ancho y la ficha nunca llegaría a
   sus 460px. Esta es la razón de que ese `minmax` estuviera ya ahí. */
.dos-paneles.abierto {
  grid-template-columns: minmax(0, 1fr) 460px;
}
/* ---------- La ficha acompaña al scroll ----------

   La tabla de estas tres pantallas mide cincuenta filas y la ficha mide una
   pantalla. Sin esto, bajar por la tabla dejaba la ficha arriba y la mitad
   derecha de la ventana en blanco: la obra que se estaba comparando desaparecía
   justo mientras se comparaba.

   **El `sticky` va en el ELEMENTO DE LA REJILLA y no en la `.sup` de dentro.**
   Es la única forma de que funcione: el bloque contenedor de un elemento de
   rejilla es su ÁREA, y el área ocupa el alto de la fila entera —el de la tabla
   larga—, así que hay recorrido por el que deslizarse. Puesto en la `.sup`, el
   contenedor sería el `<div>` de fuera, que con `align-items: start` mide lo que
   mide la ficha: cero píxeles de recorrido y un `sticky` que no hace nada.

   Y `align-items: start` de la rejilla, arriba, es la otra mitad de la
   condición: con el `stretch` de por defecto el elemento llenaría su área y
   tampoco tendría por dónde moverse. Los dos van juntos.

   `top: 20px` es el mismo relleno superior que `.lienzo`, así que la ficha se
   detiene donde empezaba: no es un número mágico, es el margen de la página.

   El `max-height` con su `overflow` hace falta para las fichas más altas que la
   ventana —la de una obra con presupuestos, pedidos e instalaciones lo es—:
   clavada arriba y sin recortar, su final quedaría fuera de la pantalla y sería
   inalcanzable. `dvh` con respaldo, por lo mismo que la hoja.

   No hay trampa de `.solo-lector` aquí, que es la que suele traer un `overflow`
   nuevo: `sticky` es un elemento posicionado, así que ya es el bloque contenedor
   de lo que lleve dentro en absoluto.

   ============ Y POR QUÉ SON DOS DIVS Y NO UNO ============

   Porque mientras la ficha era el propio elemento de la rejilla, **su alto era el
   de la fila** cuando la lista era más corta que ella: seis obras en la tabla y
   una ficha de una pantalla, y el alto de la página lo ponía la ficha. De ahí
   salían los dos defectos que se pidió arreglar, que eran el mismo:

     · **Pulsar una fila movía la página.** Al abrir otra obra la ficha se vacía
       mientras llega la nueva, la página encoge de golpe, el navegador recorta el
       desplazamiento hasta donde llega ahora y eso se ve como un tirón hacia
       arriba. Medido: se pulsaba la última fila estando abajo y la página subía
       cien píxeles, y con una ficha grande —presupuestos, pedidos y partes— subía
       hasta el principio. `sinSalto` no lo evitaba: solo hacía el tirón suave.
     · **La ficha se iba arriba y se quedaba fuera.** Un `sticky` no puede salirse
       de su bloque contenedor, y el contenedor medía lo que la propia ficha: sin
       recorrido por el que deslizarse, al bajar por la tabla la ficha se iba con
       ella y su mitad de arriba quedaba fuera de la pantalla.

   Con dos divs desaparecen los dos. El de fuera es el elemento de la rejilla,
   **mide cero** y es el que se pega: no cuenta para el alto de la fila, así que el
   alto de la página lo pone SOLO la lista y abrir una obra ya no lo cambia — y su
   recorrido de pegado es la tabla entera, que es lo que hacía falta. El de dentro
   sale del flujo (`absolute`), cuelga de ese punto y se recorta él solo cuando no
   cabe. Con la lista más corta que la ficha, la ficha se pinta encima del hueco
   vacío de debajo de la tabla, que es hueco y no tapa nada.

   `left/right: 0` y no un ancho: el ancho lo pone la columna de la rejilla, que es
   la que cambia de 340 a 460. Escribirlo aquí sería el mismo número en dos sitios. */
.panel-ficha {
  position: sticky;
  top: 20px;
  height: 0;
}
/* ALTO FIJO y no `max-height`, y este es el segundo medio arreglo del salto.
   Fuera del flujo, la caja ya no cuenta para el alto de la fila —eso lo arregla el
   `height: 0` de arriba— pero **sí cuenta para lo que se puede desplazar**: si
   sobresale por debajo de donde acaba la tabla, la página se alarga hasta su
   final. Con `max-height`, ese sobrante cambiaba con lo que hubiera dentro —una
   ficha de 500px, un «Cargando…» de 100— y volvíamos al mismo sitio: la página
   encogía al pulsar otra obra y el navegador recortaba el desplazamiento. Medido:
   42px de subida en una lista de seis obras.

   Con el alto fijo el sobrante es siempre el mismo, así que **nada de lo que se
   pinte dentro puede mover la página**. La caja es transparente: cuando la ficha
   es corta, lo que queda debajo no se ve, y al no desbordar no se lleva la rueda
   del ratón. Y `overflow-y: auto` sigue haciendo su trabajo con las fichas más
   altas que la ventana, que se leen con su propio desplazamiento.

   Vacía vuelve a cero: si no, una columna que ya no se ve —la rejilla la cierra
   arriba— seguiría alargando la página una pantalla entera por nada. */
.panel-ficha-caja {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: calc(100vh - 40px);
  height: calc(100dvh - 40px);
  overflow-y: auto;
}
.panel-ficha-caja:empty {
  height: 0;
}

/* El aviso que ocupa el sitio del botón cuando un pedido no puede salir: al
   proveedor se le pide por correo y su ficha no tiene dirección (ADR-0020).

   Ámbar y no rojo: no se ha roto nada, falta un dato — el pedido sigue en la
   recámara esperando. Es el mismo ámbar de `.est.aviso` y de las bandas de
   margen bajo, y como en ellas el color no va solo: delante lleva su glifo y
   detrás la frase entera (Canto, regla 4).

   VA AQUÍ Y NO AL FINAL DE LA HOJA a propósito. De la línea 2397 para abajo
   esto es todo `@media`, y una media query no añade especificidad: una regla
   normal escrita después le gana a la de dentro y apaga lo responsive sin que
   nada falle. */
.tabla .sub.sin-canal {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--ambar);
  white-space: normal;
}
.tabla .sub.sin-canal svg {
  flex: 0 0 auto;
}

/* El atajo de una fila —«Ir al proveedor», «Ir a la obra»—, debajo de la frase o
   de la píldora que lo pide. Secundario y separado por 4px: el acento de la fila
   es el botón que cursa el pedido, y éste solo lleva a donde se arregla lo que
   la columna acaba de decir. */
.fila-atajo {
  margin-top: 4px;
}

/* ==================== EL FORMULARIO DE AJUSTES (ADR-0020) ====================

   Los trece campos del membrete, en una sola superficie. La superficie ES el
   `<form>` —`.sup.ajustes-form` en el HTML—, así que su `h2` y su `.cuerpo` ya
   los visten las reglas de `.sup`: aquí solo va lo de dentro.

   Y lo de dentro es una rejilla que se reparte sola. `auto-fit` con un mínimo de
   220px da tres columnas en el escritorio, dos en una tableta y una en un móvil
   sin una sola media query — que es mejor que tres puntos de corte a mano, sobre
   todo con el lateral quitando y poniendo 240px de ancho al lado.

   Canto: filete de 1px por los cuatro lados en el `fieldset`, ni una franja de
   color en ningún borde (regla 1), y el único acento es el botón de guardar
   (regla 2). Sin sombras.

   VA AQUÍ Y NO AL FINAL DE LA HOJA, por lo mismo que `.tabla .sub.sin-canal` de
   arriba: de aquí para abajo esto es todo `@media`, y una media query no añade
   especificidad — una regla normal escrita después le gana y apaga lo responsive
   sin que nada falle. */
/* El ancho lo pone la VISTA y no el formulario, y esa es la diferencia que se ve
   en cuanto se guarda algo: con el tope solo en el `<form>`, el cartel de
   «Guardado.» se estiraba hasta el borde de la pantalla y la superficie se
   quedaba 400px antes. Dos cajas que hablan de lo mismo tienen que empezar y
   acabar en la misma línea.

   980px porque son tres columnas de 220 con sus huecos y un poco de aire: más
   ancho da cuatro columnas y un formulario de trece campos en cuatro columnas se
   lee como un listado, no como una ficha. */
.ajustes-secciones > .nota-aviso,
.ajustes-cuenta > .nota-aviso,
.ajustes-form {
  max-width: 980px;
}
.ajustes-grupo {
  margin: 0 0 18px;
  padding: 14px 15px 4px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
}
.ajustes-grupo:last-of-type {
  margin-bottom: 4px;
}
/* El `legend` del navegador viene con su propio hueco y su propio tamaño. Se
   reescriben los dos: es un rótulo de sección, no un título. */
.ajustes-grupo > legend {
  padding: 0 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--tinta);
}
.ajustes-nota {
  margin: 0 0 12px;
  font-size: 12px;
  color: var(--subtinta);
}
/* Los cuatro materiales de la pantalla de Proveedores, con la misma rejilla que
   los campos de Ajustes: cuatro desplegables cortos uno al lado de otro mientras
   quepan, y apilados cuando no. Comparten regla porque son lo mismo —un campo con
   su rótulo y su ayuda debajo— y una copia se separaría a la primera. */
.prv-materiales,
.ajustes-rejilla {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0 14px;
  /* Igual que `.plan-cotas`, y por lo mismo: un motivo escrito debajo de un campo
     no tiene por qué estirar a los de al lado. */
  align-items: start;
}
/* Los dos que no piden 220px de ancho —el código postal y el teléfono— se quedan
   en una columna estrecha en vez de estirarse hasta el borde. Un campo de cinco
   dígitos con 300px de caja se lee como si faltara algo por escribir. */
.ajustes-rejilla > .campo.corto input {
  max-width: 180px;
}
/* Y los dos que son prosa ocupan la fila entera: una dirección de entrega y un
   párrafo de condiciones no caben en un tercio de la rejilla. */
.ajustes-rejilla > .campo.ancho {
  grid-column: 1 / -1;
}
/* La marca de obligatorio. Va con la etiqueta y en palabras, no con un asterisco
   rojo: un asterisco es color y una convención, y esto tiene que leerse igual en
   blanco y negro y con un lector de pantalla (Canto regla 4, FE-01). */
.ajustes-form .campo > .k .req {
  margin-left: 6px;
  font-weight: 400;
  font-size: 10.5px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ambar);
}
/* La línea de ayuda debajo del campo. Es lo que un `placeholder` NO puede ser:
   sigue estando después de escribir. */
.ajustes-form .campo > .ayuda {
  display: block;
  margin-top: 5px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--subtinta);
}
.ajustes-acciones {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}
/* El filete separa los botones de lo que hay encima. Cuando no hay nada encima
   —«Cerrar sesión», que es una sección de un solo botón— quedaba a 14px del de
   la cabecera: dos rayas paralelas con un renglón vacío entre medias. */
.cuerpo > .ajustes-acciones:first-child {
  padding-top: 0;
  border-top: 0;
}
/* El cartel de lo que falta, separado de la superficie que hay debajo.
   `:not(.oculta)` y no un margen fijo: escondido es `display: none`, pero el
   hueco de un hermano oculto se lo comería el `gap` si algún día esto fuera una
   rejilla, y así el margen solo existe cuando el cartel existe. */
.ajustes-secciones > .nota-aviso:not(.oculta),
.ajustes-cuenta > .nota-aviso:not(.oculta) {
  margin-bottom: 14px;
}

/* El resultado de guardar, pegado por debajo al botón que lo provoca y alineado
   con él a la derecha: el ojo que acaba de pulsar no tiene que ir a buscarlo.
   Vacío no ocupa —el contenedor existe siempre para que `aria-live` tenga a
   quien anunciar—, así que el hueco solo aparece cuando hay algo que decir. */
.ajustes-resultado:empty {
  display: none;
}
.ajustes-resultado > .nota-aviso {
  margin-top: 10px;
  /* Ajustado al texto y pegado a la derecha, que es donde está el botón. A todo
     ancho parecería un cartel de la pantalla y volvería a leerse como los de
     arriba; lo que se quiere es que se lea como la respuesta de ESE botón. */
  width: fit-content;
  max-width: 100%;
  margin-left: auto;
}

/* ==================== LA PÁGINA DE AJUSTES: ÍNDICE A LA IZQUIERDA ====================

   Petición de Raúl del 6-9-2026. La pantalla ya son cuatro secciones largas
   —membrete, colores, tipos de ventana y la cuenta de uno— y llegar a la última
   era deslizar a ciegas.

   Dos columnas: el índice a la izquierda y las secciones a la derecha, con el
   mismo ancho de 980px que tenían solas. El índice **no** se pega con `fixed`
   como el lateral, sino con `sticky`, y por eso la retícula lleva
   `align-items: start`: un hijo de retícula estirado ocupa toda su celda y
   entonces no tiene por dónde correr — `sticky` se escribe y no hace nada. Sin
   estirar, la celda mide lo que la fila (la columna larga de al lado) y al índice
   le sobra recorrido.

   El `top` es el mismo padding que el lienzo deja arriba: pegado al borde de la
   ventana, el índice se lee como si flotara sobre el título. */
.ajustes-pagina {
  display: grid;
  grid-template-columns: 190px minmax(0, 1fr);
  gap: 22px;
  align-items: start;
}
.ajustes-indice {
  position: sticky;
  top: 20px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
/* Las entradas hablan el idioma del lateral —misma altura de pulsación, mismo
   radio, mismo acento suave para la que está puesta— pero no reutilizan
   `.nav-it`: aquélla es una fila con glifo y recuento y ésta es un rótulo. Lo que
   se comparte de verdad es la regla de Canto: la activa lleva acento y peso, no
   una barra de color al lado. */
.ajustes-indice-it {
  padding: 6px 10px;
  border-radius: var(--r-ctrl);
  font-size: 13px;
  line-height: 1.35;
  color: var(--subtinta);
  text-align: left;
}
.ajustes-indice-it:hover {
  background: var(--panel);
  color: var(--tinta);
}
.ajustes-indice-it.on {
  background: var(--canto-s);
  color: var(--canto-h);
  font-weight: 600;
}
/* La única entrada que se señala: el segundo factor cuando no está puesto.

   Ámbar de estado —es algo por hacer, no algo roto— y `flex` solo aquí, que es
   donde hay un glifo que alinear: las demás entradas son texto y se quedan como
   estaban. El color no va solo: lleva el glifo delante y, para quien no ve
   ninguno de los dos, un «, sin activar» que solo leen los lectores de pantalla
   (Canto, regla 4). */
.ajustes-indice-it.falta {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--ambar);
  font-weight: 600;
}
/* Marcada Y en pantalla: manda el ámbar y no el acento, porque lo que dice esta
   entrada sigue siendo que falta algo. */
.ajustes-indice-it.falta.on {
  background: var(--ambar-s);
  color: #54350a;
}
/* El aire que se deja por encima de la sección a la que se salta. Sin esto, la
   sección aterriza pegada al borde de arriba de la ventana y no se ve que hay
   pantalla por encima. */
.ajustes-secciones > [data-indice] {
  scroll-margin-top: 16px;
}
/* La sección a la que se salta desde el índice, marcada un instante con el
   acento (Raúl, 28-9-2026). `outline` y no `border`: no mueve nada de sitio y
   vale igual para una superficie con filete que para «Mi cuenta», que no lo
   tiene. Se va sola; la clase la quita `ajustes.js`. */
.ajustes-secciones > .ajustes-destino {
  outline: 2px solid var(--canto);
  outline-offset: 2px;
  animation: ajustes-destino 1.6s ease-out forwards;
}
@keyframes ajustes-destino {
  0%,
  60% {
    outline-color: var(--canto);
  }
  100% {
    outline-color: transparent;
  }
}
/* Mi cuenta no es una superficie: es la caja que junta sus dos carteles con su
   formulario, para que el índice tenga un solo destino al que saltar. El aire de
   arriba es el mismo que separa las demás secciones entre sí. */
.ajustes-cuenta {
  margin-top: 14px;
}

/* ============ EL REPARTO POR MATERIAL DE UNA PRESUPUESTO (ADR-0021) ============

   Cuatro grupos —marco, vidrio, mosquitera, accesorio— uno debajo de otro, y lo
   que los separa es **un filete de un pelo por arriba**, no una caja para cada
   uno. Cuatro superficies anidadas dentro de una superficie serían cuatro
   marcos dentro de un marco, y Canto pide el hueco antes que el borde.

   Y el filete va ARRIBA y no abajo: así el primer grupo no lleva raya —ya la
   tiene la cabecera del `<h2>` de su superficie— y no se apilan dos líneas a un
   pelo de distancia. `:first-child` lo apaga en el primero. */
.material + .material {
  border-top: 1px solid var(--hairline);
  margin-top: 12px;
  padding-top: 4px;
}
/* La barra de cada grupo no lleva la raya de `.barra`: la separación entre
   grupos ya la pone la de arriba, y las dos juntas dibujan un renglón vacío. */
.material > .barra {
  border-bottom: 0;
  padding-bottom: 4px;
}
/* El nombre del material a la izquierda; la cuenta y quién lo trae, a la
   derecha. `.barra .cuenta` ya lleva `margin-left: auto`, así que basta con que
   el nombre NO sea `.cuenta` para que se quede donde está. */
.material > .barra > .pieza {
  font-size: 12px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Los avisos de un grupo, pegados a su tabla y con menos aire que los de la
   pantalla: aquí son una nota al pie de cuatro renglones, no un cartel. */
.material > .nota-aviso {
  margin: 8px 0 0;
}

/* ==================== UN DICTADO SIN ASIGNAR ====================

   Reutiliza el bloque de `.material` —la barra con su rótulo y su cuenta— porque
   es exactamente la misma pieza: un grupo con cabecera y filas dentro. Lo que
   cambia es que aquí cada fila lleva su párrafo y sus botones, no una tabla.

   Bordes iguales por los cuatro lados y ningún color de estado: lo que separa un
   borrador del siguiente es el filete de arriba, igual que entre dos materiales
   (Canto, regla 2). Lo que dice que hay que hacer algo con él es que esté en esta
   bandeja, no un tono. */
/* El relleno lateral de la bandeja. `.material > .barra` trae el suyo de
   `.barra`, pero los borradores colgaban del `.sup` sin ninguno y el texto salía
   pegado al borde de la superficie, desalineado de la barra de arriba y de la
   tabla de abajo. Y el filete de abajo, que es lo que separa la bandeja de la
   lista: sin él, «No hay nada pendiente» se leía como parte del dictado. */
#incidencias-borradores > .material {
  padding-bottom: 12px;
  border-bottom: 1px solid var(--hairline);
}
#incidencias-borradores .borrador {
  padding-inline: 12px;
}
.borrador + .borrador {
  border-top: 1px solid var(--hairline);
  margin-top: 10px;
  padding-top: 10px;
}
.borrador > .nom {
  margin: 0;
  font-weight: 600;
}
/* `.sub` y `.freno` no traen cuerpo por sí solas —son convenciones de la casa
   que escribe cada contexto, como `.k`—, y aquí no las escribía nadie: lo
   dictado y el motivo salían al cuerpo del texto corrido y en negro, o sea con
   más peso que el «otros · Medio» que los encabeza, y el `<p>` sin margen propio
   abría un renglón en blanco entre medias. */
.borrador > .sub,
.borrador > .freno {
  margin: 4px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--subtinta);
}
/* Los candidatos, en fila y con aire entre ellos. `.acciones` ya reparte, así
   que aquí solo se le da el hueco de arriba: el porqué de cada uno va dentro del
   botón y hace que sean altos. */
.borrador > .acciones {
  margin-top: 8px;
  gap: 8px;
  flex-wrap: wrap;
}
/* El porqué, dentro del botón y en la letra de lo secundario: es lo que hace
   explicable la sugerencia sin sacarla del sitio donde se pulsa. */
.borrador .acciones .sub {
  font-weight: 400;
}

/* Por debajo de 1000px no hay dos columnas que repartir: la ficha se va debajo
   de la tabla y el ensanchado no significa nada.

   Y el `sticky` se apaga con ellas, que es obligatorio y no una preferencia: sin
   columna al lado, la ficha se muda a la hoja de encima —`prestarALaHoja`, el
   mismo umbral de 1000px— y allí una ficha clavada arriba con su propio recorte
   sería un segundo contenedor de desplazamiento dentro del de la hoja. */
@media (max-width: 1000px) {
  .dos-paneles,
  .dos-paneles.abierto {
    grid-template-columns: minmax(0, 1fr);
  }
  .panel-ficha {
    position: static;
    height: auto;
  }
  /* Y la caja de dentro vuelve al flujo con él: fuera de flujo dentro de un
     elemento que ya no está posicionado, se colgaría del `.lienzo` y se iría a
     pintar encima de la tabla. */
  .panel-ficha-caja {
    position: static;
    max-height: none;
    overflow-y: visible;
  }
}

/* Y dentro de la hoja nunca, sea cual sea el ancho.

   El nodo de la ficha se PRESTA a la hoja tal cual —con sus clases puestas— así
   que sin esto bastaría con ensanchar la ventana por encima de 1000px con la hoja
   levantada para que la ficha se clavara arriba y con su propio recorte dentro de
   un contenedor que ya se desplaza. Gana por especificidad y no por orden, que es
   lo que hay que buscar cuando lo de arriba vive en una media query. */
.hoja .panel-ficha {
  position: static;
  max-height: none;
  overflow-y: visible;
}
/* Un aviso es una sola tarjeta: a 1180px sus botones quedaban de lado a lado de
   la pantalla. La hoja se estrecha cuando lo que lleva prestado es un aviso. */
.hoja:has(#hoja-prestada > #avisos-ficha) {
  width: min(680px, calc(100vw - 48px));
}
/* Y la caja que se presta, que es la que lleva el alto fijo y la posición
   absoluta de la columna: desde el 28-9-2026 la ficha de la bandeja se abre en la
   hoja también en escritorio, y con aquello puesto se quedaba en una franja. */
.hoja .panel-ficha-caja {
  position: static;
  height: auto;
  overflow-y: visible;
}
.ficha .ref {
  font-size: 11.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
  margin: 0;
}
.ficha .tit {
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 3px 0 10px;
}
.ficha .detalle {
  font-size: 13.5px;
  color: #3a3b3f;
  margin: 0 0 14px;
}
/* ---------- los pares rótulo/valor de una ficha ----------

   `.datos` es el bloque y `.dato` cada par. Lo pinta `listaDatos()` (`util.js`), y
   que el bloque EXISTA es la mitad de esta regla: sin un padre común, una fila no
   tiene forma de saber lo que mide el rótulo de la fila de al lado, y sin eso el
   carril del rótulo no se puede compartir.

   ============ POR QUÉ NO ES `space-between` ============

   Lo era, y con `space-between` el par se separa tanto como mida el contenedor. En
   la columna de 340px de una ficha eso se leía bien; el problema es que la MISMA
   lista se pinta en superficies `ancho` —el detalle de una fase, la ficha de un
   proveedor a pantalla entera—, y allí «Entró» se quedaba pegado a la izquierda y
   «13 ago» se iba al otro extremo: media pantalla de fila vacía que hay que barrer
   con la vista para emparejar un rótulo con su valor. Es el mismo defecto que
   `resumen()` evita en los recuentos, y venía del mismo sitio: una regla pensada
   para una columna estrecha, aplicada donde el contenedor es la ventana entera.

   ============ LOS DOS CARRILES ============

   1. `fit-content(168px)`, el rótulo. Mide lo que mida el rótulo MÁS LARGO del
      bloque, así que el valor arranca justo detrás y a la vez cuadra en vertical
      con los de arriba y abajo — las dos cosas a la vez, que es lo que no da una
      medida fija: con un carril de 168px clavado, «Entró» dejaba 130px de blanco
      hasta su fecha. El techo está para que un rótulo suelto muy largo
      («Instalaciones completadas») no se coma el carril del valor en una ficha de
      340px: pasado ese ancho se parte en dos líneas él, no la lista entera.
   2. `minmax(0, 1fr)`, el valor, que se lleva el resto del bloque. Y el BLOQUE es
      lo que está topado en 520px: en una columna de 340px eso no hace nada —el
      filete de la fila llega al borde de la tarjeta, como el de `.ficha-accion`
      justo debajo— y en una superficie ancha corta la fila ahí en vez de dejarla
      cruzar la pantalla. Probado al revés: con el carril del valor a ancho de
      contenido, los filetes de la tarjeta de un cliente se quedaban 50px cortos y
      la tarjeta parecía a medio pintar.

   El hueco entre rótulo y valor va de `padding-right` del rótulo y no de
   `column-gap` porque el rótulo es lo que se mide: con `gap`, `fit-content(168px)`
   tasa el texto y el hueco se suma aparte, de manera que el techo real pasan a ser
   182px y un rótulo largo vuelve a comerse el carril del valor.

   `subgrid` es lo que hace que la fila use los carriles del BLOQUE y no los suyos,
   y va detrás de `@supports` porque si no existe la declaración se cae sola y la
   fila se quedaría en un solo carril, con el valor debajo del rótulo. El respaldo
   es la regla de arriba —dos carriles por fila, el valor pegado al rótulo pero sin
   cuadrar de una fila a otra—: peor, pero se lee.                              */
.ficha .dato {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  font-size: 12.5px;
  padding: 6px 0;
  border-bottom: 1px solid var(--panel);
}
.ficha .dato:last-of-type {
  border-bottom: 0;
}
.ficha .dato .k {
  color: var(--subtinta);
  padding-right: 14px;
}
/* El valor arranca donde acaba el carril del rótulo, así que el `text-align: right`
   que hacía falta con `space-between` sobra — y puesto, volvería a separar el par.
   Tabulares se quedan: una columna de importes que no cuadre en vertical es peor
   que no tenerla. `anywhere` es para el valor sin espacios —un correo largo, una
   referencia— que en el carril de una ficha de 340px se saldría de la caja. */
.ficha .dato .v {
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
@supports (grid-template-columns: subgrid) {
  .ficha .datos {
    display: grid;
    grid-template-columns: fit-content(168px) minmax(0, 1fr);
    /* El tope, por lo mismo que `.fase-acciones .freno` se corta en 62ch: pasado
       ese ancho el filete ya no une un rótulo con su valor, solo estira. */
    max-width: 520px;
  }
  .ficha .datos > .dato {
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
  }
}
/* ---------- el resumen de una superficie ancha ----------

   Lo que sustituye a una lista de `.dato` cuando la superficie es `ancho`. Ver
   `resumen()` en `util.js` para cuál va dónde; aquí solo el cómo.

   Las separaciones son el `gap` de 1px dejando ver el fondo de la caja, no un
   borde por casilla: una casilla con `border-left` es exactamente lo que la
   regla 1 de DIS-002 manda buscar en esta hoja, aunque fuera gris, y esto no lo
   necesita para nada.

   `auto-fit` con `minmax(140px, 1fr)`: cuatro recuentos caben en fila en una
   pantalla de escritorio y bajan a dos y a uno según estrecha, sin media query.
   Y el `overflow: hidden` es lo que hace que el radio de la caja recorte las
   casillas de las esquinas — sin él, el fondo blanco de la primera casilla
   asoma por fuera de la curva.                                                */
.resumen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  overflow: hidden;
  margin-bottom: 14px;
}
.resumen .rk {
  background: var(--blanco);
  padding: 9px 13px;
}
.resumen .k {
  display: block;
  font-size: 12px;
  color: var(--subtinta);
}
/* 20px y no los 24 de `.cifra .val`: esto es el resumen DE una superficie, y al
   mismo cuerpo que un KPI del panel competiría con el titular de la pantalla.
   Tabulares, que es la mitad de la razón de ponerlos en fila. */
.resumen .v {
  display: block;
  margin-top: 2px;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
/* Una píldora dentro de una casilla se queda de su tamaño, no del de la cifra:
   es una etiqueta de estado y no un número. Se sube a la línea base del resto
   con el mismo hueco de arriba. */
.resumen .v .est {
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0;
}

.ficha .acciones {
  display: grid;
  gap: 8px;
  margin-top: 14px;
}
/* La fila de atajos de la cabecera de una ficha: ampliar a pantalla entera desde
   la columna, cerrar desde la pantalla entera. Arriba del todo y a la izquierda,
   antes que los datos, porque es la acción de la tarjeta y no su conclusión — la
   misma colocación que «Ir al proyecto» en la ficha de Márgenes.

   Visible siempre, nunca en `:hover`: una acción que se descubre por accidente
   no existe (FE-01, y la regla de Canto sobre acciones escondidas). */
.ficha .acciones-ficha {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}
/* Las acciones que Sentinel propone y esta versión todavía no puede ejecutar.
   Se enseñan deshabilitadas y con el motivo escrito: esconderlas haría creer
   que el aviso no lleva ninguna acción asociada. */
.propuesta {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--subtinta);
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  padding: 8px 11px;
}
.propuesta .ico {
  width: 13px;
  height: 13px;
}
.ficha textarea {
  width: 100%;
  min-height: 74px;
  padding: 9px 11px;
  font: inherit;
  font-size: 13px;
  color: var(--tinta);
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  resize: vertical;
}
.ficha .contador {
  font-size: 11px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
  margin: 4px 0 0;
  text-align: right;
}

/* ============================================ FOTOS DE UNA INCIDENCIA

   Las hizo alguien en una obra con un móvil: vienen en vertical, en apaisado y
   con la exposición que hubiera. Se pintan en una rejilla de cuadrados con
   `object-fit: cover` para que la ficha no se descoloque según lo que subió
   cada uno, y el hueco es del tamaño exacto tenga o no imagen dentro — así la
   ficha no da un salto cuando la foto tarda o falta.                        */

.fotos {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  margin: 0 0 14px;
}
.foto {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  overflow: hidden;
  background: var(--panel);
  cursor: zoom-in;
}
.foto img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.foto:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 2px;
}
/* Lo que se ve cuando el binario no está en el almacén. No es un icono roto
   del navegador: es un hueco con su motivo escrito (FE-02). */
.foto .nohay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px;
  font-size: 10.5px;
  line-height: 1.25;
  text-align: center;
  color: var(--subtinta);
}
.foto .nohay .ico {
  width: 15px;
  height: 15px;
}

/* ---- la respuesta del cliente al enlace del programa (V096) ----

   Debajo de la tabla de presupuestos de la obra. La firma va en su caja blanca y
   a tamaño de firma: es un trazo sobre fondo transparente, y sobre el gris del
   panel se leía como una mancha. */
.enlace-estado {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--subtinta);
}
.enlace-motivo {
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--tinta);
}
.enlace-datos {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 12px;
  margin: 8px 0 0;
  font-size: 13px;
}
.enlace-datos dt {
  color: var(--subtinta);
}
.enlace-datos dd {
  margin: 0;
  color: var(--tinta);
  overflow-wrap: anywhere;
}
.enlace-firma {
  display: block;
  width: min(100%, 240px);
  margin: 10px 0 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--blanco);
}
.enlace-firma img {
  display: block;
  width: 100%;
  height: auto;
}
.enlace-firma:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 2px;
}

/* El parte firmado (`d-parte`): lo que dejó escrito el equipo y el nombre de
   quien firmó, encima de su firma. */
.parte-obs,
.parte-firmante {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--tinta);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* ============================================================== GRÁFICOS */

/* Columnas de obra confirmada por mes.
   Forma «énfasis» (una serie en acento, el resto en gris de contexto): el mes
   en curso lleva el material porque es lo vivo; los cerrados son contexto. Sin
   eje: cada columna lleva su importe escrito encima, así que no hace falta una
   escala que el ojo tenga que interpolar. Serie única, luego no hay leyenda —
   el título de la superficie ya dice qué se mide. */
.cols {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  height: 168px;
  padding-top: 4px;
}
.col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  gap: 5px;
}
/* El valor va en tinta secundaria y NO en el color de la barra: el texto lleva
   tokens de texto, la barra lleva la identidad. */
.col .v {
  font-size: 10.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  /* Red de seguridad para la cifra que no quepa igualmente —un mes de siete
     dígitos en una tarjeta estrecha—: se recorta con puntos suspensivos dentro de
     su columna en vez de meterse encima de la de al lado. Cortada se ve que está
     cortada; pisada, las dos cifras se leen como una tercera que no existe. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Sin `flex: none` la etiqueta se encoge cuando la columna reparte el alto —es
     un hijo flex más— y con `overflow: hidden` puesto, encogerse significa
     comerse los rabos de las letras. Las etiquetas miden lo que miden; lo que
     cede es la barra. */
  flex: none;
}
.col .barra-col {
  width: 100%;
  background: var(--barra);
  transition: background 120ms ease;
  /* Extremo del dato redondeado, base anclada a la línea de cero. */
  border-radius: 4px 4px 0 0;
  min-height: 3px;
}
.col.viva .barra-col {
  background: var(--canto);
}
.col .m {
  font-size: 11px;
  color: var(--subtinta);
  flex: none;
}
.col.viva .m {
  color: var(--canto-h);
  font-weight: 600;
}
/* Señalar la barra que hay debajo del ratón. Pedido por Raúl el 18-08-2026.
   Tres decisiones dentro:

   · **El área que responde es la columna entera**, no los píxeles de la barra.
     Un mes flojo son 5px de alto: apuntar ahí es un juego de puntería, y el
     dedo del ratón ya está sobre esa columna cuando alguien la está mirando.
   · **El color no cambia de significado, solo de intensidad**: el gris de
     contexto se oscurece a `--barra-h` y el acento del mes en curso a
     `--canto-h`. Teñir de acento un mes cerrado diría que está vivo (DIS-002
     regla 2), que es lo que este gráfico usa el color para decir.
   · **Sin `cursor: pointer`**, a diferencia de `.col.pulsa`: estas columnas no
     llevan a ningún sitio. El resalte dice «es esta»; la mano prometería un
     clic que no hace nada (FE-01, y el comentario de `columnasMes` en `app.js`).

   `@media (hover: hover)` porque en una pantalla táctil `:hover` se queda
   pegado después de tocar: la barra se quedaría oscura hasta tocar otra cosa, y
   eso se lee como una selección que nadie ha hecho. */
@media (hover: hover) {
  .col:hover .barra-col {
    background: var(--barra-h);
  }
  /* (0,4,0) contra los (0,3,0) de arriba: gana por especificidad, no por orden,
     así que el mes en curso conserva su acento aunque alguien mueva la regla. */
  .col.viva:hover .barra-col {
    background: var(--canto-h);
  }
}

/* ---------- DOS BARRAS POR MES: EL MES Y EL MISMO MES DE HACE UN AÑO ----------

   El gráfico de «Lo vendido» de la portada y de su hoja. Mismo dibujo que el de
   arriba con una diferencia que obliga a cambiar la caja: cada columna lleva dos
   barras, y la altura de una barra en porcentaje solo se resuelve contra un
   contenedor de altura definida.

   Por eso la columna pasa a `grid` aquí: con `1fr` para la pareja de barras, la
   fila tiene alto definido y los `height: %` de dentro miden contra ella. Con el
   `flex` de `.col`, la pareja se encogería para dejar sitio a las etiquetas y las
   barras se saldrían por arriba de su propia columna.

   El color deja de ser énfasis y pasa a identificar: acento el mes, gris de
   contexto su pareja de hace un año. Por eso este gráfico lleva leyenda y el de
   una serie no — y por eso además cada columna lleva `title` y línea equivalente,
   que es lo que hace que la identidad no dependa solo del color (regla 4). */
.cols.dobles .col {
  display: grid;
  /* `minmax(0, 1fr)` y no `1fr`: el mínimo automático de `1fr` lo fija el
     contenido, y cada `<svg>` de barra mide 300px por defecto. Con él, la hoja
     de un año pintaba columnas de 603px en celdas de 90 — barras de un mes
     encima de las del siguiente y rótulos del pie pisados. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 1fr auto;
  gap: 5px;
  /* Los dos que hay que escribir aquí porque `.col` es `flex` y los suyos
     significan otra cosa en una rejilla: `align-items: center` dejaría la pareja
     de barras a la altura de su contenido —o sea, a cero, porque su alto es un
     porcentaje del hueco— en vez de estirada a la fila. */
  align-items: stretch;
  justify-items: stretch;
}
/* Con `justify-items: stretch` la cifra ocupa la columna entera, así que su
   centrado deja de venir de la caja y se escribe. */
.cols.dobles .col .v {
  text-align: center;
}
/* Con la cifra encima —solo en grande, donde cabe— la columna tiene tres filas. */
.cols.dobles.con-cifra .col {
  grid-template-rows: auto 1fr auto;
}
.cols.dobles .par {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 3px;
  min-height: 0;
  width: 100%;
}
.cols.dobles .par .barra-col {
  flex: 1 1 0;
  width: auto;
}
/* Las etiquetas del pie: una por barra y alineada con ella. Es una fila flex con
   el mismo hueco que la pareja de arriba, así que cada rótulo cae bajo su propia
   columna en vez de uno centrado bajo las dos. Cada uno lleva su año —«Ene 25» y
   «Ene 26»—, que es lo que hace que una barra diga de cuándo es sin depender de
   la leyenda. */
.cols.dobles .m {
  display: flex;
  gap: 3px;
  width: 100%;
}
.cols.dobles .m > span {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  /* Cortado se ve que está cortado; partido en dos líneas descoloca el pie de
     todas las columnas de la fila. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* ---------- LA VERSIÓN DE LA TARJETA ----------

   Barras estrechas y el par centrado en su columna (Raúl, 8 sep. 2026). Con las
   barras estiradas a todo el ancho —`flex: 1 1 0`, que es lo que hace falta en
   grande— el hueco de 3px de dentro del par y el de 4px de entre columnas se
   parecían demasiado: doce barras a la misma distancia no se leen como seis
   parejas, se leen como una fila desordenada. Con un ancho fijo, lo que separa
   dos meses es el hueco que sobra a cada lado, y ese SÍ es mayor que el de
   dentro. La separación es la misma entre todos los pares porque todas las
   columnas miden lo mismo: `.col` es `flex: 1 1 0`.

   9px no es un capricho: por debajo, una barra corta —el mínimo son 3px de alto—
   deja de leerse como barra y parece un punto. */
.cols.dobles.compacta .par {
  gap: 2px;
}
.cols.dobles.compacta .par .barra-col {
  flex: 0 1 9px;
  max-width: 9px;
}

.cols.dobles .barra-col.antes {
  background: var(--barra);
}
.cols.dobles .barra-col.ahora {
  background: var(--canto);
}
@media (hover: hover) {
  /* La columna entera responde, como en el de una serie: un mes flojo son 5px de
     alto y apuntar ahí es un juego de puntería. Cada barra se oscurece dentro de
     su propio color, que no cambia de significado. */
  .cols.dobles .col:hover .barra-col.antes {
    background: var(--barra-h);
  }
  .cols.dobles .col:hover .barra-col.ahora {
    background: var(--canto-h);
  }
}

/* ---------- LA TARJETA DE LO VENDIDO, EN LA PORTADA ----------

   `.grafico-abrir` nació en la ficha de un cliente, donde la tarjeta mide 396px y
   por eso su resumen va a una columna. El mínimo de 240px es lo que pide
   «−199 726,89 €» a este cuerpo, y con `auto-fit` la tarjeta se coloca sola:
   apilada cuando va estrecha —que es lo que le pasa desde el 8 sep. 2026, en el
   cuarto de la derecha de `.panel-dos`— y en dos casillas cuando le dan ancho,
   sin medio metro de blanco al lado. El alto del gráfico es suyo en los dos
   casos: es el que aquella tarjeta de la ficha no tenía. */
.panel-ventas .grafico-abrir .resumen {
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.panel-ventas .grafico-abrir .cols {
  height: 190px;
  min-height: 190px;
}

/* La leyenda. Solo la lleva el gráfico de dos series: con una, el título de la
   superficie ya dice qué se mide y una leyenda de una línea sería un rótulo
   repetido. */
.leyenda-grafico {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  align-items: center;
  font-size: 11.5px;
  color: var(--subtinta);
  margin-bottom: 8px;
}
.leyenda-grafico .muestra {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 5px;
  vertical-align: -1px;
}
.leyenda-grafico .muestra.ahora {
  background: var(--canto);
}
.leyenda-grafico .muestra.antes {
  background: var(--barra);
}

/* Proyectos por fase: nueve clases con significado. Nueve colores serían nueve
   colores indistinguibles bajo daltonismo y un segundo, tercero y noveno
   acento. Se resuelve con longitud + cifra, que es lo que la pregunta pide:
   cuántos hay en cada fase. */
.fases {
  display: grid;
  gap: 9px;
}
.fase .lin {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12.5px;
  margin-bottom: 3px;
}
.fase .lin .n {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fase .lin .c {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--subtinta);
}
.fase .via {
  height: 5px;
  background: var(--panel);
  border-radius: 3px;
  overflow: hidden;
}
.fase .relleno {
  height: 100%;
  background: var(--barra);
  border-radius: 3px;
  transition: background 120ms ease;
}
/* Lo mismo que en las columnas, y por lo mismo: la fila entera es el área que
   responde —la vía mide 5px— y el gris solo se oscurece. Aquí no hay ninguna
   barra en acento: nueve fases son nueve clases con significado y el color no
   distingue ninguna, así que basta con una regla. */
@media (hover: hover) {
  .fase:hover .relleno {
    background: var(--barra-h);
  }
}

/* Las bolas de fase de una obra: diez puntos en fila, y en cuál está.

   Tres estados y **tres señales por estado**, no solo el color: las pasadas van
   llenas en gris de contexto, la de ahora lleva el acento del material y es más
   grande, y las que quedan son un círculo hueco con el filete de 1px de siempre.
   Bajo daltonismo, el relleno y el tamaño siguen distinguiéndolas; y al lado va
   escrita la fase, que es la señal que no falla nunca (DIS-002 regla 4, FE-01).

   El acento va solo en la bola viva, que es lo que Canto reserva para lo que
   está en curso (regla 2). Diez bolas de color serían diez acentos. */
.bolas {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  vertical-align: middle;
  margin-right: 8px;
}
.bola {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1px solid var(--hairline);
  background: var(--blanco);
  flex: 0 0 auto;
}
.bola.hecha {
  background: var(--barra);
  border-color: var(--barra);
}
.bola.viva {
  width: 10px;
  height: 10px;
  background: var(--canto);
  border-color: var(--canto);
}

/* ================= EL RECORRIDO DE UNA OBRA =================

   Diez pasos en fila, con la línea que los une por detrás. Cada uno es un
   `<button>`: se pulsa para leer cuánto duró esa fase y por qué.

   El color, donde manda Canto: el paso en curso lleva el acento del material
   porque es lo vivo (regla 2); los hechos van en el gris de contexto de los
   gráficos, y los que faltan, huecos con el filete de 1px de siempre. Y el color
   no va solo: relleno, tamaño, glifo y el nombre debajo dicen lo mismo (regla 4,
   FE-01). */
.pasos {
  display: flex;
  overflow-x: auto;
  padding-bottom: 4px;
}
.paso {
  all: unset;
  box-sizing: border-box;
  flex: 1 1 0;
  min-width: 74px;
  text-align: center;
  position: relative;
  cursor: pointer;
  padding: 4px 2px 6px;
  border-radius: var(--r-ctrl);
}
/* La línea que une un paso con el anterior. Va por detrás del anillo (`z-index`)
   y arranca en el centro del paso de la izquierda. */
.paso:not(:first-child)::before {
  content: '';
  position: absolute;
  top: 19px;
  left: -50%;
  width: 100%;
  height: 1px;
  background: var(--hairline);
  z-index: 0;
}
.paso.hecha:not(:first-child)::before,
.paso.en_curso:not(:first-child)::before {
  background: var(--barra);
}
.paso .anillo {
  position: relative;
  z-index: 1;
  margin: 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--blanco);
  border: 1px solid var(--hairline);
  color: var(--subtinta);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.paso .anillo .ico {
  width: 12px;
  height: 12px;
}
.paso.hecha .anillo {
  background: var(--barra);
  border-color: var(--barra);
  color: var(--blanco);
}
.paso.en_curso .anillo {
  background: var(--canto);
  border-color: var(--canto);
  color: var(--blanco);
}
.paso .lab {
  display: block;
  font-size: 11px;
  line-height: 1.25;
  color: var(--subtinta);
  margin-top: 8px;
  padding: 0 2px;
}
.paso .fec {
  display: block;
  font-size: 10.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.paso.hecha .lab,
.paso.en_curso .lab {
  color: var(--tinta);
  font-weight: 600;
}
/* El abierto: tinte del material, nunca una barra lateral. */
.paso.sel {
  background: var(--canto-s);
}
.paso:hover .lab {
  color: var(--tinta);
}
.paso:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}
/* Las bolas cuando no se pulsan (`FASES_PULSABLES` en `proyectos.js`). Son
   `<span>`, así que ni el foco ni el `:hover` de arriba les llegan por el
   elemento — pero `:hover` sí casa con un `span`, y una etiqueta que se aclara
   al pasar por encima promete un clic que no existe.

   Va DESPUÉS de `.paso:hover .lab` y con una clase más, que es lo que hace que
   gane: entre dos reglas de la misma especificidad manda la última, y aquí no se
   quiere depender de eso. */
.pasos.fijos .paso {
  cursor: default;
}
.pasos.fijos .paso:hover .lab {
  color: var(--subtinta);
}
.pasos.fijos .paso.hecha:hover .lab,
.pasos.fijos .paso.en_curso:hover .lab {
  color: var(--tinta);
}

/* El detalle de la fase abierta, debajo de los pasos y separado por el mismo
   filete de 1px que todo lo demás. */
.fase-detalle {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}

/* ============ EL DETALLE DE LA FASE VA EN HORIZONTAL ============

   Esta tarjeta es de pantalla entera —mil quinientos píxeles en un monitor de
   oficina— y lo que llevaba dentro medía trescientos: cuatro datos de dos
   palabras, unas frases y una columna de botones, con el resto en blanco a la
   derecha. Lo que se perdía no era sitio, era información por debajo del
   pliegue: en `medicion` había que bajar con la rueda para leer que las cotas
   las meten los medidores desde el móvil.

   Ahora son dos columnas. Desde el 27-9-2026 (Raúl) el próximo paso va a la
   izquierda, que es lo primero que se lee, y a la derecha la información —el
   porqué calculado y dónde pasa la otra mitad de la fase—, que es texto y se lee
   mejor estrecho. El techo de 380px en esa columna es eso mismo: una línea de
   más de unos 80 caracteres deja de leerse de un barrido. */
.fase-cuerpo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(260px, 380px);
  gap: 24px;
  align-items: start;
}
/* Con una sola de las dos columnas, la que queda se lleva el ancho. */
.fase-cuerpo.sola {
  grid-template-columns: minmax(0, 1fr);
}

/* La cabecera: el nombre de la fase a un lado y la marcha atrás al otro, que es
   el hueco que sobraba a la derecha del título. */
.fase-cabeza {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
/* La vuelta atrás, alineada a la derecha. Solo sale en la fase de la obra. */
.fase-atras {
  display: grid;
  justify-items: end;
}

.fase-detalle .tit {
  font-size: 15px;
  font-weight: 600;
  margin: 2px 0 10px;
  /* El título y su píldora en la misma línea, y la píldora sin heredar el peso
     del título: es un estado, no parte del nombre de la fase. */
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.fase-detalle .tit .est {
  font-weight: 500;
}

/* ============ LO QUE SE PUEDE HACER EN LA FASE ABIERTA ============

   Aquí viven los botones que antes estaban repartidos por las tarjetas de abajo.

   ---------- ANTES IBAN EN COLUMNA; AHORA VAN EN FILA ----------

   Aquí ponía: «en columna y no en fila: cada uno puede traer debajo la frase de
   por qué está apagado, y en fila esa frase separaría los botones de una manera
   que no dice a cuál pertenece». El argumento era bueno y seguiría siéndolo con
   los botones sueltos en una fila; lo que lo resuelve es **envolver cada pareja**
   —el botón y su frase— en una celda propia. La frase ya no flota entre dos
   botones: está en la caja de su botón, debajo, y el filete de al lado dice dónde
   acaba una acción y empieza la siguiente.

   Y así deja de haber una columna de mil píxeles de alto en una tarjeta que mide
   mil quinientos de ancho.

   El filete se dibuja con un pseudo-elemento y no con `border-left`. Las dos
   razones cuentan: la primera es que un `border-left` recorre la celda de arriba
   abajo y este tiene que quedarse corto por los dos extremos —el mismo gesto que
   el filete horizontal de la tarjeta, que tampoco toca los lados—, y eso un borde
   no lo hace. La segunda es la regla 1 de Canto: `border-left` es el tell que se
   rechaza en revisión, y aunque aquí sería gris y no de acento, dejarlo escrito
   en la hoja obliga a que alguien lo compruebe cada vez. */
.fase-acciones {
  display: grid;
  justify-items: stretch;
  align-content: start;
  gap: 10px;
}
/* El rótulo del próximo paso, con peso de título y no de etiqueta: es lo que
   se viene a leer en esta tarjeta (Raúl, 27-9-2026). */
.fase-acciones .fase-proximo {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--tinta);
}
/* La información, a la derecha. El filete que la separa es el mismo gesto que
   el de entre botones —vertical, de 1px, corto por los dos extremos—. */
.fase-info {
  position: relative;
}
.fase-info::before {
  content: '';
  position: absolute;
  left: -12px;
  top: 4px;
  bottom: 4px;
  width: 1px;
  background: var(--hairline);
}
/* Sin columna a la izquierda no hay nada que separar. */
.fase-cuerpo.sola .fase-info::before {
  display: none;
}
.fase-info > .porques:first-child {
  margin-top: 0;
}
/* La nota de que los porqués son calculados: etiqueta, no dato. */
.fase-info > .k {
  font-size: 12px;
  color: var(--subtinta);
}
.fase-info .propuesta {
  margin: 12px 0 0;
}
.fase-info > .propuesta:first-child {
  margin-top: 0;
}
/* Las columnas las cuenta el JS y las escribe en `--cols`, por lo que dice
   `accionesDeFase`: con `auto-fit` la hoja no puede saber qué celda empieza
   fila, y es justo la que no lleva filete. */
.fase-acciones .acciones {
  display: grid;
  grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));
  row-gap: 16px;
}
.fase-acciones .acciones[data-cols='1'] {
  --cols: 1;
}
.fase-acciones .acciones[data-cols='2'] {
  --cols: 2;
}
.fase-acciones .acciones[data-cols='3'] {
  --cols: 3;
}
.fase-acciones .acciones[data-cols='4'] {
  --cols: 4;
}
.fase-acciones .accion {
  position: relative;
  display: grid;
  justify-items: start;
  align-content: start;
  gap: 6px;
  /* El hueco a los lados es lo que separa el botón del filete. Sin él, un botón
     ancho lo tocaría y parecería que el filete es su borde. */
  padding: 0 16px;
}
/* ---------- EL BOTÓN AQUÍ SÍ PARTE EL RÓTULO ----------

   `.btn` lleva `height` fija y el texto en una línea, que es lo correcto en una
   barra o al pie de una hoja: allí el botón manda sobre el sitio que ocupa. En
   una celda de doscientos píxeles manda la celda, y «Volver a agendar una
   medición» se saldría por la derecha —o peor, empujaría la rejilla y las cuatro
   columnas dejarían de medir lo mismo—.

   Así que aquí el alto es un mínimo y no una medida: el botón crece hacia abajo
   lo que necesite el rótulo. El texto pasa a la izquierda porque un rótulo de dos
   líneas centrado se lee como un cartel, no como un botón. */
.fase-acciones .accion .btn {
  max-width: 100%;
  height: auto;
  min-height: var(--toque);
  padding-top: 6px;
  padding-bottom: 6px;
  justify-content: flex-start;
  text-align: left;
  white-space: normal;
  line-height: 1.3;
}
/* El glifo no se encoge cuando el rótulo se parte: `flex` reparte el hueco que
   falta entre todos los hijos, y un SVG de 14px puede quedarse en 6 sin que nada
   avise. Es el mismo `flex: none` que llevan los glifos de las píldoras. */
.fase-acciones .accion .btn .ico {
  flex: none;
}
/* El filete entre celdas: corto por arriba y por abajo, como el horizontal de la
   tarjeta no llega a los lados. */
.fase-acciones .accion::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 1px;
  background: var(--hairline);
}
/* ---------- DOS CAMINOS: EN FILA, NO REPARTIDOS ----------

   La rejilla de columnas iguales reparte el ancho de la tarjeta entre las
   acciones, y eso es correcto cuando son acciones distintas. Con una «o» en
   medio no lo es: manda los dos botones a los extremos y deja la letra flotando
   en medio del blanco, que se lee como dos cosas sin relación en vez de como una
   alternativa. En fila cada botón mide lo que mide su rótulo y quedan pegados,
   con la «o» en el hueco.

   Esta fila **no se parte en una columna a los 1100px** como el resto de la
   rejilla, y por eso todas sus reglas se repiten dentro de esa media query. En
   columna la «o» dejaría de estar entre los dos botones, que es lo único que la
   hace significar algo; la columna llega a los 620px, donde ya no caben en fila
   ni forzándolos, y allí la «o» se pone entre las dos filas. */
.fase-acciones .acciones.juntas {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  column-gap: 0;
}
/* Menos hueco que en la rejilla: aquí los 16px de cada lado sumaban 32 entre dos
   botones que quieren leerse juntos. Con 13 el hueco sigue dando de sobra para
   la «o» y los dos caben en el mismo golpe de vista. */
.fase-acciones .acciones.juntas .accion {
  padding: 0 13px;
  /* El motivo escrito de un botón apagado es una frase, y una frase sin techo
     estira la celda hasta empujar al de al lado fuera de la fila. */
  max-width: 320px;
}

/* ---------- LA «O» ENTRE DOS FORMAS DE HACER LO MISMO ----------

   La celda marcada con `.con-o` no lleva filete: el filete separa acciones
   distintas, y estas dos son la misma cosa por dos caminos. En su lugar va la
   «o», colocada sobre el borde izquierdo de la celda —que es justo donde iba el
   filete— y centrada a la altura del botón, no de la celda entera: con el motivo
   de un freno escrito debajo, el centro de la celda cae por debajo del botón y
   la letra se quedaría flotando entre dos renglones. */
.fase-acciones .accion.con-o::before {
  display: none;
}
.fase-acciones .accion-o {
  position: absolute;
  /* El borde IZQUIERDO de la celda, que con el relleno de `.juntas` cae en medio
     del hueco entre los dos botones. `translateX(-50%)` la centra sobre él. */
  left: 0;
  top: 0;
  /* Alto igual al del botón y contenido centrado dentro: así la letra queda a la
     altura del rótulo y no del centro de la celda, que con un freno escrito
     debajo cae entre dos renglones. */
  height: var(--toque);
  display: grid;
  align-items: center;
  transform: translateX(-50%);
  font-size: 12px;
  color: var(--subtinta);
}
/* La primera de cada fila no separa nada: cuelga del borde. Una regla por número
   de columnas posible, que son cuatro contados —el techo lo pone el JS—. */
.fase-acciones .acciones > .accion:first-child,
.fase-acciones .acciones[data-cols='2'] > .accion:nth-child(2n + 1),
.fase-acciones .acciones[data-cols='3'] > .accion:nth-child(3n + 1),
.fase-acciones .acciones[data-cols='4'] > .accion:nth-child(4n + 1) {
  padding-left: 0;
}
.fase-acciones .acciones > .accion:first-child::before,
.fase-acciones .acciones[data-cols='2'] > .accion:nth-child(2n + 1)::before,
.fase-acciones .acciones[data-cols='3'] > .accion:nth-child(3n + 1)::before,
.fase-acciones .acciones[data-cols='4'] > .accion:nth-child(4n + 1)::before {
  display: none;
}
/* El rechazo de un PDF subido, dentro de la celda de su zona. */
.fase-acciones .freno {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--subtinta);
}
/* La buena noticia de una acción ENCENDIDA: «Ya se han recibido todos los
   pedidos» debajo de «Ir a compras». Verde de estado, con glifo y palabra, que es
   lo que la separa del acento del botón de encima (reglas 2 y 4 de Canto).

   La píldora se escribe en una línea en todos sus demás usos —dentro de una fila
   de tabla, donde la fila manda—, y aquí manda la celda: con tres acciones en una
   ventana de 1.140px la celda mide 223px y la frase 235, así que se salía por
   encima del filete de la de al lado. Aquí envuelve, y entonces el texto cuelga
   del glifo en vez de centrarse con él. */
.fase-acciones .accion .est {
  white-space: normal;
  align-items: flex-start;
  text-align: left;
}
.fase-acciones .accion .est .ico {
  flex: none;
  margin-top: 3px;
}
/* Este sí es un rechazo, no el motivo normal de que una acción aún no toque.
   Por eso usa la caja roja de error: fondo, filete y esquinas redondeadas. */
.fase-acciones .freno.error-importacion {
  padding: 11px 13px;
  border: 1px solid #f0c4c0;
  border-radius: var(--r-sup);
  background: var(--rojo-s);
  color: var(--rojo);
  font-weight: 600;
}
/* Los porqués: una lista de frases calculadas. Sin viñeta redonda de navegador
   —que no hereda el color— sino un guion largo de la casa. */
.porques {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: grid;
  gap: 6px;
}
.porques li {
  position: relative;
  padding-left: 16px;
  font-size: 13px;
  line-height: 1.45;
  color: #3a3b3f;
}
.porques li::before {
  content: '—';
  position: absolute;
  left: 0;
  color: var(--subtinta);
}
/* ============ LA FRASE QUE AVISA DE QUE ALGO VA MAL ============

   Las marca el dominio, no esta hoja (ver `Porque` en `dominio/recorrido.ts`):
   un pedido que lleva 21 días de retraso, una medición que se canceló, un parte
   sin firmar. Lo que hace aquí es que se vean sin leerlas todas.

   Tres señales y no una, que es la regla 4 de Canto y FE-01 a la vez: el rojo,
   el triángulo de aviso en el sitio del guion, y la palabra «Aviso» en
   `.solo-lector` para quien la oye. Con el monitor en gris siguen dos.

   Caja y no solo tinta, porque «resaltado» es lo que se pidió y porque es la
   misma caja roja que ya usa `.freno.error-importacion` ahí arriba: filete de
   1px IGUAL POR LOS CUATRO LADOS —nada de franja lateral, que es la regla 1— y
   el rosa de `--rojo-s` de fondo. Sin sombra y sin degradado.

   `--rojo` sobre `--rojo-s` da **4.81:1**, medido y no mirado (FE-01): pasa el
   AA de texto normal por poco, y por eso el fondo es el rosa de la casa y no uno
   más saturado. El filete no llega a 3:1 y no le hace falta: no delimita un
   control, y lo que dice la frase lo dicen ya el glifo, el color y el texto.

   El rojo es estado, no acción (regla 3): aquí no se pulsa nada. */
.porques li.mal {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 7px 10px;
  border: 1px solid #f0c4c0;
  border-radius: var(--r-ctrl);
  background: var(--rojo-s);
  color: var(--rojo);
  font-weight: 500;
}
/* El guion se va: su sitio lo ocupa el triángulo, y los dos juntos serían dos
   viñetas para la misma frase. */
.porques li.mal::before {
  content: none;
}
.porques li.mal .ico {
  /* `align-items: baseline` alinea el `<svg>` por su borde inferior, que lo deja
     un pelo alto respecto a la primera línea de texto. Este medio píxel es lo
     que lo sienta sobre la línea base de la frase. */
  transform: translateY(1px);
  color: var(--rojo);
}

/* ============ LO QUE PASÓ EN ESTA FASE, DENTRO DE SU TARJETA ============

   La tira que encabeza cada tarjeta de la ficha de una obra desde que las bolas
   del recorrido dejaron de pulsarse (`FASES_PULSABLES` en `proyectos.js`). Lleva
   la fase, si ya pasó y los porqués calculados; las fechas y la duración se
   quitaron el 27-9-2026.

   Va ARRIBA del cuerpo y separada de la tabla por el filete de siempre, no
   metida en una caja propia: una caja dentro de otra caja son dos bordes a 14px
   de distancia, y esto no es una tarjeta aparte — es el encabezado de la que ya
   está. Nada de franja lateral de color (regla 1 de Canto). */
.fase-tira {
  margin: -14px -16px 14px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--panel);
}
.fase-tira-cab {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}
/* Los porqués aquí van más apretados que en el recorrido: son el pie de una
   tira de tres líneas, no el cuerpo de media superficie. */
.fase-tira .porques {
  margin-top: 8px;
  gap: 4px;
}
.fase-tira .porques li {
  font-size: 12px;
}
/* En la tira el aviso va más apretado, como todo lo demás de aquí: es el pie de
   un encabezado de tres líneas, no el cuerpo de media superficie. */
.fase-tira .porques li.mal {
  padding: 5px 8px;
}

/* ============ LAS VECES QUE LA FASE SE CORRIGIÓ A MANO (18-9-2026) ============

   Debajo del detalle de la fase, y solo cuando hay alguna (V081). Es el mismo
   filete de 1px que separa todo lo demás: un bloque que casi nunca sale no
   necesita además una caja que lo destaque — el que sale ya destaca por salir.

   Sin color de estado: una obra rebobinada no está mal, está corregida, y el
   ámbar de esta casa significa que algo pide atención. Lo que hay que leer es el
   motivo, y va en tinta normal. */
.correcciones {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}
.correcciones h3 {
  margin: 0 0 10px;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--subtinta);
}
.correcciones ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.correcciones li {
  font-size: 13px;
  line-height: 1.45;
  color: var(--tinta);
}
.correcciones .cuando {
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
  margin-right: 6px;
}
/* Quién, al final de la línea y en pequeño: la pregunta es qué pasó; quién fue
   es lo segundo que se mira, y casi siempre se sabe ya. */
.correcciones .quien {
  color: var(--subtinta);
  font-size: 11.5px;
  margin-left: 8px;
}
.correcciones .motivo {
  margin: 2px 0 0;
}
.correcciones .arrastro {
  margin: 3px 0 0;
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--subtinta);
}

/* ============ LAS SEIS PESTAÑAS DE LA OBRA (16-9-2026) ============

   La fila que reparte las tarjetas de la ficha por fase. Se lee con las bolas
   del recorrido justo encima, así que **habla su mismo idioma** —el anillo con
   el visto, el de la fase en curso, el número de la que no ha llegado— en vez de
   inventar otra señal para lo mismo.

   ============ COMO LAS DE UN NAVEGADOR (27-9-2026) ============

   Raúl: que se entienda que todo lo de abajo está dentro de la fase pulsada. La
   abierta es blanca, lleva filete arriba y a los lados, y por abajo se abre a su
   panel, que es una sola tarjeta con la tira de la fase y todas sus tarjetas.
   Las demás van sin caja, sobre el fondo, y su filete de abajo es el borde
   superior de ese panel: por eso la fila no lleva hueco entre pestañas y el
   panel no lleva `border-top`. El contorno sale igual por los cuatro lados.

   Sigue sin haber subrayado de color debajo de la pestaña —la franja de la
   regla 1 con otro nombre—: el acento es solo el rótulo de la abierta, como el
   filtro activo de la barra. */
.fases-pest {
  display: flex;
  overflow-x: auto;
  margin-top: 16px;
}
.fase-pest {
  all: unset;
  /* Después del `all: unset`, como en `.col.pulsa` y `.paso`: `all` devuelve
     `box-sizing` a `content-box` y este botón se saltaría el `border-box` de la
     primera regla de la hoja. */
  box-sizing: border-box;
  flex: 1 1 0;
  min-width: 132px;
  min-height: 40px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  /* Solo el filete de abajo, y sin lados transparentes: un lado transparente
     hace inglete con el de abajo y deja una muesca en la raya a cada pestaña. */
  border: 0;
  border-bottom: 1px solid var(--hairline);
  border-radius: var(--r-ctrl) var(--r-ctrl) 0 0;
  color: var(--subtinta);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.fase-pest .anillo {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--blanco);
  border: 1px solid var(--hairline);
  color: var(--subtinta);
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.fase-pest .anillo .ico {
  width: 11px;
  height: 11px;
}
.fase-pest.hecha .anillo {
  background: var(--barra);
  border-color: var(--barra);
  color: var(--blanco);
}
.fase-pest.en_curso .anillo {
  background: var(--canto);
  border-color: var(--canto);
  color: var(--blanco);
}
.fase-pest.hecha,
.fase-pest.en_curso {
  color: var(--tinta);
}
.fase-pest:hover {
  color: var(--tinta);
}
/* Sin filete abajo, y el blanco llega hasta la raya de las demás: eso es lo que
   abre la pestaña a su panel. Los lados bajan hasta esa misma raya y la cierran
   en escuadra. */
.fase-pest.activa {
  border: 1px solid var(--hairline);
  border-bottom: 0;
  background: var(--blanco);
  color: var(--canto-h);
}
.fase-pest:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}

.fase-panel {
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-top: 0;
  border-radius: 0 0 var(--r-sup) var(--r-sup);
  padding: 16px;
  margin-bottom: 16px;
}
.fase-panel > .rejilla {
  margin-bottom: 0;
}
/* El encabezado del panel: la misma tira que hasta el 16-9 iba dentro de la
   primera tarjeta de cada fase. Aquí es la cabeza del panel, en el mismo blanco
   que la pestaña para que esta no se corte en una banda gris, y separada de las
   tarjetas por el filete de siempre. */
.fase-panel-cab {
  margin: -16px -16px 16px;
  border-bottom: 1px solid var(--hairline);
}
.fase-panel-cab .fase-tira {
  margin: 0;
  border-bottom: 0;
  background: none;
}
.fase-panel:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 2px;
}

/* Una columna de gráfico que se puede pulsar. Mismo dibujo que la que no, más
   el cursor y el foco; y la elegida se tiñe con el acento, igual que una fila
   seleccionada. */
.col.pulsa {
  all: unset;
  /* `box-sizing` después del `all: unset`, como en `.th-btn` y `.celda-btn`:
     `all` devuelve TODAS las propiedades a su valor inicial, y el inicial de
     `box-sizing` es `content-box` — o sea que este botón se saltaría el
     `* { box-sizing: border-box }` de la primera regla de la hoja. */
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  gap: 5px;
  cursor: pointer;
  border-radius: var(--r-ctrl);
}
.col.pulsa:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}
.col.pulsa.sel {
  background: var(--canto-s);
}
.col.pulsa.sel .barra-col {
  background: var(--canto);
}
.col.pulsa:hover .barra-col {
  background: var(--canto-h);
}

/* =====================================================================
   PROVEEDORES, INSTALADORES Y LA IMPUTACIÓN
   ===================================================================== */

/* Cifra en rojo dentro de una tabla: los retrasos, los partes sin firmar, las
   entregas tarde. **El color nunca va solo** (regla 4 de DIS-002, FE-01): en
   todos los sitios donde se usa, la propia columna dice qué se cuenta y el
   número está escrito. Aquí el rojo solo hace que salte, y por eso el cero de la
   misma columna va en gris — si todos los ceros fueran negros, no saltaría
   ninguno de los que no lo son. */
.mal-txt {
  color: var(--rojo);
}
/* Y el verde de lo completo, con el mismo criterio: acompaña a «8/8», que ya lo
   dice sin color. */
.ok-txt {
  color: var(--verde);
}

/* El filete que separa los filtros de origen de los de imputación en la barra de
   posventa. Son dos preguntas distintas —quién avisó y de quién fue— y sin la
   separación se leen como cinco botones de lo mismo.

   Es un hueco de 1px, que es como Canto separa: no un borde de color ni una
   sombra (DIS-002, regla 1). */
.sep-filtros {
  width: 1px;
  align-self: stretch;
  min-height: 20px;
  background: var(--hairline);
  margin: 0 2px;
}

/* El formulario de imputación de la columna estrecha: va separado de los datos
   por un filete arriba, porque es lo único de esa ficha que escribe. */
.ficha .imputar {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}
.imputa {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px;
}
.imputa > .pie {
  flex-basis: 100%;
}
#imputar-destino-caja {
  flex: 1 1 160px;
}

/* Los desplegables. Heredan la forma de `.campo-linea input` —fondo de panel,
   hairline por los cuatro lados, esquina de control— para que un `<select>` y un
   `<input>` de la misma barra no parezcan de dos aplicaciones distintas.

   `appearance: none` quita el degradado que Safari le pone al nativo, que es
   justo lo que Canto no admite; la flecha se dibuja con un `background-image`
   embebido y no con una fuente de iconos ni un pseudoelemento, porque un `select`
   no admite `::after`. */
.campo-linea select {
  appearance: none;
  font: inherit;
  font-size: 13px;
  color: var(--tinta);
  background-color: var(--blanco);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--subtinta) 50%),
    linear-gradient(135deg, var(--subtinta) 50%, transparent 50%);
  background-position:
    calc(100% - 15px) calc(50% + 1px),
    calc(100% - 11px) calc(50% + 1px);
  background-size:
    4px 4px,
    4px 4px;
  background-repeat: no-repeat;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  padding: 6px 28px 6px 10px;
  min-height: 30px;
  max-width: 100%;
}
.campo-linea select:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 1px;
}
.campo-linea select:disabled {
  color: var(--subtinta);
  background-color: var(--panel);
}
/* El rótulo encima y no al lado: con el desplegable dentro, la fila de una línea
   se queda sin sitio en cuanto el nombre de un proveedor es largo. */
.imputa .campo-linea {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* Las listas del informe: puntos clave, decisiones, tareas y pasos. */
.lista-informe {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.55;
}
.lista-informe li {
  margin-bottom: 4px;
}

/* El informe en Markdown ya convertido. Se le da tipografía de lectura y no la
   densa de una tabla: es el único sitio del producto donde se lee un texto
   seguido, y la fila de 32px de Canto no es para esto. */
.md {
  font-size: 13.5px;
  line-height: 1.6;
}
.md h3,
.md h4,
.md h5 {
  font-size: 13px;
  font-weight: 600;
  margin: 16px 0 6px;
  color: var(--tinta);
}
.md h3:first-child,
.md h4:first-child {
  margin-top: 0;
}
.md p {
  margin: 0 0 8px;
}

/* La etiqueta de un vínculo, con su aspa para quitarlo. El aspa va DENTRO de la
   píldora y no al lado: son la misma cosa, y separadas se pulsa la que no es. */
.est .quitar {
  all: unset;
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  margin-left: 4px;
  padding: 2px;
  border-radius: 4px;
  opacity: 0.6;
}
.est .quitar:hover {
  opacity: 1;
}
.est .quitar:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 1px;
  opacity: 1;
}
.est .quitar .ico {
  width: 9px;
  height: 9px;
}

/* El desplegable de la transcripción. `summary` con pinta de botón, sin el
   triángulo del navegador, que en Safari y en Chrome no se parecen. */
details > summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
}
details > summary::-webkit-details-marker {
  display: none;
}

/* =========================================================================
   HABLAR CON SENTINEL

   Una conversación, no una tabla, y es la única superficie del producto que no
   es una. Aun así se rige por lo mismo: filete de 1px por los cuatro lados, sin
   sombras, sin degradados, y el acento del material solo en lo accionable.

   Desde el 16-9-2026 es una PANTALLA (`#v-chat`) y no una hoja levantada, así
   que el alto lo pone el lienzo y no hay `dvh` que medir: el hilo crece y la
   página se desplaza, como en cualquier otra pantalla. El compositor se queda
   pegado abajo con `sticky` — es a donde se vuelve después de cada respuesta, y
   buscarlo al final de una conversación larga es el motivo por el que se deja de
   preguntar.

   Los dos lados del hilo se distinguen por POSICIÓN Y RÓTULO, no por color: uno
   se alinea a la derecha con fondo de panel y el otro a la izquierda con
   superficie blanca y filete. Pintar de verde lo que dice el modelo sería usar
   el color del material para decorar, que es justo lo que Canto no hace
   (DIS-002, reglas 2 y 4).
   ========================================================================= */
/* ============ EL CHAT MIDE LA VENTANA, NO EL HILO ============

   Desde el 17-9-2026 (Raúl) el compositor va clavado al fondo, como en cualquier
   chat, y lo que se desplaza es el hilo por detrás. Antes estaba `sticky`, que
   solo pega cuando hay contenido de sobra: con tres mensajes la caja se quedaba
   flotando a media pantalla.

   Para eso el lienzo deja de crecer y mide la ventana **solo mientras la pantalla
   abierta es esta**: `:has()` con la vista visible dentro. Sin esa condición,
   fijar el alto en `.lienzo` recortaría todas las demás pantallas, que sí crecen
   con su contenido. El `padding-bottom` se va con él — el aire de abajo del
   lienzo aquí sería un hueco muerto bajo el compositor. */
.lienzo:has(> #v-chat:not(.oculta)) {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  padding-bottom: 0;
}
#v-chat {
  display: flex;
  flex-direction: column;
  /* `min-height: 0` no es cosmético: sin él, un elemento flexible no encoge por
     debajo de su contenido y el hilo empujaría el compositor fuera de la pantalla
     en vez de desplazarse por dentro. */
  flex: 1;
  min-height: 0;
  /* ---------- UNA COLUMNA, Y LAS TRES PIEZAS DENTRO (Raúl, 17-9-2026) ----------

     El hilo, el compositor y el aviso miden lo mismo y van centrados. Antes cada
     uno ocupaba el lienzo entero: en un monitor ancho, la pregunta quedaba pegada
     al borde derecho y la respuesta al izquierdo, con un metro de blanco entre
     las dos y una caja de escribir de pared a pared. Una conversación se lee en
     una columna, como un texto, y es lo que hace cualquier chat.

     Va como variable y no repetida tres veces porque las tres TIENEN que medir lo
     mismo: en cuanto una se desalinea, el globo deja de acabar donde acaba la
     barra de abajo y se ve al instante. */
  --chat-ancho: 760px;
}
.chat-hilo {
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 0 16px;
  width: 100%;
  max-width: var(--chat-ancho);
  margin-inline: auto;
}
.chat-msg {
  /* Tan ancho como la barra de abajo y no más: el tope es la columna. Lo corto
     encoge a su contenido —`align-self` en una columna flex— y por eso un «hola»
     sigue siendo un globo pequeño. */
  max-width: 100%;
  padding: 12px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
}
.chat-msg.yo {
  align-self: flex-end;
  background: var(--panel);
}
.chat-msg .quien {
  display: block;
  margin-bottom: 5px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--subtinta);
}
.chat-msg .md > :first-child {
  margin-top: 0;
}
.chat-msg .md > :last-child {
  margin-bottom: 0;
}

/* Lo que una respuesta deja pulsable: la obra, el cliente, el pedido que nombra.

   Es el acento y va subrayado, que son las dos señales de un enlace — el color
   solo no lo sería (regla 4). Y es un `<button>` y no un `<a>` porque no navega
   a ninguna URL de fuera: lo resuelve el router por dentro. */
.enlace-vivo {
  padding: 0;
  font: inherit;
  color: var(--canto);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.enlace-vivo:hover {
  color: var(--canto-h);
  text-decoration-thickness: 2px;
}

/* Ya no lleva `position: sticky`: es la penúltima fila de una columna que mide
   la ventana, así que está abajo por la maqueta y no por un truco de
   desplazamiento. El fondo sí se queda —es el del lienzo, `--panel`, no el de
   una superficie: el compositor no es una tarjeta— porque el hilo se desplaza
   por detrás y un mensaje no puede leerse a través de él. */
/* ============ UN DOCUMENTO DENTRO DE UNA RESPUESTA ============

   Petición de Raúl (17-9-2026): cuando Sentinel nombra un documento, que esté el
   documento —para pulsarlo, verlo y guardarlo—, no un enlace más en mitad del
   párrafo.

   Es el MISMO `data-ir` que un enlace vivo y lo abre el mismo oyente: lo único
   distinto es la pinta. Y lo que hay al otro lado tampoco es nuevo: la pantalla
   del papel, con su botón de imprimir, que es por donde se guarda en PDF. Un
   visor propio aquí dentro sería la segunda forma de enseñar lo mismo.

   Canto: filete de 1px por los cuatro lados, sin sombra y sin color decorativo.
   El único acento es «Abrir», que es lo que se pulsa. */
.doc-adjunto {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  margin: 10px 0 2px;
  padding: 10px 12px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
  text-align: left;
}
.doc-adjunto:hover {
  border-color: var(--canto);
}
.doc-adjunto > .ico {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  color: var(--subtinta);
}
/* `adj-` y no `doc-` en las piezas de dentro, y no es manía: `.doc-tit` YA existe
   —es la cabecera del papel impreso, con su filete negro debajo— y está escrita
   más abajo en la hoja, así que ganaba por orden y pintaba una raya en mitad de
   la tarjeta. Se vio en la primera captura. Un nombre de clase es un espacio de
   nombres compartido por las diez mil líneas de este fichero. */
.adj-quien {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}
.adj-tit {
  font-size: 13.5px;
  font-weight: 500;
  /* Un nombre de obra largo no parte la tarjeta en tres renglones. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.adj-sub {
  font-size: 11.5px;
  color: var(--subtinta);
}
.adj-ver {
  flex: 0 0 auto;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--canto);
}
.doc-adjunto:hover .adj-ver {
  color: var(--canto-h);
}

/* ============ LA HOJA QUE ENSEÑA UN FICHERO ============

   Más alta y más ancha que el resto de hojas, porque lo que lleva dentro es una
   hoja A4: con el ancho de una ficha, un albarán sale ilegible y hay que
   descargarlo igualmente, que es justo lo que este visor viene a evitar.

   El cuerpo no tiene relleno propio —lo pone `.hoja-cuerpo`— y el visor ocupa
   todo lo que quede: el papel manda, y el marco es lo de menos. */
.hoja-doc {
  width: min(96vw, 900px);
  max-width: 96vw;
}
.hoja-doc .hoja-cuerpo {
  /* Alto fijo y no `auto`: un `<iframe>` sin alto declarado mide 150px por
     defecto, que es media cabecera de un albarán. */
  height: min(76vh, 820px);
  display: flex;
}
.doc-visor {
  flex: 1;
  width: 100%;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--blanco);
}
/* Una foto se enseña entera y centrada, sin recortar: lo que interesa de la foto
   de una incidencia puede estar en cualquier esquina. */
.doc-visor-img {
  flex: 1;
  min-width: 0;
  object-fit: contain;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--panel);
}
/* Las fotos de una incidencia (`#d-foto`): la foto en medio y una flecha a cada
   lado cuando hay más de una. */
.foto-visor {
  align-items: center;
  gap: 10px;
}
.foto-visor .doc-visor-img {
  align-self: stretch;
}
.foto-paso {
  flex: none;
  width: var(--toque);
  padding: 0;
}
/* ============ LA HOJA QUE ENSEÑA UN PAPEL GENERADO ============

   La de al lado enseña un fichero; esta enseña el documento de la medición, que
   lo escribe la aplicación. Mismo ancho por el mismo motivo —dentro va un A4— y
   el papel centrado: `.doc` mide 860px fijos y sin esto quedaría pegado a la
   izquierda del marco. */
.hoja-papel {
  width: min(96vw, 900px);
  max-width: 96vw;
}
.hoja-papel .doc {
  margin-inline: auto;
}

@media print {
  /* **Esta hoja sí se imprime**, al revés que el resto de `dialog` —ver el bloque
     grande del final—: mientras está levantada ES lo que se está mirando, y era
     la razón por la que este documento tenía que ser una pantalla.

     Se esconde la aplicación de detrás (el lateral y la cabecera ya los esconde
     aquel bloque; el lienzo es el que quedaba) y la hoja deja de comportarse como
     una hoja: sin marco, sin barra y sin alto máximo. Con el alto puesto, el papel
     saldría cortado por donde cortaba el scroll en vez de seguir en la hoja
     siguiente. */
  body:has(#d-papel[open]) .lienzo {
    display: none !important;
  }
  #d-papel[open] {
    display: block !important;
    position: static;
    width: auto;
    max-width: none;
    max-height: none;
    margin: 0;
    border: 0;
    overflow: visible;
  }
  #d-papel[open]::backdrop {
    display: none;
  }
  #d-papel .hoja-barra {
    display: none;
  }
  #d-papel .hoja-cuerpo {
    padding: 0;
  }
}

/* El compositor es UNA caja con la flecha dentro, igual que la barra de la
   portada: el mismo acto se pinta igual en los dos sitios, y así lo que se
   escribe y lo que lo manda se leen como una sola pieza. El campo pierde su
   filete propio —dos marcos, uno dentro de otro, es el marco de más que Canto no
   quiere— y el aro de foco pasa a la caja entera. */
.chat-pie {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 52px;
  padding: 0 8px 0 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
  width: 100%;
  max-width: var(--chat-ancho);
  margin: 0 auto 10px;
}
.chat-pie:focus-within {
  border-color: var(--canto);
}
.chat-pie input {
  flex: 1;
  min-width: 0;
  min-height: var(--toque);
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  /* 16px: por debajo de eso, iOS amplía la pantalla al tocar el campo. */
  font-size: 16px;
}
/* Deshabilitado se ve deshabilitado, y no solo deja de responder: si Sentinel no
   está conectado, el compositor tiene que decirlo de un vistazo (FE-02). */
.chat-pie input:disabled,
.chat-pie button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* El aviso del artículo 50, debajo del compositor y en gris pequeño (Raúl,
   17-9-2026). Va como texto secundario y no como `.nota-aviso` ámbar: no es un
   estado ni algo que haya pasado, es una condición permanente de esta pantalla, y
   un cartel de color repetido en cada visita se deja de leer a la tercera. Sigue
   estando a la vista siempre, que es lo que pide la norma: el compositor no se
   va de la pantalla. */
.chat-nota {
  margin: 0 auto 14px;
  width: 100%;
  max-width: var(--chat-ancho);
  text-align: center;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--subtinta);
}

/* =========================================================================
   LA PORTADA: EL SALUDO Y LA BARRA DE SENTINEL

   Lo que abre la pantalla de inicio desde el 16-9-2026. Centrado y con aire, que
   es lo contrario del resto del producto —filas compactas y números tabulares—
   y es a propósito: aquí no se lee una tabla, se dice qué se quiere.

   La barra se levanta del fondo por TAMAÑO y por SITIO, no por un anillo ni por
   una sombra: 60px de alto, centrada y sola en el primer pantallazo, blanca
   sobre el gris del lienzo y con el filete de 1px de siempre por los cuatro
   lados. Es lo único que Canto permite, y sobra.
   ========================================================================= */
.portada {
  width: min(100%, 720px);
  margin: 0 auto;
  padding: 28px 0 34px;
}
.portada-saludo {
  margin: 0 0 22px;
  text-align: center;
  font-size: clamp(26px, 3vw, 38px);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.03em;
}
.portada-busca {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 60px;
  padding: 0 10px 0 16px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
}
.portada-busca:focus-within {
  border-color: var(--canto);
}
.portada-busca > .ico {
  width: 17px;
  height: 17px;
  flex: 0 0 auto;
  color: var(--subtinta);
}
.portada-busca input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  /* 16px: por debajo de eso, iOS amplía la pantalla al tocar el campo. */
  font-size: 16px;
}
/* El botón de enviar: un cuadrado con la flecha dentro, sin rótulo. Mide 36 y no
   los 32 de `.btn.chico` porque vive dentro de una caja de 60: más pequeño se lee
   como un adorno del campo y no como el acto de la pantalla.

   Apagado es el `.btn[disabled]` de siempre —gris de `--panel`, filete y letra
   `--subtinta`—, así que sin texto no hay acento en la portada. Es lo correcto:
   el único acento de acción se lo lleva lo que se puede pulsar. */
.portada-busca .btn.enviar,
.chat-pie .btn.enviar {
  width: 36px;
  height: 36px;
  padding: 0;
  flex: 0 0 auto;
}
.portada-busca .btn.enviar .ico,
.chat-pie .btn.enviar .ico {
  width: 15px;
  height: 15px;
}

.btn-descartar {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Alto de toque completo aunque la letra sea pequeña: difícil de acertar sin
     querer no puede significar imposible de acertar queriendo (FE-01). */
  min-height: var(--toque);
  padding: 0 12px;
  font-size: 12px;
  color: var(--subtinta);
  background: none;
  border-radius: var(--r-ctrl);
}
.btn-descartar .ico {
  width: 11px;
  height: 11px;
}
.btn-descartar:hover {
  color: var(--rojo);
  background: var(--rojo-s);
}

/* =====================================================================
   MÓVIL Y TABLET  —  y por qué este bloque va AL FINAL de la hoja
   =====================================================================

   Estaba en mitad del fichero, y ahí no funcionaba del todo. Una media query
   **no añade especificidad**: entre dos reglas que empatan gana la que va
   después en la hoja. Y después de este bloque se fueron añadiendo secciones
   —los desplegables de imputación, la conversación— que redefinían exactamente
   los mismos selectores, así que los apagaban sin que se notara: `.campo-linea
   select` (13px) apagaba el paso a 16px de aquí abajo, o sea que «De quién fue»
   seguía ampliando la pantalla al tocarlo en un iPhone, y un campo con dos
   clases le ganaba por especificidad al de una.

   Es la misma trampa que ya se documenta más abajo con `.app.plegado`, y la cura
   estructural es esta: **lo responsive va al final**. Así una sección nueva no
   puede desactivarlo por el orden, que es un fallo que no da ningún síntoma en
   escritorio y solo se ve abriendo la app en un móvil.
   ===================================================================== */

/* ================================================== EL AVISO QUE FLOTA

   Una sola cosa se dice así: que a la cuenta le falta el segundo factor. Es la
   misma pieza que lleva el plano de control, y está por lo mismo — dentro de
   Ajustes solo se enteraría quien ya ha entrado allí.

   Abajo y centrado, y no una franja arriba: una barra en el techo empuja la
   pantalla entera cada vez que aparece. Sin sombra, con el filete de 1px por los
   cuatro lados y el ámbar de estado, con glifo y texto (regla 4). */
.aviso-flotante {
  position: fixed;
  /* Centrado entre los dos bordes y no con `left: 50%`: aquello deja como hueco
     disponible media ventana, y la caja se parte en cuatro renglones mucho antes
     de llegar a su `max-width`. */
  left: 0;
  right: 0;
  margin-inline: auto;
  width: fit-content;
  bottom: calc(18px + env(safe-area-inset-bottom, 0px));
  /* La misma capa que el botón de dictar: por encima de la cabecera fija de las
     tablas y por debajo de cualquier `<dialog>`. */
  z-index: 30;
  /* El hueco de los lados no es aire: es lo que deja libre el botón de dictar,
     que vive en la esquina de abajo a la derecha. */
  max-width: min(560px, calc(100vw - 176px));
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px 9px 14px;
  border-radius: var(--r-sup);
  background: var(--ambar-s);
  border: 1px solid #ebd5ac;
  color: #54350a;
  font-size: 12.5px;
  line-height: 1.4;
}
.aviso-flotante .ico {
  width: 15px;
  height: 15px;
  color: var(--ambar);
  flex: none;
}
.aviso-flotante p {
  margin: 0;
}
/* Dos renglones: qué pasa y por qué importa. */
.aviso-flotante b {
  display: block;
  font-weight: 600;
  font-size: 13px;
}
.aviso-flotante .btn {
  flex: none;
}
/* En Ajustes, fuera: taparía el formulario que el propio aviso manda abrir. */
.app:has(#v-ajustes:not(.oculta)) .aviso-flotante {
  display: none;
}
/* El del informe nuevo (Raúl, 28-9-2026): neutro, porque no falta nada, ha
   llegado algo. Y sale también en Ajustes, que no tiene nada que ver con él. */
.aviso-flotante.informe-nuevo,
.app:has(#v-ajustes:not(.oculta)) .aviso-flotante.informe-nuevo {
  display: flex;
  background: var(--blanco);
  border-color: var(--hairline);
  color: var(--tinta);
}
.aviso-flotante.informe-nuevo .ico {
  color: var(--subtinta);
}
/* Estrecho: el texto no cabe al lado del botón sin partirse en cuatro renglones,
   así que el botón baja y ocupa el ancho — que además es donde llega el pulgar. Y
   el conjunto sube por encima del botón de dictar, que aquí ya no cabe al lado. */
@media (max-width: 560px) {
  .aviso-flotante {
    max-width: calc(100vw - 24px);
    /* Por encima del botón de dictar entero — los 56 del micrófono, los 24 de la
       pestaña de esconder y el hueco que dejan al borde. */
    bottom: calc(112px + env(safe-area-inset-bottom, 0px));
    flex-wrap: wrap;
    gap: 8px 11px;
  }
  .aviso-flotante p {
    flex: 1 1 180px;
    min-width: 0;
  }
  .aviso-flotante .btn {
    flex: 1 0 100%;
  }
}

/* FE-01: el zoom no se desactiva y la retícula cede antes de desbordar. En
   tablet el lateral cerrado pasa a ser una fila y su botón abre una pantalla de
   navegación completa. */
@media (max-width: 860px) {
  /* `.app.plegado` y no `.app` a secas, y es un arreglo. Plegar es un estado que
     se queda puesto —no se guarda entre cargas, pero sí mientras se estrecha la
     ventana— y `.app.plegado { grid-template-columns: 56px … }` le gana por
     especificidad a un `.app` de aquí dentro, esté esta regla donde esté en la
     hoja: la barra se quedaba como carril de 56px con los glifos envueltos en
     dos columnas en vez de pasar a fila de arriba. Aquí abajo plegar no
     significa nada: la barra ya no es un carril que se pueda estrechar. */
  .app,
  .app.plegado {
    /* `block`, y NO una retícula de una columna. Es lo que hace posible que la
       barra se quede pegada arriba al deslizar.

       `position: sticky` solo se mueve dentro de su BLOQUE CONTENEDOR, y el de
       un hijo de retícula es su celda. Aquí la retícula tenía dos FILAS —barra y
       lienzo— así que la celda de la barra medía exactamente lo que la barra:
       cero píxeles por los que desplazarse, y el `sticky` no hacía nada por más
       que se escribiera. En flujo normal el bloque contenedor es el documento y
       la barra ya tiene por dónde correr.

       En escritorio se sigue quedando como retícula, y allí el `sticky` del
       carril funciona desde siempre por lo contrario: el lateral es una COLUMNA,
       su celda mide todo el alto de la retícula y le sobra recorrido.

       De paso desaparece el motivo del `align-content: start` que había aquí: no
       hay filas entre las que repartir el alto sobrante, así que ya no se abre el
       hueco muerto entre la barra y el título. */
    display: block;
  }
  /* ---------------- EL LATERAL DEJA DE SER LATERAL ----------------

     Aquí es una barra de arriba, y por eso pierde el alto de ventana y el
     `sticky` del carril: un carril de 216px en un móvil de 375 se come la mitad
     del ancho.

     Y lo que había antes tampoco valía: la barra enseñaba de golpe la marca, el
     logotipo del cliente, las cinco entradas y el pie, todo en fila, y con
     `flex-wrap` eso son cuatro renglones de menú antes de llegar al contenido.
     En un móvil eso no es una barra de navegación: es la pantalla entera.

     Así que **una sola fila siempre visible** —marca a la izquierda, botón de
     menú a la derecha— y el resto detrás del botón. Al abrir, el menú se
     convierte en la pantalla: oculta el lienzo, empieza arriba y termina en
     «Cerrar sesión». No es una capa encima de contenido que siga activo.

     Y **se queda pegada arriba al deslizar**. Es la única forma de que en un
     móvil se pueda cambiar de sección sin volver antes al principio de una tabla
     de doscientas filas: la puerta de la navegación tiene que estar donde está el
     pulgar, no donde empieza el documento.

     Los dos estados NO se comportan igual, y esa es la idea:

       · **Cerrada** se pega arriba y no se va nunca. Es la que acompaña al dedo.
       · **Abierta** ocupa el documento y se desplaza como una pantalla normal
         hasta su última acción. Solo elegir una sección permite salir.

     El `position: static` de abajo para el menú abierto es obligatorio, no una
     preferencia. `sticky` no significa «pégate cuando salgas»: significa «no te
     salgas del hueco que marca `top`». Como esta barra ARRANCA en el borde de
     arriba del documento, su posición natural ya viola `top: 0` en cuanto se
     desliza un píxel, así que el navegador la clava desde el primer momento. Con
     `static` recupera su comportamiento de pantalla desplazable y deja llegar
     hasta «Cerrar sesión». */
  .lateral {
    position: sticky;
    top: 0;
    /* Y el ancho vuelve a ser el de la fila: aquí la barra es una tira de arriba
       y no un carril. Hay que apagarlo a mano porque arriba es `var(--carril)`,
       que sigue valiendo 216px —o 56 si se dejó plegada— y dejaría la barra a
       media anchura de la pantalla. */
    width: auto;
    /* Por encima de la cabecera fija de las tablas, que también es `sticky` y
       vive en `z-index: 1`. Sin esto, las columnas de una tabla larga se pintan
       por encima de la barra al deslizar. */
    z-index: 20;
    height: auto;
    overflow: visible;
    border-right: 0;
    border-bottom: 1px solid var(--hairline);
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 10px 14px;
  }
  /* NO hace falta descolgar la cabecera de las tablas por debajo de esta barra, y
     conviene saber por qué: `.tabla` lleva `overflow-x: auto`, y en cuanto un eje
     deja de ser `visible` el otro pasa a `auto` también. O sea que `.tabla` es su
     propio contenedor de desplazamiento, y el `position: sticky` de
     `.tabla thead th` se pega al recuadro de `.tabla` —que no se desplaza en
     vertical— en vez de a la ventana. Medido: la cabecera se mueve 1:1 con la
     página en móvil Y en escritorio. **La cabecera fija de las tablas no funciona
     hoy en ninguna parte**, así que no puede chocar con esta barra.
     Es un fallo anterior a esto y no se arregla de tapadillo: darle altura a
     `.tabla` cambia cómo se recorre toda la aplicación y eso se decide, no se
     cuela en un arreglo de móvil. */

  /* Abierto deja de ser una barra y pasa a ser la pantalla. El lienzo y todos
     sus flotantes desaparecen; la propia página desplaza esta columna hasta
     «Cerrar sesión» y termina ahí. */
  .app.menu-abierto .lateral {
    position: static;
    min-height: 100vh;
    min-height: 100dvh;
    flex-direction: column;
    align-items: stretch;
    flex-wrap: nowrap;
  }
  .app.menu-abierto > :not(.lateral) {
    display: none !important;
  }
  /* No se ofrece un falso botón de cierre: esta pantalla termina al elegir una
     sección. El foco ya ha pasado a la primera entrada antes de ocultarlo. */
  .app.menu-abierto .menu-movil {
    display: none;
  }
  .lateral .marca {
    margin: 0;
    padding: 0;
    font-size: 19px;
  }
  /* El botón de menú aparece y se va al extremo derecho de la primera fila. */
  .menu-movil {
    display: inline-flex;
    margin-left: auto;
  }
  .plegar {
    display: none;
  }
  /* Lo que vive dentro del menú: escondido mientras esté cerrado.
     Se esconde **el grupo entero** y no las entradas una a una, y es un arreglo
     de dos fallos a la vez:

       · El RÓTULO se quedaba puesto. Escondiendo solo `.nav-it`, los tres
         `<p class="nav-sec">` seguían pintados, así que la barra cerrada
         enseñaba «ESENCIALES CRM OPERACIONES» flotando al lado de la marca y
         medía 143px en vez de 65. No se veía en el arranque porque
         `.app.plegado .nav-sec` los tapaba de rebote —el móvil abre plegado—,
         pero bastaba con haber pulsado «Plegar» en escritorio y estrechar la
         ventana para que apareciesen.
       · Y los `.nav-grupo` vacíos seguían siendo hijos flex de la barra, así que
         se llevaban su parte del `gap: 6px` sin tener nada dentro. */
  .grupo,
  .nav-grupo,
  .lateral .pie {
    display: none;
  }
  /* El logotipo del cliente no entra ni cerrado ni abierto. Cerrado, la fila
     tiene sitio para dos cosas y las dos están pedidas: la marca del producto y
     la puerta del menú. Abierto, quedaba colgando al lado del botón y estrechaba
     la única fila que siempre se ve. La empresa no se pierde: su razón social
     está en el pie del menú, debajo del nombre de quien ha entrado. */
  .logo-cliente {
    display: none;
  }

  /* ---- Abierto ----
     Cada entrada ocupa su renglón entero: es una lista vertical, que es como se
     lee un menú, y de paso el área táctil pasa a ser toda la fila.

     ================== POR QUÉ `flex-basis` Y NO SOLO `width` ==================

     Esto estaba roto y no un poco: el menú abierto no era una lista, eran tres
     columnas pisándose. `width: 100%` en `.nav-it` se resuelve contra su padre,
     y el padre es el `.nav-grupo`; pero `.nav-grupo` es a su vez un hijo flex de
     una barra en FILA y nadie le había dado base de reparto, así que se quedaba
     al ancho de su contenido y los tres se colocaban uno al lado de otro donde
     iban cabiendo. Medido en un iPhone de 390px: «Esenciales» en x=248 y=10,
     «Operaciones» en x=152 y=208 y «CRM» en x=14 y=277 —solapados en vertical, a
     tres sangrías distintas— y de rebote la marca empujada de y=25 a y=87, o sea
     que abrir el menú movía el logotipo.

     `flex-basis: 100%` es lo que fuerza el renglón entero para el GRUPO. Con eso
     el `width: 100%` de la entrada ya mide la barra completa —362px de los 390—
     y el área táctil es de verdad toda la fila, que es lo que este comentario
     decía y no cumplía. */
  .app.menu-abierto .nav-grupo,
  .app.menu-abierto .lateral .pie {
    display: flex;
    flex: none;
    width: 100%;
  }
  .app.menu-abierto .nav-it {
    display: flex;
    width: 100%;
    min-height: var(--toque);
  }
  /* Abrir el menú se anima: las entradas bajan un poco y aparecen. Al elegir una
     sección se cambia de pantalla directamente; no hay un cierre independiente
     que pueda dejar ver el lienzo anterior durante la transición. */
  .app.menu-abierto .nav-grupo,
  .app.menu-abierto .lateral .pie {
    animation: menu-baja 200ms ease-out;
  }
  /* El rótulo del grupo vuelve. Lo apagaba `.app.plegado .nav-sec` —el móvil
     arranca plegado— y aquí abajo eso no tiene sentido: el motivo de esconderlo
     era que «OPERACIONES» no cabe en un carril de 56px, y este menú tiene el
     ancho de la pantalla. Sin él el cajón son once entradas seguidas, que es
     exactamente el muro que los tres grupos existen para deshacer. */
  .app.menu-abierto .nav-sec {
    display: block;
  }
  /* Y con el rótulo delante, el corte entre grupos vuelve a ser el margen del
     lateral desplegado y no el filete de plegado: la línea sobra y suma ruido.
     Los 16px son los de `.nav-grupo + .nav-grupo`; se restauran, no se inventan. */
  .app.menu-abierto .nav-grupo + .nav-grupo,
  .app.plegado.menu-abierto .nav-grupo + .nav-grupo {
    margin-top: 16px;
    padding-top: 0;
    border-top: 0;
  }
  .app.menu-abierto .lateral .pie {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    margin: auto 0 0;
    padding-top: 10px;
    border-top: 1px solid var(--hairline);
  }
  .app.menu-abierto .lateral .quien {
    display: block;
    padding: 0 8px 6px;
  }
  .lienzo {
    padding: 16px 14px 48px;
  }

  /* ---- Y aquí se desmonta el estado «plegado» ----

     Es una trampa de especificidad: `.app.plegado .nav-it .txt { display: none }`
     le gana a cualquier regla de esta media query, esté donde esté en la hoja.
     Plegar es «estrecha el carril»; sin carril no hay nada que estrechar, y el
     estado se arrastraba hasta aquí dejando la barra con glifos sin texto.
     Plegado no significa nada en móvil: se deshace entero. */
  .app.plegado .lateral {
    padding: 10px 14px;
  }
  .app.plegado .lateral .marca {
    margin: 0;
    padding: 0;
    justify-content: flex-start;
    overflow: visible;
  }
  .app.plegado .lateral .marca .nombre {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
  }
  /* El símbolo vuelve a su tamaño de marca: los 28px son del carril de 56, y al
     lado del nombre se leerían como un logotipo suelto en vez de como el prefijo
     que es. */
  .app.plegado .simbolo-casa {
    width: 1.15em;
    height: 1.15em;
  }
  .app.plegado.menu-abierto .nav-it .txt {
    display: inline;
  }
  .app.plegado.menu-abierto .nav-it .cuenta {
    display: inline-block;
  }
  .app.plegado.menu-abierto .lateral .quien {
    display: block;
  }
  /* Los mismos 8px de `.nav-it` sin plegar: se restauran, no se inventan. */
  .app.plegado .nav-it {
    justify-content: flex-start;
    padding: 0 8px;
  }

  /* ---------------- EL ZOOM QUE SALTA SOLO ----------------

     Safari de iPhone amplía la página **él solo** al tocar un campo cuya letra
     mida menos de 16px, y luego te deja ahí, con la pantalla desencuadrada y a
     medio leer. Es el zoom que molesta, porque no lo pide nadie.

     La cura es la letra, no desactivar el zoom: a 16px Safari no amplía. Y solo
     en móvil, que es donde pasa — en escritorio los campos siguen con la escala
     densa de DIS-002.

     Lo que NO se toca es el zoom de dos dedos: `user-scalable=no` es una barrera
     de accesibilidad (FE-01, crítico) y se queda permitido. Ese hay que pedirlo
     para que ocurra; el de 16px ocurría sin pedirlo.

     ============ LA LISTA ESTABA INCOMPLETA, Y TRES SE ESCAPABAN ============

     Medido campo a campo al ancho de un iPhone, no deducido. Los tres que
     seguían ampliando la pantalla al tocarlos:

       · `.campo textarea` — no tenían NINGUNA regla (ver `.campo textarea`,
         arriba), así que se quedaban en el monospace de 13,33px del navegador.
       · Un campo con DOS clases le ganaba por especificidad al `.campo-linea
         input` de una sola que hay en esta misma lista. Una regla de aquí abajo
         no gana por estar dentro de una media query.
       · `.campo-linea select` — «De quién fue» de la imputación y el tipo del
         historial de informes. Los desplegables no estaban contemplados: la
         lista solo hablaba de `input` y de `textarea`.

     Los `<select>` de `.campo` no entran porque hoy no existe ninguno; el día que
     se pinte uno, entra aquí. */
  .campo input,
  .campo textarea,
  .campo-linea input,
  .campo-linea select,
  .ficha textarea,
  input[type='search'] {
    font-size: 16px;
  }

  /* ---------------- EL ÁREA DE LO QUE SE PULSA ----------------

     FE-01 pide 24×24px como suelo y 44×44 como buena medida. Los filtros se
     quedaban en 29px de alto, que pasa el suelo por poco y se falla con el pulgar
     — y son la PRIMERA decisión de las tres pantallas de lista.

     Suben a 36px y no a 44 a propósito, y desde el 9-9-2026 es un tamaño propio
     de aquí y no el de `.filtro.grande` prestado: en escritorio ese filtro bajó a
     30px al hacer sitio a la tabla, y el suelo de un pulgar no baja con él. A 44
     la barra de posventa —nueve filtros que envuelven— se comía tres renglones
     de pantalla, y la densidad es la regla 5 de Canto: lo que no cede es la
     TABLA.

     La letra no cambia. Lo que crece es el área. */
  .filtro,
  .filtro.grande {
    min-height: 36px;
  }

  .portada {
    padding: 14px 0 24px;
  }
  /* El rótulo del botón sobra en 390px de ancho: el glifo del bocadillo con la
     chispa ya dice qué hace, y con la palabra la caja de escribir se queda en
     media pantalla. Lo que oye un lector de pantalla no cambia — el `<label>`
     del campo sigue diciendo a quién se le pregunta (FE-01). */
  .portada-busca .btn .txt-largo {
    display: none;
  }
}
@media (max-width: 620px) {
  html,
  body {
    max-width: 100%;
    overflow-x: clip;
  }
  .ocultamv {
    display: none;
  }
  .lienzo,
  .lienzo > main,
  .sup,
  .sup > .cuerpo {
    min-width: 0;
  }
  .lienzo {
    overflow-x: clip;
  }
  .cabecera h1 {
    font-size: 22px;
  }
  .cabecera {
    gap: 10px 12px;
    margin-bottom: 16px;
    padding-bottom: 12px;
  }
  .cabecera .centro > .acciones-de > div {
    min-width: 0;
    flex: 1 1 100%;
    flex-wrap: wrap;
  }
  .cabecera .centro .btn {
    min-width: 0;
    max-width: 100%;
    padding-inline: 12px;
    white-space: normal;
    line-height: 1.2;
  }

  /* La portada pasa a una tarjeta por fila en la regla final de `.cifras`, más
     abajo. Aquí se blinda lo que vive dentro: una cifra o un pie excepcionalmente
     largos se resumen con elipsis en vez de atravesar el borde. */
  .cifra {
    min-width: 0;
    overflow: hidden;
    padding: 12px;
  }
  .cifra .lab,
  .cifra .val,
  .cifra .pie {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cifra .val,
  .tend {
    font-size: clamp(19px, 5.4vw, 22px);
  }
  .tend {
    max-width: 100%;
    overflow: hidden;
  }
  .panel-dos,
  .rejilla {
    gap: 12px;
  }
  .panel-dia .dia-cab {
    min-width: 0;
    padding: 10px 12px 0;
  }
  .panel-dia .dia-cab .val {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .panel-dia > .cuerpo > .acciones {
    padding: 0 12px 12px;
  }
  /* La hoja se queda con casi toda la pantalla. Los 48px de hueco son de una
     ventana de escritorio; en un móvil de 390 dejaban la ficha en 342px y, con el
     relleno del cuerpo, 310px para leer una incidencia. Con 16 quedan 374. Sigue
     habiendo hueco —se ve que hay algo detrás, que es lo que dice que esto es una
     hoja y no una pantalla— pero medido para el sitio que hay. */
  /* `width: auto` y el hueco puesto con `margin`, NO `calc(100vw - 16px)`: `vw`
     cuenta la barra de desplazamiento del navegador, así que en una ventana de
     escritorio estrecha —por debajo de 620px, que es donde entra esta regla— la
     hoja se pasaba de ancho justo lo que mide la barra y se salía por la derecha.
     Un `<dialog>` modal lleva `inset: 0` del navegador, y con `width: auto` eso
     significa «llena lo que haya menos los márgenes»: la medida la hace el
     motor y sale bien con barra y sin ella. */
  .hoja {
    width: auto;
    max-height: calc(100vh - 16px);
    max-height: calc(100dvh - 16px);
    margin: 8px;
  }
  .hoja-cuerpo {
    padding: 14px;
  }
  .hoja > .nota-aviso {
    margin: 14px 14px 0;
  }
}

/* =========================================================================
   EL CALENDARIO DE INSTALACIONES

   El reparto es el de Google Calendar y el aspecto es Canto entero. Las cinco
   reglas, y dónde se cumple cada una en este bloque:

     1. **Ni una franja de color en un lateral.** La rejilla se dibuja con
        `border-collapse: collapse` y `border: 1px solid var(--hairline)` en
        cada celda: el mismo filete por los cuatro lados, que es lo que hace la
        cuadrícula. Ni un `border-left` de color en todo el bloque — la
        comprobación de revisión de DIS-002 sigue dando cero.
     2. **El material es el acento y solo donde hay algo vivo o pulsable:** el
        día de hoy, la obra confirmada o en curso, el «+2 más», la vista
        seleccionada y el foco. Un día cualquiera es blanco con su filete.
     3. **Un solo acento.** Ni un azul. El verde de `completada` y el gris de
        los días cerrados hablan de estado, no de acción.
     4. **El color nunca es la única señal:** cada obra lleva su glifo y el
        nombre de su estado —visible en semana y día, en `.solo-lector` en el
        mes, donde la casilla no da para más— y los fines de semana los nombra
        la propia cabecera de la columna.
     5. **Densidad primero:** la obra ocupa tres renglones de 11,5px, las cifras
        van tabulares y el recuento está en la barra.

   Prohibido y ausente aquí también: sombras, degradados, zebra, un segundo
   acento y acciones que solo aparecen al pasar el ratón.
   ========================================================================= */

/* --- la barra de arriba: hoy, flechas, el rango escrito, y las vistas --- */

/* El hueco elástico es lo que manda la navegación a la izquierda y los
   selectores a la derecha, como en cualquier calendario. Es un `<span>` vacío y
   no un `margin-left: auto` sobre el grupo siguiente porque la barra envuelve en
   pantallas estrechas, y un margen automático sobre una línea nueva empuja el
   grupo al centro de la nada. */
.cal-hueco {
  flex: 1 1 0;
}
.cal-rango {
  font-size: 15px;
  font-weight: 600;
  margin: 0;
  /* Los números del rango van tabulares: pasando meses con la flecha, «30 de
     noviembre» y «31 de octubre» tienen que ocupar lo mismo o el título baila. */
  font-variant-numeric: tabular-nums;
}
.cal-flechas,
.cal-vistas {
  display: inline-flex;
  gap: 6px;
}
/* Las flechas son glifo solo —su nombre lo pone el `aria-label`— así que el
   relleno se reparte parejo en vez de dejar el hueco del texto que no hay. */
.cal-flecha {
  padding: 5px 9px;
}
.cal-flecha .ico {
  width: 13px;
  height: 13px;
}

/* --- el renglón de aviso: el periodo vacío y el día no laborable --- */
.cal-nota {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  padding: 10px 16px;
  font-size: 12.5px;
  color: var(--subtinta);
  border-bottom: 1px solid var(--hairline);
}
.cal-nota .ico {
  width: 14px;
  height: 14px;
  flex: none;
}

/* --- la rejilla ---

   `table-layout: fixed` y no automático: sin él, la columna del día que tenga la
   razón social más larga se queda con el doble de ancho que las demás y la
   semana deja de leerse como una semana. Con reparto fijo las siete columnas
   miden lo mismo y el texto que no cabe se recorta, que es lo correcto en una
   rejilla de calendario.

   `min-width` en la tabla y `overflow-x` en el contenedor: por debajo de eso las
   casillas dejarían de caber y siete columnas de 40px no son un calendario. La
   rejilla se desplaza dentro de su caja; la página NO se va de ancho. */
/* ============ `position: relative` NO ES DECORATIVO, ARREGLA UN DESBORDE ============

   Medido en Chrome a 390px de ancho: sin esta línea, la vista de MES hacía que
   la página entera se pudiera desplazar de lado —`documentElement.scrollWidth`
   586 contra 375 de ventana— mientras que las de semana y día no. El `.cal` sí
   recortaba: su `scrollWidth` era 660 y su `clientWidth` 345.

   Lo que se escapaba eran los `.solo-lector`. Esa clase es `position: absolute`,
   y un elemento absoluto NO lo recorta un antepasado con `overflow` si ese
   antepasado no es su bloque contenedor — es decir, si está `static`. Se iban a
   buscar el bloque contenedor mucho más arriba y arrastraban con ellos la
   anchura de la tabla al área desplazable del documento.

   Y solo pasaba en el mes porque solo ahí hay muchos: la obra compacta lleva su
   estado y su equipo en `.solo-lector` (la casilla de 90px no da para
   enseñarlos), o sea dos por obra y treinta en la rejilla. En semana y día esos
   textos se ven, y el único absoluto es el `<caption>`, que se queda pegado al
   borde izquierdo y no desborda nada.

   Con `relative`, `.cal` pasa a ser el bloque contenedor de sus absolutos y el
   `overflow-x` los recorta como al resto. La rejilla se sigue desplazando dentro
   de su caja; la página, no. */
.cal {
  position: relative;
  overflow-x: auto;
  padding: 12px 16px 16px;
}
.cal table {
  width: 100%;
  min-width: 760px;
  border-collapse: collapse;
  table-layout: fixed;
  font-variant-numeric: tabular-nums;
}
.cal th,
.cal td {
  border: 1px solid var(--hairline);
  vertical-align: top;
  padding: 4px;
}
.cal thead th {
  background: var(--panel);
  text-align: left;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--subtinta);
  padding: 6px 8px;
  vertical-align: middle;
}
/* La abreviatura del día de la semana con su nombre entero detrás. Sin
   subrayado punteado: el navegador lo pinta por su cuenta y en una cabecera de
   11,5px se lee como una raya sucia. */
.cal thead abbr {
  text-decoration: none;
}
/* La esquina de arriba a la izquierda, donde se cruzan las franjas con los días.
   No es una cabecera de nada —por eso es `<td>` y no `<th>`— y se queda del
   color del panel para que la fila de arriba se lea entera. */
.cal-esquina {
  background: var(--panel);
  width: 104px;
}
/* La columna de las franjas. Es `<th scope="row">`: un lector de pantalla que
   entra en una casilla anuncia «Martes 15, Por la mañana», que es exactamente lo que
   hace falta para saber dónde se está sin ver la rejilla (FE-01). */
.cal-franja {
  background: var(--panel);
  width: 104px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--subtinta);
  text-align: left;
  padding: 6px 8px;
  vertical-align: middle;
}
.cal-baja {
  font-weight: 400;
  color: var(--subtinta);
}

/* --- los días que no se trabaja ---

   Gris de panel, el mismo que ya usan las cabeceras. NO es la única señal: el
   fin de semana lo dice la cabecera de su columna y el festivo lleva su motivo
   escrito debajo del número (regla 4). */
.cal td.cerrado {
  background: var(--panel);
}
/* Los días del mes de al lado. Se pintan más flojos en vez de dejarse en blanco:
   una obra del 1 de octubre que cae en la rejilla de septiembre tiene que verse
   —para eso se piden esos días— pero sin competir con el mes que se está
   mirando. */
.cal td.fuera .cal-num {
  color: var(--subtinta);
  font-weight: 400;
}
.cal-cerrado {
  display: block;
  font-size: 10.5px;
  color: var(--subtinta);
  padding: 0 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- el número del día. Es un botón: lleva a la vista de ese día --- */
.cal-cab-dia {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}
/* 24×24 medidos, que es el suelo de área de pulsación de FE-01 y no una cifra
   redondeada a ojo: con `padding: 2px` se quedaba en 22,75px de alto y fallaba
   por milímetro y medio. No sube a 44 porque hay 42 de estos en la rejilla de un
   mes y a esa altura la casilla del día se queda sin sitio para las obras, que
   es lo que se viene a mirar. */
.cal-num {
  font-size: 12.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  border-radius: var(--r-pill);
  padding: 3px 7px;
  min-width: 24px;
  min-height: 24px;
  text-align: center;
  flex: none;
}
.cal-num:hover {
  background: var(--panel);
}
/* HOY, en el acento del material: es «lo vivo» del calendario, que es
   literalmente para lo que la regla 2 reserva el color. Blanco sobre #0E6E62
   pasa AA de sobra. */
.cal td.hoy > .cal-cab-dia > .cal-num,
.cal th.hoy .cal-num {
  background: var(--canto);
  color: var(--blanco);
}
.cal td.hoy > .cal-cab-dia > .cal-num:hover,
/* Con su `color`, por lo mismo que `.btn.pri`: fondo oscuro y letra blanca van
   en la misma regla. */
.cal th.hoy .cal-num:hover {
  background: var(--canto-h);
  color: var(--blanco);
}
/* En la cabecera de la semana el botón lleva las dos cosas —«Mar» y «15»— y se
   pulsa entero, así que se comporta como una sola pieza. */
.cal-semana thead .cal-num {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  text-align: left;
}
.cal-semana thead .cal-num .n {
  font-size: 14px;
}

/* --- la pila de obras de una casilla --- */
.cal-pila {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.cal-mes td {
  height: 104px;
}
/* La casilla del mes se pulsa entera y por eso lo parece. El cambio de fondo es
   el mismo que el del número: pulsar el papel y pulsar el «17» llevan al mismo
   sitio, así que se marcan igual. */
.cal-mes td.cal-dia-pulsable {
  cursor: pointer;
}
.cal-mes td.cal-dia-pulsable:hover {
  background: var(--panel);
}
.cal-semana td,
.cal-dia td {
  height: 76px;
}

/* --- una obra ---

   Filete de 1px por los cuatro lados y fondo entero cuando hay algo que decir.
   La tentación de la plantilla —una franja de color a la izquierda para el
   estado— es exactamente lo que la regla 1 prohíbe, y aquí se dice con el fondo
   y con el glifo.

   El selector lleva también `.cal-visita`, la pieza de una visita a medir: es la
   misma caja en la misma rejilla, y separarlas en dos bloques idénticos acaba
   con dos `padding` distintos en dos meses. Lo que cambia de una a otra —el
   glifo, la palabra y el color del estado— está abajo, en su propia sección. */
.cal-obra,
.cal-visita {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--blanco);
  padding: 4px 6px;
  font-size: 11.5px;
  line-height: 1.35;
  min-width: 0;
}
.cal-obra:hover,
.cal-visita:hover {
  background: var(--panel);
}
/* Envuelve en vez de recortar. En la vista de semana, a 1280px, siete de las
   once obras dejaban el estado en «Confirm…»: el renglón no da para el código
   entero más la palabra, y una etiqueta cortada por la mitad se lee como un
   fallo de maqueta. Envolviendo, el estado baja a su propia línea cuando no
   cabe y se lee entero; donde sí cabe —la vista de día, con columnas anchas— se
   queda arriba a la derecha, que es donde estaba. */
.cal-obra-cab {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 4px;
  min-width: 0;
}
.cal-obra .ico,
.cal-visita .ico {
  width: 11px;
  height: 11px;
  flex: none;
}
/* `flex: none` en el CÓDIGO y el recorte en el estado, y no al revés.

   Medido en la vista de semana a 1280px: con la obra encogiendo por el código,
   una columna de 146px enseñaba «2026-0…» al lado de «Confirmada» entera. El
   código es lo que identifica la obra —es como se la nombra por teléfono— y
   nunca puede salir a medias; el estado, en cambio, ya lo dicen el glifo de al
   lado y el fondo de la pieza, así que es lo que puede ceder sitio. */
.cal-obra-cod {
  flex: none;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cal-obra-est {
  margin-left: auto;
  min-width: 0;
  font-size: 10.5px;
  color: var(--subtinta);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cal-obra-cli,
.cal-obra-pie,
.cal-visita-quien {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-obra-pie,
.cal-visita-quien {
  font-size: 11px;
  color: var(--subtinta);
}
/* Confirmada y en curso: el acento del material. Es la obra que está viva, que
   es el otro sitio donde la regla 2 deja poner color. */
.cal-obra.curso {
  background: var(--canto-s);
  border-color: var(--canto);
}
.cal-obra.curso:hover {
  background: #d5e8e4;
}
.cal-obra.curso .ico,
.cal-obra.curso .cal-obra-cod {
  color: var(--canto-h);
}
/* Completada: gris de panel, no verde de fondo. Lo que está hecho pesa menos en
   una rejilla que existe para mirar hacia delante; el verde se queda en el
   glifo, que es donde habla de estado y no de acción (reglas 2 y 3). */
.cal-obra.ok {
  background: var(--panel);
  color: var(--subtinta);
}
.cal-obra.ok:hover {
  background: #ebebef;
}
.cal-obra.ok .ico {
  color: var(--verde);
}
/* «Ya se ha instalado» y «Ya se ha medido», con el verde del tick y no en gris
   (Raúl, 28-9-2026): lo hecho tiene que leerse de un vistazo. Es color de estado
   con su glifo y su texto, no de adorno. */
.cal-obra.ok .cal-obra-est,
.cal-visita.ok .cal-obra-est {
  color: var(--verde);
  font-weight: 600;
}

/* --- una visita a medir ---

   Misma pieza que una obra: mismo filete de 1px por los cuatro lados, mismo
   fondo entero, mismos renglones. Se hereda de `.cal-obra` con una lista de
   selectores y no con una clase suelta porque son la misma cosa en la misma
   rejilla, y el día que cambie el padding de una tiene que cambiar el de la
   otra — dos bloques separados se separan del todo en dos meses.

   Lo que la distingue son las tres señales de `calVisita`, y ninguna es un
   borde de color: el glifo de la cota, la palabra («Por medir», «Midiendo»,
   «Ya se ha medido») y el ámbar del estado de espera. **Ni un color nuevo entra aquí**:
   `--ambar` ya está en el sistema y ya significa esto mismo en la píldora de la
   ficha de la obra (DIS-002, regla 3).

   La caja, el filete, el `padding` y el tamaño de letra están arriba, en el
   selector que comparte con `.cal-obra`. Aquí abajo solo va lo que cambia. */

/* Agendada: ámbar. Es una espera con día puesto —alguien tiene que ir— y es lo
   mismo que dice el ámbar en el resto de la aplicación. Al lado de una obra
   confirmada en verde, la casilla se lee de un vistazo sin leer la palabra;
   pero la palabra está, que es lo que pide la regla 4. */
.cal-visita.aviso {
  background: var(--ambar-s);
  border-color: var(--ambar);
}
.cal-visita.aviso:hover {
  background: #fae9cd;
}
.cal-visita.aviso .ico,
.cal-visita.aviso .cal-obra-cod {
  color: var(--ambar);
}
/* Midiendo: el acento del material, igual que una obra en curso. Es lo vivo del
   día, que es el otro sitio donde la regla 2 deja poner color. */
.cal-visita.curso {
  background: var(--canto-s);
  border-color: var(--canto);
}
.cal-visita.curso:hover {
  background: #d5e8e4;
}
.cal-visita.curso .ico,
.cal-visita.curso .cal-obra-cod {
  color: var(--canto-h);
}
/* Medida: gris de panel, como la obra completada y por lo mismo — lo que está
   hecho pesa menos en una rejilla que mira hacia delante. El verde se queda en
   el glifo, donde habla de estado y no de acción. */
.cal-visita.ok {
  background: var(--panel);
  color: var(--subtinta);
}
.cal-visita.ok:hover {
  background: #ebebef;
}
.cal-visita.ok .ico {
  color: var(--verde);
}

/* La columna de visitas de la vista de día. El filete a la izquierda separa dos
   GRUPOS de columnas, que es para lo que existe un borde de tabla; no es una
   franja de color en el lateral de una superficie, que es lo que prohíbe la
   regla 1 — es `--hairline`, el mismo gris que el resto de la rejilla. */
.cal-dia .cal-col-medir {
  border-left: 1px solid var(--hairline);
}
.cal-dia th.cal-col-medir {
  color: var(--subtinta);
  font-weight: 600;
}

/* El «+2 más». Es accionable —lleva a la vista de día— así que va en el acento,
   y es texto de verdad, no un icono ni tres puntos suspensivos. */
.cal-mas {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 600;
  color: var(--canto-h);
  border-radius: var(--r-ctrl);
  padding: 2px 6px;
}
.cal-mas:hover {
  background: var(--canto-s);
}

/* ================================ LA FRANJA LIBRE ================================

   La casilla que el motor da por libre y en la que cabe alguien. Lleva el
   acento porque es de las dos cosas que lo llevan: se puede pulsar, y ha
   cambiado (DIS-002 regla 2). El filete a trazos es lo que la distingue de una
   casilla vacía sin depender del color, que es la regla 4 — y va por los CUATRO
   lados, no como franja lateral.                                            */

.cal-libre {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px dashed var(--canto);
  border-radius: var(--r-ctrl);
  background: transparent;
  color: var(--canto);
  padding: 4px 6px;
  font-size: 11.5px;
  line-height: 1.35;
  min-width: 0;
}
.cal-libre:hover:not(:disabled) {
  background: var(--canto-s);
}
/* Sin candidatos deja de ser una acción: se apaga el acento y se queda como el
   dato que es. Sigue viéndose, que un jueves libre y sin nada que meter es
   justo lo que se viene a mirar aquí. */
.cal-libre:disabled {
  border-color: var(--hairline);
  color: var(--subtinta);
  cursor: default;
}
.cal-libre-cab {
  display: flex;
  align-items: center;
  gap: 4px;
  font-weight: 600;
}
.cal-libre .ico {
  width: 11px;
  height: 11px;
  flex: none;
}
.cal-libre-n {
  display: block;
  font-size: 10.5px;
  color: var(--subtinta);
}

/* ============ LA MARCA DE AGENDA POR LLENAR (SEMANA Y MES) ============

   La hermana pequeña de `.cal-libre`, y deliberadamente pequeña: en esas dos
   vistas comparte casilla con las obras colocadas, y lo colocado gana. Ver
   `calHuecosSutiles` en `calendario.js` para el porqué entero.

   Nada de acento: esto no llena el hueco, lleva a la vista de día que sí lo
   llena, así que el verde del material —que Canto reserva para lo accionable—
   se queda en el botón de allí. Y nada de fondo ni de filete propio: en una
   casilla de mes de 90px, una caja más es una caja de más. */
.cal-libre-sutil {
  all: unset;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  margin-top: 3px;
  padding: 1px 2px;
  border-radius: var(--r-ctrl);
  cursor: pointer;
  font-size: 11px;
  line-height: 1.3;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
}
.cal-libre-sutil .ico {
  width: 10px;
  height: 10px;
  flex: none;
}
/* Al pasar por encima sí se declara pulsable, y ahí es donde aparece el acento:
   es el momento en el que deja de ser un dato de fondo y pasa a ser una puerta. */
.cal-libre-sutil:hover {
  color: var(--canto);
  background: var(--canto-s);
}
.cal-libre-sutil:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}

/* ======================= QUIÉN ENTRA EN LA FRANJA LIBRE =======================

   El diálogo. Estrecho a propósito: aquí no se compara nada en paralelo, se lee
   una opción y se decide. La hoja ancha de las incidencias existe porque allí
   hay fotos y una descripción larga; esto es un nombre, un motivo y un botón. */

.hoja.estrecha {
  width: min(560px, calc(100vw - 48px));
}

/* ---------- AGENDAR LA INSTALACIÓN, LA OTRA EXCEPCIÓN ----------

   Los 560px se quedaron cortos el día que la hoja dejó de ser una lista de tres
   filas: ahora lleva un MES entero de siete columnas y, debajo, los equipos que
   pueden ir con la gente de cada uno. A 560px la casilla de un día medía 70px y
   no cabía en ella una palabra (petición de Raúl, 10 sep. 2026).

   880 y no los 1180 de `.hoja` a secas: lo que fija el ancho es que «Disponible»
   quepa en una séptima parte, y estirar más solo separaría los días entre sí.

   Y desde el 21 sep. 2026 son las tres hojas que llevan un mes dentro, no una:
   agendar la medición y apuntar la entrega del proveedor usan la misma rejilla,
   así que a 560px tenían el mismo día de 70px que aquí se arregló. Un calendario
   que se lee distinto según desde dónde se abra es el mismo calendario dos
   veces. */
#d-instalacion.hoja.estrecha,
#d-medicion.hoja.estrecha,
#d-entrega.hoja.estrecha {
  width: min(880px, calc(100vw - 48px));
}
/* Y en móvil, lo que hace `.hoja` para todas: la pantalla entera menos el
   margen. Va AQUÍ y no con las demás reglas de móvil de la línea 5378 porque
   una media query no añade especificidad: esta regla es de tres partes, la de
   allí también, y la que gana es la que se escribe después. Escrita arriba, la
   hoja de agendar sería la única que en un móvil se queda a 342px teniendo 374
   disponibles — y lo peor, sin que nada falle a la vista. */
@media (max-width: 620px) {
  #d-instalacion.hoja.estrecha,
  #d-medicion.hoja.estrecha,
  #d-entrega.hoja.estrecha {
    width: auto;
  }
}
.hueco-opcion {
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  padding: 12px 14px;
  margin-bottom: 12px;
}
.hueco-pos {
  font-size: 11px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--subtinta);
  margin: 0 0 6px;
}
.hueco-obra {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0 0 8px;
}
.hueco-obra b {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.hueco-obra span {
  color: var(--subtinta);
}
.hueco-motivo {
  font-size: 12.5px;
  color: var(--subtinta);
  line-height: 1.45;
  margin: 9px 0 0;
}

.hueco-canal {
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  padding: 12px 14px;
  margin-bottom: 12px;
}
.hueco-canal .seg {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
}
.hueco-canal .sub {
  font-size: 11.5px;
  color: var(--subtinta);
  margin: 0 0 7px;
}
.hueco-tel {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 2px 0 0;
}
.hueco-tel a {
  color: var(--tinta);
  text-decoration: none;
}
.hueco-tel a:hover {
  text-decoration: underline;
}
/* Falta el dato, y se dice cuál falta y qué hacer en vez de dejar el sitio en
   blanco (FE-02). No es un fallo: hay fichas de cliente sin teléfono. */
.hueco-nada {
  font-size: 12.5px;
  color: var(--subtinta);
  margin: 0;
}
/* El borrador se escribe, no solo se lee: es un `<textarea>`, y de ahí el
   `resize: vertical` y el fondo blanco. El de la aplicación es un punto de
   partida —la fecha, la obra y el porqué ya puestos—, no un texto cerrado: quien
   llama sabe cosas del cliente que el motor no sabe.

   Sigue en monoespaciada y con el mismo cuerpo que tenía de `<pre>`: es el texto
   que va a salir por correo y se lee mejor sabiendo dónde corta cada renglón.

   El selector lleva el `textarea` delante y no es de adorno: `.campo textarea`
   —que está arriba— tiene una clase y un elemento, así que le gana a `.hueco-
   borrador` a secas y su `font: inherit` se llevaría por delante la
   monoespaciada. Con el elemento delante empatan y gana esta, que va después.
   El cuerpo de 16px en móvil está al final de la hoja, con el resto. */
textarea.hueco-borrador {
  display: block;
  width: 100%;
  font-family: var(--mono);
  font-size: 11.5px;
  line-height: 1.5;
  white-space: pre-wrap;
  color: var(--tinta);
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  padding: 10px 12px;
  margin: 0;
  resize: vertical;
  min-height: 120px;
  max-height: 320px;
}
textarea.hueco-borrador:focus-visible {
  border-color: var(--canto);
}
.hueco-mensaje {
  margin-bottom: 0;
}
.hueco-pie-canal {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin: 9px 0 0;
}
.hueco-pie {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}

/* ---------------- LA HOJA DE AGENDAR LA INSTALACIÓN ----------------

   Tres piezas sobre la tabla de opciones: cuándo es lo antes que se puede
   montar, si se reserva o se fija, y el pie con el botón. La tabla es la de
   siempre —`tabla()` con `tr.pulsa` y `tr.act`— y por eso aquí no hay ni una
   regla de filas: la fila elegida se tiñe con el acento que ya llevan todas las
   tablas de la aplicación (DIS-002 regla 2), sin barra lateral y sin invento. */
.inst-ventana {
  margin: 0 0 10px;
  font-size: 12.5px;
  color: var(--subtinta);
}
.inst-ventana strong {
  color: var(--tinta);
  font-weight: 600;
}
/* La hora son cinco cifras: la caja se ciñe a ellas, y la frase de Sentinel va a
   su lado, separada, en vez de pegada al borde del campo. */
.inst-hora {
  margin-top: 12px;
}
.inst-hora input {
  width: auto;
}
.inst-hora .sub {
  margin-left: 12px;
  font-size: 12.5px;
  color: var(--subtinta);
}

/* ---------- LOS DÍAS MARCADOS, Y EL BOTÓN QUE LOS CIERRA ----------

   Un montaje puede durar varios días (V090), así que entre el calendario y el
   resto de la hoja hay ahora dos cosas: lo que se lleva marcado y el botón que lo
   da por bueno. Van juntas y pegadas al mes, que es lo que han de confirmar.

   Nada de esto lleva color propio: el recuento es texto y el botón es el `pri` de
   siempre. El único acento de la hoja sigue siendo uno (regla 1 de Canto), y aquí
   lo tiene el día marcado en la rejilla. */
.inst-marcado {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin: 12px 0 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--subtinta);
}
.inst-marcado b {
  color: var(--tinta);
  font-weight: 600;
}
.inst-marcado .ico {
  width: 13px;
  height: 13px;
  margin-top: 2px;
  flex: none;
}
/* El botón ocupa su propia línea y el motivo cae debajo de él, no arriba del
   todo: el resultado se lee donde estaba mirando quien lo pulsó. */
.inst-confirmar {
  margin-top: 12px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--hairline);
}
.inst-confirmar .campo-mal {
  margin: 8px 0 0;
}
/* Lo que se abre al confirmar. El margen lo separa del filete de arriba, que es
   lo que dice que son dos preguntas y no una lista larga. */
.inst-segunda {
  margin-top: 14px;
}
/* Arrastrando por encima de los días, el navegador iba pintando de azul los
   números por los que pasaba el dedo. Aquí no hay texto que copiar: lo que hay
   son botones, y lo que se hace con ellos es marcarlos.

   `touch-action: none` es lo que impide que el navegador se quede el gesto para
   desplazar la página en cuanto el dedo se mueve dos píxeles — sin él, en un
   móvil el arrastre no llega nunca a la casilla siguiente. Va solo en las
   casillas, así que la hoja se sigue deslizando con el dedo en cualquier otro
   sitio. */
.agenda-rejilla .mes-dia {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}

/* La casilla de reservar. `<label>` envolviendo al `<input>`, que es lo que hace
   que el texto entero sea zona de pulsación sin un `for` que mantener (FE-01). */
/* Aquí estaba `.inst-reserva`, la casilla de «reservar la plaza» de la hoja de
   agendar. Se fue con ella: esa decisión ya no es una casilla que se marca antes,
   son los dos botones del pie. */

/* El pie: los botones a la derecha y debajo, en pequeño, lo que se va a fijar. Es
   la última oportunidad de leer la fecha antes de comprometer a un equipo. */
.hoja-pie {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  border-top: 1px solid var(--hairline);
  padding-top: 12px;
}
/* Los botones, en fila dentro del pie, que es una columna. Hacen falta las dos
   direcciones: la columna apila los botones sobre la frase de la fecha, y esta
   fila pone los dos botones uno al lado del otro. Sin ella se apilarían también
   entre sí, y el pie de la hoja mediría tres renglones de botones.

   `flex-wrap` porque los dos textos son largos —uno nombra el día— y en una hoja
   estrecha no caben en la misma línea. Envolver es lo correcto ahí: lo que no
   puede pasar es que el segundo se salga por la derecha. */
.hoja-pie-botones {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
}
.hoja-pie .k {
  margin: 0;
  font-size: 12px;
  color: var(--subtinta);
}

/* ---------------- LA LISTA DE CANDIDATAS ----------------

   Todas a la vez, plegadas, y una desplegada. Es una lista de verdad —`<ul>` de
   `<li>`— y no un montón de cajas: son las obras que caben en la franja, y el
   lector de pantalla tiene que poder decir cuántas hay antes de recorrerlas. */

.hueco-lista {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* El filete por los CUATRO lados, igual en todos (DIS-002 regla 1). Lo que
   cambia de una fila a otra es el color del filete entero, nunca un canto. */
.hueco-cand {
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  overflow: hidden;
}
/* Desplegada = seleccionada, y eso lo dice el acento del material (regla 2). */
.hueco-cand.abierta {
  border-color: var(--canto);
}
/* Rechazada en una apertura anterior. El rojo habla de estado, no de acción
   (regla 3), y no va solo: la fila lleva su píldora con glifo y texto (regla 4).
   Gana al acento de «abierta» a propósito — que se haya desplegado otra vez no
   borra que se rechazó. */
.hueco-cand.rechazada {
  border-color: var(--rojo);
}
.hueco-cand-cab {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 40px;
  padding: 7px 11px;
  text-align: left;
}
.hueco-cand-cab:hover {
  background: var(--panel);
}
.hueco-cand.abierta .hueco-cand-cab {
  background: var(--canto-s);
}
/* El chevrón gira; no es la señal del estado —esa es `aria-expanded` y el panel
   abierto—, es la confirmación de que la fila hace algo al pulsarla. */
.hueco-cand-flecha {
  display: inline-flex;
  flex: none;
  color: var(--subtinta);
  transition: transform 0.15s ease;
}
.hueco-cand-flecha .ico {
  width: 13px;
  height: 13px;
}
.hueco-cand.abierta .hueco-cand-flecha {
  transform: rotate(90deg);
  color: var(--canto-h);
}
.hueco-cand-quien {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
  flex: 1;
}
.hueco-cand-quien b {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.hueco-cand-quien span {
  font-size: 12.5px;
  color: var(--subtinta);
}
.hueco-cand-marcas {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
}
.hueco-cand-panel {
  border-top: 1px solid var(--hairline);
  padding: 11px 12px 12px;
}
.hueco-cand-panel .hueco-motivo {
  margin-top: 0;
}
/* Dentro de la fila, la caja del canal ya no es una caja: la fila es la
   superficie. Un recuadro blanco con filete dentro de otro recuadro blanco con
   filete son dos superficies para un solo contenido. */
.hueco-cand-panel .hueco-canal {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 12px 0;
}
.hueco-clase {
  font-size: 12.5px;
  color: var(--subtinta);
  line-height: 1.45;
  margin: 6px 0 0;
}
/* El correo se escribe aquí cuando en la ficha no hay ninguno. El campo hereda
   `.campo`; lo único propio es que no arrastre el margen de un formulario. */
.hueco-correo {
  margin-bottom: 6px;
}
.hueco-correo input {
  font-variant-numeric: tabular-nums;
}
.hueco-cand-panel .hueco-nada {
  margin-bottom: 9px;
}

/* ================================================== AGENDAR LA VISITA A MEDIR

   La hoja `#d-medicion` y el pie de acción del bloque de mediciones de la ficha.

   Casi todo se reaprovecha —`.campo`, `.btn`, `.propuesta`, `.estado-caja`— y lo
   que hay aquí son las cuatro cosas que no existían: el pie de una superficie de
   la ficha que además ACTÚA, el grupo segmentado fuera de la hoja del hueco, el
   `<select>` dentro de un `.campo` y la nota de debajo. Ni un `border-left` de
   color y ni una sombra (DIS-002, reglas 1 y 3). */

/* El pie de una superficie que lleva acción. Separado de la tabla por el mismo
   hueco que separa dos superficies, para que no se lea como una fila más. */
.ficha-accion {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  display: flex;
  justify-content: flex-end;
  /* Los mismos 10px que `.ajustes-acciones` y que el resto de grupos de botones.
     Sin `gap`, «Editar el proveedor» y «Dar de baja» salían pegados, y dos
     botones que se tocan se leen como un control partido en dos — peor todavía
     cuando el de al lado es el rojo de dar de baja. */
  gap: 10px;
}
.ficha-accion .propuesta {
  margin: 0;
  text-align: left;
}

/* El grupo segmentado, sacado de `.hueco-canal .seg` porque ya lo usan dos
   sitios. Se deja la regla vieja donde estaba: allí vive dentro de una caja con
   su propio margen inferior, y unificar los dos márgenes habría movido una
   pantalla que ya estaba revisada. */
.seg {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* El `<select>` de un `.campo`. Mismas propiedades que `.campo-linea select` y
   por lo mismo que dice aquella: `appearance: none` quita el degradado que
   Safari le pone al nativo —Canto no admite degradados— y la flecha se dibuja
   con dos gradientes, porque un `select` no admite `::after`.

   Lo que cambia respecto al de la barra de filtros es el tamaño: aquí es un
   campo de formulario y va a la altura de toque, como el `<input>` de al lado.
   Dos alturas distintas en el mismo formulario se ven a la primera. */
.campo select {
  appearance: none;
  width: 100%;
  height: var(--toque);
  font: inherit;
  color: var(--tinta);
  background-color: var(--blanco);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--subtinta) 50%),
    linear-gradient(135deg, var(--subtinta) 50%, transparent 50%);
  background-position:
    calc(100% - 16px) calc(50% + 1px),
    calc(100% - 12px) calc(50% + 1px);
  background-size:
    4px 4px,
    4px 4px;
  background-repeat: no-repeat;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  padding: 0 32px 0 12px;
}
.campo select:focus-visible {
  border-color: var(--canto);
}

/* La línea de debajo del selector: la carga de esa persona ese día. Es la que
   evita que se elija a ciegas, así que no se esconde ni se abrevia.

   Solo sube 8px cuando cuelga de un `.campo`, que es lo que la pega a él. Detrás
   de cualquier otra cosa —una tabla, un calendario, el dibujo de una ventana—
   esos 8px la metían dentro de la caja de encima y el texto se montaba en su
   filete (Raúl, 27-9-2026). */
.medicion-nota {
  font-size: 12px;
  color: var(--subtinta);
  line-height: 1.45;
  margin: 8px 0 14px;
}
.campo + .medicion-nota {
  margin-top: -8px;
}

.medicion-pie {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 4px;
}

/* ==================== LO QUE HAY QUE ENTREGARLE A UNA PERSONA ====================

   La segunda cara de la hoja del alta de un instalador: su correo y la contraseña
   que se genera al darlo de alta. Se enseña **una vez** —en la base solo queda el
   hash (AUTH-01, SEC-02)— así que esto es lo único que hay que copiar bien, y por
   eso el valor va en `--mono`: en la tipografía del sistema, una `l` y un `1` de
   una contraseña generada son el mismo trazo.

   `user-select: all` hace que un solo clic seleccione el valor entero. Hay botón
   de copiar y sigue siendo la vía buena; esto es para cuando el portapapeles no
   está disponible —un navegador sin permiso, un escritorio remoto— y hay que
   seleccionar a mano sin arrastrar por encima de 32 caracteres sin espacios.

   `word-break: break-all` porque la contraseña es base64url y no tiene por dónde
   partir: sin esto ensancha la hoja en una pantalla estrecha en vez de bajar de
   línea. Filete de 1px por los cuatro lados, sin franja de color y sin sombra
   (DIS-002 regla 1). */
.credencial {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 6px 14px;
  align-items: baseline;
  padding: 11px 13px;
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
}
.credencial-k {
  margin: 0;
  font-size: 12px;
  color: var(--subtinta);
}
.credencial-v {
  margin: 0;
  font-size: 13px;
  word-break: break-all;
  user-select: all;
}

/* En qué paso de la hoja de agendar estamos. Una línea de 12px en gris y no una
   fila de pastillas numeradas: son dos pasos, y un indicador que ocupa más que el
   primer campo del formulario cuenta el andamio en vez del trabajo. */
.medicion-paso {
  font-size: 12px;
  color: var(--subtinta);
  margin: 0 0 14px;
}

/* Lo que se recuperó de un borrador, con su salida al lado. Reaprovecha
   `.propuesta` —la misma caja de filete que el aviso de la fecha pasada— y solo
   reparte: el texto a la izquierda y «Empezar de cero» pegado a la derecha. */
.medicion-recuperado {
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 14px;
}
.medicion-recuperado > span {
  display: flex;
  align-items: center;
  gap: 7px;
}
/* La salida no se parte en dos líneas: el texto de al lado es el que envuelve,
   porque es el que puede. Un botón de dos renglones en una caja de 12px se lee
   como un error de maqueta. */
.medicion-recuperado .btn {
  flex: none;
  white-space: nowrap;
}

/* El pie del paso que tiene vuelta atrás. Hereda de `.medicion-pie` y solo cambia
   el reparto: la salida a la izquierda y el acento a la derecha, para que «Atrás»
   no parezca la pareja del botón que agenda. El filete de arriba es el mismo hueco
   de 1px de siempre —el que separa el formulario de apuntar del pie de la hoja— y
   no un borde de color (DIS-002 regla 1). */
.medicion-pasos {
  justify-content: space-between;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}

/* Las dos respuestas de si hay medidas (`medicionPasoMedidas`): dos botones
   grandes, uno debajo de otro y a todo el ancho, con lo que pasa escrito debajo
   del rótulo. Los dos en secundario: ninguna de las dos respuestas es la buena,
   y el acento se lo lleva la que cierra el acto en el paso siguiente. */
.medicion-eleccion {
  display: grid;
  gap: 10px;
  margin-bottom: 14px;
}
.medicion-eleccion .eleccion {
  width: 100%;
  height: auto;
  min-height: var(--toque);
  padding: 12px 14px;
  justify-content: flex-start;
  align-items: flex-start;
  gap: 10px;
  text-align: left;
  white-space: normal;
}
.medicion-eleccion .eleccion .ico {
  flex: none;
  margin-top: 2px;
}
.medicion-eleccion .eleccion-tit {
  display: block;
  font-weight: 600;
  color: var(--tinta);
}
.medicion-eleccion .eleccion-sub {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--subtinta);
}

/* El cuerpo de la hoja de confirmar: qué pasa si se pulsa.
   A 14px y con `--tinta`, no a 12px en gris de nota al pie. Es lo que hay que
   leer antes de decidir, así que se lee como texto principal: la letra pequeña
   es justo lo que hace que nadie lea una advertencia. */
.conf-texto {
  font-size: 14px;
  color: var(--tinta);
  line-height: 1.5;
  margin: 0 0 16px;
  /* Los textos de estas seis preguntas nombran a un proveedor o a un equipo y
     luego explican qué se queda como estaba. Con la hoja `estrecha` (420px) eso
     son cuatro o cinco líneas, y sin tope superior se pegarían al pie. */
  max-width: 52ch;
}

/* ---- Lo que se va a llevar por delante, punto por punto (V089) ----
   Lista sin bola: el rótulo en negrita de cada línea —«Se borra:», «Fuera del
   programa:»— ya separa un punto del siguiente, y una bola más a la izquierda
   sangra el texto sin añadir nada. Sin color: la clase la dice la palabra. */
.conf-puntos {
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
  max-width: 52ch;
  font-size: 14px;
  line-height: 1.5;
  color: var(--tinta);
}
.conf-puntos li {
  padding: 8px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
}
.conf-puntos li + li {
  margin-top: 6px;
}

/* La fecha, con cifras tabulares como el resto de los números del producto. El
   `::-webkit-calendar-picker-indicator` se deja como lo pinta el navegador: es
   el control nativo del sistema, y sustituirlo por un glifo propio significaría
   reimplementar el calendario entero para ganar un icono. */
#medicion-fecha {
  font-variant-numeric: tabular-nums;
}

/* ---------------- QUÉ LLEVA EL HUECO: MOSQUITERA Y CAJONERA (V031) ------------

   Las dos primeras casillas de la oficina, así que aquí se decide cómo se ve un
   checkbox en esta aplicación.

   El `<fieldset>` va sin borde ni relleno: es una agrupación para quien escucha
   la pantalla (FE-01), no una caja para quien la mira. La caja ya es la hoja, y
   un recuadro alrededor de dos palabras es un filete que no separa nada.

   `accent-color` en vez de dibujar la casilla a mano: el control nativo trae el
   foco, el estado indeterminado y el tamaño de toque del sistema, y lo único que
   le sobra es el azul del navegador. Con esto lleva el acento del material, que
   es lo que Canto pide de algo que se pulsa (regla 2). */
.hueco-lleva {
  border: 0;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 18px;
}
.hueco-lleva > .k {
  /* `<legend>` con `float` a nada: un legend dentro de un fieldset flex no
     participa del flex en todos los navegadores, así que se saca del flujo y se
     le da ancho completo para que las dos casillas caigan debajo. */
  float: left;
  width: 100%;
  padding: 0;
  font-size: 12px;
  color: var(--subtinta);
  margin: 0 0 8px;
}
/* La casilla del motor cuando no hay cajonera (V041). El `disabled` ya la
   bloquea; esto es lo que hace que se vea. Y el color no va solo: la frase de al
   lado dice qué falta (FE-02, Canto regla 4). */
.hueco-lleva .casilla.apagada {
  opacity: 0.45;
  cursor: default;
}
.casilla {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--tinta);
  cursor: pointer;
  /* El área de toque va por el relleno y no por un alto fijo: así el objetivo
     crece con la letra si alguien sube el tamaño del navegador. */
  padding: 6px 2px;
}
.casilla input {
  width: 16px;
  height: 16px;
  margin: 0;
  flex: none;
  accent-color: var(--canto);
  cursor: pointer;
}

/* Varias `.casilla` que se leen juntas: los siete días de la semana que le van
   bien al cliente (V071). En rejilla y no en columna porque son una sola
   pregunta —«¿qué días?»— y siete filas la convertirían en siete. */
.lista-casillas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 0 12px;
}

/* ------------------- LA HOJA DE ALTA Y EDICIÓN DE UN CLIENTE -------------------

   Reaprovecha `.campo`, `.seg`, `.medicion-nota` y `.medicion-pie`: es el mismo
   formulario en otra hoja, y dos juegos de reglas para la misma caja es como
   acaban pareciendo de dos aplicaciones. Lo propio son dos cosas. */

/* El rótulo que separa lo que se sabe al colgar el teléfono de lo que hace falta
   para facturar. Con su filete arriba, que es el mismo hueco de 1px de siempre y
   no un borde de color (DIS-002 regla 1). */
.cliente-grupo {
  margin: 4px 0 2px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}

/* El mismo filete, sin rótulo, para separar las notas de todo lo anterior. No
   son una sección más —son lo que se apunta al margen— y ponerles un rótulo
   encima repetiría la etiqueta que ya lleva el propio campo. */
.cliente-filete {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: 18px 0 14px;
}

/* Los dos botones de una fila de la tabla de direcciones. `inline-flex` y no
   `flex`: la celda ya coloca su contenido, y un contenedor de bloque lo
   descolocaría a la izquierda. */
.dir-acciones {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* El código postal no necesita 460px de ancho. Se queda a la mitad y el campo de
   al lado no sube a su línea porque cada `.campo` es `display: block`: es un
   estrechamiento, no una rejilla de dos columnas — que a 390px se partiría. */
.campo.corto input {
  max-width: 180px;
}

/* ============================================ PRESUPUESTOS EN PDF

   La ficha de un presupuesto es una COMPARACIÓN, y de ahí sale toda la maqueta: el
   PDF que salió de PrefWeb a la izquierda y lo que el lector entendió a la
   derecha, en mitades iguales. No hay una mitad principal — el trabajo de esta
   pantalla es mirar las dos a la vez y decidir si dicen lo mismo.

   Mitades IGUALES y no una fija de 460px: privilegiar un lado sería decir cuál
   de los dos se lee, y aquí se leen los dos. `minmax(0, 1fr)` en las dos
   columnas y no `1fr` a secas por lo mismo que en `.dos-paneles`: el contenido
   mínimo de la tabla de posiciones —ocho columnas que no parten— pondría un
   suelo al ancho y las mitades dejarían de serlo. Lo que no cabe se desliza
   dentro de la tabla, que es lo que ya hacen todas las tablas de la aplicación. */
.presupuesto-paneles {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}

/* El PDF acompaña al scroll, como la ficha de Márgenes y por el mismo motivo
   —bajar por las posiciones dejaría el documento arriba, justo mientras se
   contrasta— y con la misma mecánica: el `sticky` va en el ELEMENTO DE LA
   REJILLA, cuyo bloque contenedor es su área y por tanto tiene recorrido, y
   `align-items: start` arriba es la otra mitad de la condición.

   La diferencia con `.panel-ficha` es `height` en vez de `max-height`, y es
   obligatoria: dentro va un `<iframe>` que tiene que LLENAR la caja, y un
   porcentaje contra un alto indefinido no resuelve — el visor se quedaría en los
   150px que Chrome le da por defecto a un iframe sin alto.

   `top: 20px` es el relleno superior de `.lienzo`: el documento se detiene donde
   empezaba. No es un número mágico, es el margen de la página. */
.panel-pdf {
  position: sticky;
  top: 20px;
  height: calc(100vh - 40px);
  height: calc(100dvh - 40px);
}
.panel-pdf > .sup {
  display: flex;
  flex-direction: column;
  height: 100%;
}
/* El cuerpo se queda el hueco Y es a su vez una columna flex, igual que en
   `.rejilla > .sup > .cuerpo`: como bloque normal, el `flex: 1` del visor no
   tendría contra qué crecer. Relleno de 10px y no de 16: lo que hay dentro es un
   documento con sus propios márgenes impresos, y el marco no necesita otro. */
.panel-pdf > .sup > .cuerpo {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
}

/* El visor. Filete por los cuatro lados como cualquier superficie de Canto, y
   fondo `--panel` para que el blanco de la hoja se distinga del blanco de la
   caja mientras carga. */
.pdf-visor {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--panel);
}
.pdf-pie {
  margin: 0;
  flex: 0 0 auto;
  /* Las tres cosas que se pueden hacer con el papel, en una fila. Envuelven en
     vez de desbordar: esta columna ya compite con el visor, y un botón que se
     sale es un botón que no se pulsa. */
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

/* Las tres superficies de la derecha, apiladas con el mismo hueco que la
   rejilla. `+ .sup` y no un `gap` en el contenedor porque no es una rejilla: es
   una columna de bloques, y el margen entre hermanos no deja hueco colgando
   cuando solo hay uno. */
#presupuesto-leido > .sup + .sup {
  margin-top: 16px;
}

/* Los avisos del lector, debajo de los totales y separados por el filete de
   siempre. El rótulo va en ámbar porque habla de algo que hay que atender, y
   lleva glifo: el color no es nunca la única señal (DIS-002, reglas 3 y 4). */
.presupuesto-avisos {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline);
}
.presupuesto-avisos > .k {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 7px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ambar);
}
.presupuesto-avisos > .k .ico {
  width: 13px;
  height: 13px;
}
.presupuesto-avisos ul {
  display: grid;
  gap: 5px;
  margin: 0;
  padding-left: 18px;
  font-size: 12.5px;
  color: var(--subtinta);
}
.presupuesto-avisos strong {
  color: var(--tinta);
  font-weight: 600;
}
.presupuesto-avisos .mono {
  color: var(--tinta);
}

/* El formulario de confirmar. `gap` en la rejilla y el margen de `.campo` a
   cero: dos separaciones para lo mismo dejan huecos de 24px entre controles que
   van juntos. */
.presupuesto-form {
  display: grid;
  gap: 10px;
}
.presupuesto-form .campo {
  margin-bottom: 0;
}
.presupuesto-form .acciones {
  display: grid;
  gap: 8px;
  margin-top: 4px;
}
.presupuesto-pista {
  margin: 0;
  font-size: 11.5px;
  color: var(--subtinta);
}
.presupuesto-pista {
  margin-top: -4px;
}
.presupuesto-pista strong {
  color: var(--tinta);
  font-weight: 600;
}

/* La confirmación enseña el pedido entero antes de escribirlo. Es una sola
   superficie dentro de la hoja: cabecera, datos y líneas separados por el
   filete habitual, sin tarjetas encadenadas ni resúmenes que oculten posiciones. */
.presupuesto-confirmacion-pedido {
  overflow: hidden;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
}
.presupuesto-confirmacion-pedido > .barra {
  padding: 12px 14px;
}
.presupuesto-confirmacion-pedido > .barra > .pieza {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
}
.presupuesto-confirmacion-pedido > .ficha {
  padding: 14px;
  border-bottom: 1px solid var(--hairline);
}
.presupuesto-confirmacion-lineas {
  padding: 0 14px 14px;
}
#d-confirmar-presupuesto .medicion-pie {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--hairline);
}

/* Las cifras de la hoja de confirmar: dos casillas cortas, una al lado de otra.
   En columna ocuparían dos filas enteras para pedir dos números de tres dígitos,
   y la pareja —horas y material— se lee junta o no se lee. Se apilan en pantalla
   estrecha por el `auto-fit`, sin necesidad de una consulta de medios. */
.conf-cifras {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px;
}
/* El `.campo` de dentro no lleva su margen inferior: el hueco entre las dos —y
   con lo que venga debajo— lo pone el `gap` de la rejilla. */
.conf-cifras > .campo {
  margin-bottom: 0;
}

/* ---------------- EL CALENDARIO EN PANTALLA ESTRECHA ----------------

   La cuadrícula se conserva completa, pero ya no necesita desplazamiento
   horizontal: siete días siguen siendo siete columnas visibles. Para que cada
   casilla quepa, la información secundaria se resume con elipsis y las piezas
   del mes pasan a una línea. El nombre y el detalle completos continúan en el
   `title` y en el nombre accesible que genera `calendario.js`.

   La barra se convierte en una rejilla de tres filas previsibles: navegación y
   rango, selector de vista, y equipo con recuento. Así no depende de dónde
   decida envolver cada navegador ni aparece un control aislado al borde. */
@media (max-width: 620px) {
  .cal-barra {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    gap: 8px;
    padding: 10px;
  }
  .cal-barra > #cal-hoy {
    grid-column: 1;
  }
  .cal-barra > .cal-flechas {
    grid-column: 2;
  }
  .cal-barra > .cal-rango {
    grid-column: 3;
    min-width: 0;
    overflow: hidden;
    text-align: right;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cal-hueco {
    display: none;
  }
  .cal-barra > .cal-vistas {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .cal-vistas .filtro {
    min-width: 0;
    justify-content: center;
    padding-inline: 6px;
  }
  .cal-barra > .campo-linea {
    grid-column: 1 / 3;
    min-width: 0;
    overflow: hidden;
  }
  #cal-equipo-caja,
  #cal-equipo {
    flex: 1 1 auto;
    min-width: 0;
  }
  #cal-equipo .desp-boton {
    min-width: 0;
    max-width: 100%;
  }
  #cal-equipo .desp-txt {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cal-barra > .cuenta {
    grid-column: 3;
    min-width: 0;
    margin-left: 0;
    overflow: hidden;
    text-align: right;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cal {
    overflow-x: hidden;
    padding: 6px;
  }
  .cal table {
    width: 100%;
    min-width: 0;
  }
  .cal th,
  .cal td {
    min-width: 0;
    padding: 2px;
    overflow: hidden;
  }
  .cal thead th {
    padding: 4px 1px;
    overflow: hidden;
    font-size: 9.5px;
    text-align: center;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cal-mes td {
    height: 82px;
  }
  .cal-semana td,
  .cal-dia td {
    height: 64px;
  }
  .cal-esquina,
  .cal-franja {
    width: 48px;
  }
  .cal-franja {
    padding: 3px;
    overflow-wrap: anywhere;
    font-size: 9px;
    line-height: 1.2;
  }
  .cal-cab-dia {
    justify-content: center;
    gap: 2px;
  }
  .cal-num {
    min-width: 20px;
    min-height: 20px;
    padding: 2px 4px;
    font-size: 11px;
  }
  .cal-semana thead .cal-num {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    padding-inline: 1px;
  }
  .cal-semana thead .cal-num .n {
    font-size: 11px;
  }
  .cal-cerrado {
    max-width: 100%;
    padding: 0;
    font-size: 8px;
  }
  .cal-pila {
    gap: 2px;
  }
  .cal-obra,
  .cal-visita,
  .cal-libre {
    overflow: hidden;
    padding: 2px 3px;
    border-radius: 4px;
    font-size: 9px;
    line-height: 1.2;
  }
  .cal-obra-cab {
    flex-wrap: nowrap;
    gap: 2px;
    overflow: hidden;
  }
  .cal-obra .ico,
  .cal-visita .ico,
  .cal-libre .ico {
    width: 9px;
    height: 9px;
  }
  .cal-obra-cod {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .cal-obra-est {
    display: none;
  }
  .cal-obra-cli,
  .cal-obra-pie,
  .cal-visita-quien,
  .cal-libre-n {
    font-size: 8.5px;
  }
  .cal-mes .cal-obra-cli,
  .cal-mes .cal-visita-quien {
    display: none;
  }
  .cal-mas {
    max-width: 100%;
    overflow: hidden;
    padding: 1px 2px;
    font-size: 9px;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cal-libre-cab {
    min-width: 0;
    gap: 2px;
    overflow: hidden;
    white-space: nowrap;
  }
  .cal-libre-n {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .cal-libre-sutil {
    min-width: 0;
    gap: 2px;
    overflow: hidden;
    padding: 1px;
    font-size: 8.5px;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* El emplazamiento y el municipio se caen en semana y día: a 88px de columna
     el tercer renglón sale recortado a dos letras, que no informa de nada y
     roba el alto que necesitan el código y el cliente. Siguen en el `title` y
     en el nombre accesible del botón.

     `.cal-visita-quien` NO entra aquí, y por eso es una clase aparte: el nombre
     del medidor es el «quién va» de una visita —lo que se viene a mirar— y
     esconderlo dejaría la pieza diciendo que hay que ir a medir sin decir quién
     va. Ocupa el renglón que deja libre el emplazamiento. */
  .cal-obra-pie {
    display: none;
  }
  .cal-rango {
    font-size: 13px;
  }
  /* El borrador del correo entra en la lista de los 16px por lo mismo que los
     otros campos: por debajo de eso, Safari de iPhone amplía la pantalla al
     tocarlo. Aquí abajo y no arriba con su regla, porque una media query no
     añade especificidad y la de arriba está escrita después que aquella. */
  textarea.hueco-borrador {
    font-size: 16px;
  }
  /* Y los dos controles de la hoja de agendar, por lo mismo. El `<select>`
     también cuenta: Safari amplía al desplegarlo si el texto va por debajo de
     16px, y el rebote deja la hoja medio fuera de la pantalla. */
  .campo select,
  #medicion-fecha {
    font-size: 16px;
  }
}

/* ---------------- LA PRESUPUESTO EN PANTALLA ESTRECHA ----------------

   Al final de la hoja como todo lo responsive: una media query no añade
   especificidad, así que lo que se escriba después la apagaría en silencio.

   El umbral es 1000px, el mismo con el que desaparece la columna de las otras
   tres pantallas: por debajo de eso no hay dos mitades que repartir.

   Y el visor **deja de acompañar al scroll y pasa a medir 60vh**, que es la parte
   que no es evidente. Apilado y con el alto de la ventana, para llegar a la
   primera posición habría que pasar una pantalla entera de PDF; y clavado arriba
   con su propio recorte, sería un contenedor de desplazamiento dentro de otro.
   Con 60vh se ve la hoja y se ve que debajo sigue habiendo algo.

   El `flex: 0 0 auto` es obligatorio aquí: con la superficie en alto automático,
   un `flex: 1 1 auto` con `min-height: 0` colapsa a cero y el visor desaparece
   sin más aviso que una caja vacía. */
@media (max-width: 1000px) {
  .presupuesto-paneles {
    grid-template-columns: minmax(0, 1fr);
  }
  .panel-pdf {
    position: static;
    height: auto;
  }
  .panel-pdf > .sup {
    height: auto;
  }
  .pdf-visor {
    flex: 0 0 auto;
    height: 60vh;
    height: 60dvh;
  }
}

/* Y los controles del formulario de confirmar entran en la lista de los 16px por
   lo mismo que los demás: por debajo de eso, Safari de iPhone amplía la pantalla
   al tocarlos y el rebote deja media pantalla fuera. El `<select>` cuenta igual,
   que es lo que se olvida. */
@media (max-width: 620px) {
  .presupuesto-form .campo input,
  .presupuesto-form .campo select {
    font-size: 16px;
  }
}

/* ==================== EL CAMINO COMERCIAL ====================

   Tres hojas: abrir la obra, presupuestarla y confirmarla. Canto entero — ni una
   franja de color en un borde, un solo acento y solo en lo accionable, y la
   densidad de un ERP en la tabla de líneas, que es donde se nota.

   Lo responsive de este bloque va AL FINAL del fichero, después de estas reglas:
   una media query no añade especificidad, así que una regla escrita más abajo la
   apaga en silencio. */

/* La dirección nueva del alta de obra, agrupada y con su filete arriba. Mismo
   recurso que `.cliente-grupo` en la hoja del cliente: separa lo que se sabe al
   coger el teléfono de lo que hay que preguntar. */
.obra-nueva {
  margin-top: 4px;
}

/* ---------------- EL BUSCADOR DE CLIENTES DEL ALTA DE OBRA ----------------

   Solo sale cuando la hoja se abre desde la barra de Proyectos. Son botones de
   verdad apilados y no un desplegable flotante: se llega a ellos con el tabulador
   sin programar las flechas, y no hay capa por encima que tape lo de debajo.

   Tope de altura y desplazamiento propio: ocho opciones a 40px son 320, y sumadas
   al resto de la hoja empujarían el botón de guardar fuera de la pantalla en un
   portátil. Con el tope, lo que se desplaza es la lista y no la hoja. */
.obra-cli-opciones {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 260px;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  margin: 6px 0 0;
  padding: 4px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
}
/* El recuento («4 clientes») va DEBAJO de la caja, no dentro, y más cerca de
   ella que una nota suelta. Vale para las dos notas que caben en este hueco: el
   recuento y el «Buscando…». */
#obra-cli-lista > .medicion-nota {
  margin-top: 6px;
}

/* Cada opción. Bordes iguales por los cuatro lados —y en reposo, ninguno—: aquí
   una franja de color a la izquierda para marcar la fila sería exactamente lo que
   Canto rechaza (regla 1). Lo que marca el hover y el foco es el tinte y el
   filete completo. */
.obra-cli-op {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-height: var(--toque);
  padding: 6px 10px;
  border: 1px solid transparent;
  border-radius: var(--r-ctrl);
  background: none;
  font: inherit;
  color: var(--tinta);
  text-align: left;
  cursor: pointer;
}
.obra-cli-op:hover {
  background: var(--panel);
}
.obra-cli-op:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
  background: var(--canto-s);
}
.obra-cli-op .nom {
  font-weight: 600;
}
.obra-cli-op .sub {
  font-size: 12.5px;
  color: var(--subtinta);
}

/* El elegido en la hoja de fusionar/cambiar titular (V082). Filete por los
   cuatro lados y tinte, nunca una franja al lado (regla 1 de Canto), y con el
   `aria-pressed` puesto para que se sepa sin ver el color. */
.obra-cli-op[aria-pressed='true'] {
  border-color: var(--canto);
  background: var(--canto-s);
}
/* Y debajo, dicho con palabras: el color solo no vale (regla 4). */
.tit-elegido {
  margin: 4px 0 0;
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--tinta);
}

/* ============ EL QUE SE ACABA DE CREAR ============

   Vuelve del alta y sale PRIMERO, aunque el orden de la lista sea alfabético y
   le tocara el puesto catorce. El motivo es que quien acaba de teclear sus datos
   no está hojeando la cartera: viene a elegirlo, y hacerle buscar entre veinte
   nombres el que ha escrito él hace tres segundos es devolverle el trabajo.

   Y va resaltado, porque salir primero no basta: en una lista ordenada por
   nombre, el primero es donde se espera «Aluminis…», no una fila especial. Tres
   señales y ninguna es solo el color (regla 4 de Canto): el filete completo en el
   acento —los cuatro lados, nunca una franja al lado (regla 1)—, el tinte, y el
   rótulo «recién creado» escrito debajo del nombre. */
.obra-cli-op.recien {
  border-color: var(--canto);
  background: var(--canto-s);
}
.obra-cli-op.recien:hover {
  background: var(--canto-s);
}
.obra-cli-op.recien .nom {
  color: var(--canto-h);
}
.obra-cli-op .nuevo {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--canto-h);
}

/* ============ DARLO DE ALTA SIN SALIR DE AQUÍ ============

   El buscador solo acepta clientes que existen, y hasta ahora el que no existía
   mandaba a la pantalla de Clientes y a volver a empezar por la obra. Este botón
   es ese camino, hecho aquí: levanta la hoja del alta ENCIMA de esta, y al
   guardar se vuelve a este mismo sitio con el cliente nuevo puesto arriba.

   `.sec` y no `.pri`: el acto de esta hoja es abrir la obra, no dar de alta a
   nadie. Un segundo botón verde compitiendo con el primario diría que hay dos
   caminos igual de principales, y el principal es elegir de la lista (regla 2). */
/* ---------- EL ALTA DE CLIENTE, AL MEDIO Y ABAJO ----------

   Decisión de Raúl del 21 ago. 2026. Estaba pegado a la izquierda debajo de la
   lista; va centrado, que es donde se busca la salida de un paso que no se puede
   dar: quien llega aquí ha escrito un nombre que no existe y lo que necesita es
   la única puerta que queda, no un botón más en el margen.

   Sigue siendo lo ÚLTIMO de la caja —`pintarCandidatos` lo concatena al final,
   detrás de la lista y del recuento— y eso no cambia: primero se mira si el
   cliente ya está, y solo después se da de alta. Un alta encima de la lista
   invita a crear el duplicado que la lista estaba a punto de evitar.

   El `margin-top` es la separación con el recuento de arriba, que es texto de
   11,5px: sin él el botón se pega a una línea de letra pequeña. */
.obra-cli-alta {
  display: flex;
  justify-content: center;
  margin-top: 10px;
}

/* El cliente ya elegido: se queda escrito arriba de la hoja con la salida al
   lado. Es la única forma de saber a nombre de quién se está abriendo la obra
   cuando el buscador ya no está en pantalla. */
.obra-cli-puesto {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  padding-bottom: 10px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--hairline);
}
/* `.k` no trae tamaño por sí sola —la convención de la casa la escribe cada
   contexto (ver `.campo > .k` y `.plan-alta > .k`)—, así que aquí también. */
.obra-cli-puesto > .k {
  flex: 1 0 100%;
  margin: 0;
  font-size: 12px;
  color: var(--subtinta);
}
.obra-cli-nom {
  flex: 1 1 auto;
  margin: 0;
  font-weight: 600;
}

/* ---------------- LA TABLA DE LÍNEAS ----------------

   Se desplaza de lado DENTRO de su caja y no estira la hoja: son ocho columnas y
   en un portátil de 13" no caben sin apretar las cifras hasta hacerlas ilegibles.
   Es el mismo recurso que el calendario en móvil. */
.presu-tabla {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
}
.presu-lineas {
  min-width: 940px;
  margin: 0;
  border: 0;
}
/* Fila de 32px de contenido: la densidad de Canto. Con los controles dentro sube
   a 40, que sigue siendo una tabla de trabajo y no un formulario. */
.presu-lineas td {
  padding: 5px 8px;
  vertical-align: top;
}
.presu-lineas th {
  padding: 6px 8px;
}
/* `block` y no el `inline-block` de serie: en una celda con dos controles —la
   descripción y su tipo, la cantidad y su unidad— en línea se quedan uno al lado
   del otro, se salen de la columna y pisan la de al lado. Con `block` cada uno
   ocupa su renglón, que es lo que el `margin-top` de abajo da por hecho. */
.presu-lineas input,
.presu-lineas select {
  display: block;
  width: 100%;
  min-height: 30px;
  padding: 4px 6px;
  font: inherit;
  font-size: 12.5px;
  color: var(--tinta);
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: 6px;
}
/* Las cifras, tabulares y a la derecha: es lo que permite leer una columna de
   veinte importes de un vistazo en vez de leerlos uno a uno. */
.presu-lineas td.num input {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* El anillo de foco lo pone la regla global `:focus-visible` del principio del
   fichero; aquí solo se marca el borde, igual que hace `.campo input`. */
.presu-lineas input:focus-visible,
.presu-lineas select:focus-visible {
  border-color: var(--canto);
}
/* El segundo control de una celda —el tipo, la unidad— más pequeño y debajo: es
   contexto de lo de arriba, no un campo con el mismo peso. */
.presu-lineas td > select:not(:only-child),
.presu-lineas td > input:not(:only-child) + select {
  margin-top: 4px;
  font-size: 11.5px;
  color: var(--subtinta);
}
/* El aviso de que esa línea no generará pedido. Ámbar porque habla de estado, y
   con glifo porque el color nunca es la única señal. */
.presu-huerfana {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 4px;
  color: var(--ambar);
}
.presu-huerfana .ico {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
}

/* La fila de debajo de la tabla: añadir línea a la izquierda, la validez a la
   derecha. Se envuelve en estrecho en vez de recortarse. */
.presu-bajo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 16px;
  margin-top: 12px;
}
.presu-bajo .campo {
  margin: 0;
}

/* Los totales. `auto-fit` y no cinco columnas fijas: en estrecho se reparten
   solos sin una media query que los persiga. */
.presu-totales {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1px;
  margin-top: 14px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  overflow: hidden;
}
/* El hueco de 1px es el filete, como en toda la casa: el fondo se ve por el
   `gap`. Ni un `border-right` que haya que quitarle al último. */
.presu-cifra {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 12px;
  background: var(--blanco);
}
.presu-cifra .num {
  font-size: 15px;
  font-variant-numeric: tabular-nums;
}

/* La tabla de presupuestos ya no lleva acciones en la fila: mandar el presupuesto
   y firmar el corte viven en el paso del recorrido que los provoca (ver
   `ACCIONES_FASE` en `app.js`). Con ellas se fue `.presu-acciones` y su regla de
   móvil, que apilaba los dos botones dentro de la columna. */

/* ---------------- EL COMERCIAL EN PANTALLA ESTRECHA ----------------

   Al final del fichero como todo lo responsive, por lo que dice la cabecera del
   bloque. Los 16px de los controles son la regla de siempre: por debajo de eso,
   Safari de iPhone amplía la pantalla al tocarlos y el rebote deja media hoja
   fuera. El `<select>` cuenta igual, que es lo que se olvida. */
@media (max-width: 620px) {
  .presu-lineas input,
  .presu-lineas select {
    font-size: 16px;
  }
  .presu-lineas td > select:not(:only-child),
  .presu-lineas td > input:not(:only-child) + select {
    font-size: 16px;
  }
}

/* =================================== UBICACIÓN DE LOS EQUIPOS EN PESTAÑAS

   Vive debajo de la lista de instaladores, no dentro de la ficha de un equipo.
   La tira superior se comporta como las pestañas de un navegador: la activa se
   une visualmente al panel blanco. Si no caben, bajan de línea; no hay un carril
   lateral que pueda moverse por accidente. */
.equipo-ubicacion-panel {
  margin-top: 16px;
  overflow: hidden;
}
.equipo-ubicacion-panel > .cabeza {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--hairline);
}
.equipo-ubicacion-panel .t,
.equipo-ubicacion-panel .sub {
  margin: 0;
}
.equipo-ubicacion-panel .t {
  font-weight: 700;
}
.equipo-ubicacion-panel .sub,
.equipo-mapa-recarga {
  color: var(--subtinta);
  font-size: 12.5px;
}
.equipo-mapas-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  overflow-x: hidden;
  padding: 9px 10px 0;
  border-bottom: 1px solid var(--hairline);
  background: var(--panel);
}
.equipo-mapa-selector {
  display: none;
}
.equipo-mapa-tab {
  min-height: 38px;
  flex: 1 1 140px;
  margin-bottom: -1px;
  padding: 7px 14px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl) var(--r-ctrl) 0 0;
  background: var(--panel);
  color: var(--subtinta);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.equipo-mapa-tab:hover {
  background: var(--blanco);
  color: var(--tinta);
}
.equipo-mapa-tab.activa {
  border-bottom-color: var(--blanco);
  background: var(--blanco);
  color: var(--canto-h);
}
@media (max-width: 620px) {
  .equipo-mapas-selector {
    padding: 8px 10px;
    border-bottom: 1px solid var(--hairline);
    background: var(--panel);
  }
  .equipo-mapa-selector {
    width: 100%;
    min-height: 44px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    border: 1px solid var(--hairline);
    border-radius: var(--r-ctrl);
    background: var(--blanco);
    color: var(--canto-h);
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    text-align: left;
  }
  .equipo-mapa-selector .ico {
    width: 15px;
    height: 15px;
    flex: 0 0 auto;
    margin-left: auto;
    transform: rotate(90deg);
    transition: transform 120ms ease-out;
  }
  .equipo-mapa-selector[aria-expanded='true'] .ico {
    transform: rotate(-90deg);
  }
  .equipo-mapas-tabs {
    display: none;
    flex-direction: column;
    gap: 4px;
    padding: 6px 0 0;
    overflow: visible;
    border: 0;
    background: transparent;
  }
  .equipo-mapas-tabs.desplegadas {
    display: flex;
  }
  .equipo-mapa-tab {
    width: 100%;
    min-height: 44px;
    flex: none;
    margin: 0;
    border-radius: var(--r-ctrl);
    text-align: left;
  }
  .equipo-mapa-tab.activa {
    border-color: var(--canto);
  }
}
.equipo-mapa-panel {
  min-height: 180px;
  background: var(--blanco);
}
.equipo-mapa-panel > .vacio,
.equipo-mapa-panel > .cargando {
  min-height: 180px;
}
.equipo-mapa-espera {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background-image: none;
  color: var(--subtinta);
  font-size: 13px;
}
.equipo-mapa-espera .ico {
  width: 16px;
  height: 16px;
}
.equipo-mapa {
  position: relative;
  height: clamp(460px, 56vh, 620px);
  overflow: hidden;
  background-color: #e7ece9;
  background-image:
    linear-gradient(var(--hairline) 1px, transparent 1px),
    linear-gradient(90deg, var(--hairline) 1px, transparent 1px);
  background-size: 32px 32px;
}
.equipo-mapa-reducido {
  cursor: pointer;
}
.equipo-mapa-reducido:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}
.equipo-mapa-ampliar {
  position: absolute;
  z-index: 3;
  top: 10px;
  right: 10px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 7px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: rgb(255 255 255 / 94%);
  color: var(--tinta);
  font-size: 12px;
  font-weight: 600;
  pointer-events: none;
}
.equipo-mapa-ampliar .ico {
  width: 14px;
  height: 14px;
}
.equipo-mapa-svg {
  display: block;
  width: 100%;
  height: 100%;
}
.equipo-mapa-marca {
  cursor: pointer;
}
.equipo-mapa-marca .punto {
  fill: var(--canto);
  stroke: var(--blanco);
  stroke-width: 2px;
}
.equipo-mapa-marca .iniciales {
  fill: #fff;
  font-size: 11px;
  font-weight: 700;
  text-anchor: middle;
}
.equipo-mapa-marca .fondo-nombre {
  fill: var(--blanco);
  stroke: var(--hairline);
}
.equipo-mapa-marca .nombre {
  fill: var(--tinta);
  font-size: 12px;
  font-weight: 600;
}
.equipo-mapa-marca:focus-visible {
  outline: none;
}
.equipo-mapa-marca:focus-visible .punto {
  stroke: var(--tinta);
  stroke-width: 4px;
}
.equipo-mapa-marca .seleccionada .punto {
  stroke: var(--tinta);
  stroke-width: 4px;
}
.equipo-mapa-atribucion {
  position: absolute;
  z-index: 3;
  right: 4px;
  bottom: 3px;
  padding: 2px 4px;
  background: rgba(255, 255, 255, 0.88);
  color: #334;
  font-size: 10px;
}
.equipo-mapa-vacio {
  min-height: 100%;
  display: grid;
  place-items: center;
  padding: 24px;
  color: var(--subtinta);
  text-align: center;
}
.equipo-ubicacion-lista {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
  gap: 1px;
  overflow: hidden;
  margin: 0;
  padding: 0;
  background: var(--hairline);
  list-style: none;
}
.equipo-ubicacion-fila {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 58px;
  padding: 9px 12px;
  background: var(--blanco, #fff);
}
.equipo-ubicacion-fila > .ini {
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  font-size: 10px;
  font-weight: 700;
}
.equipo-ubicacion-fila .quien {
  display: flex;
  min-width: 0;
  flex-direction: column;
}
.equipo-ubicacion-fila .nom {
  overflow: hidden;
  font-size: 13px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.equipo-ubicacion-fila.viva > .ini {
  background: var(--canto-s);
  color: var(--canto-h);
}
.equipo-ubicacion-fila.tarde > .ini,
.equipo-ubicacion-fila.vieja > .ini,
.equipo-ubicacion-fila.sin > .ini {
  color: var(--subtinta);
}
.equipo-ubicacion-fila.vieja .sub {
  color: var(--rojo, #9b2c2c);
}

/* El mapa grande ocupa la ventana sin usar la API de pantalla completa del
   navegador: sigue siendo una hoja accesible, se cierra con Esc y no oculta la
   salida. El selector flota arriba a la izquierda sobre las teselas. */
.mapa-equipo-grande {
  width: calc(100vw - 24px);
  max-width: none;
  height: calc(100vh - 24px);
  height: calc(100dvh - 24px);
  max-height: none;
  margin: 12px auto;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
  color: var(--tinta);
}
.mapa-equipo-grande::backdrop {
  background: rgb(29 29 31 / 38%);
}
.mapa-equipo-grande-cuerpo {
  position: absolute;
  inset: 0;
}
.equipo-mapa.mapa-equipo-grande-lienzo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
.mapa-equipo-grande-lienzo.arrastrando {
  cursor: grabbing;
}
.mapa-equipo-grande-lienzo .equipo-mapa-marca {
  cursor: inherit;
}
.mapa-equipo-miembros {
  position: absolute;
  z-index: 4;
  top: 16px;
  left: 16px;
  width: min(310px, calc(100% - 88px));
  max-height: calc(100% - 32px);
  overflow-y: auto;
  padding: 8px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: rgb(255 255 255 / 96%);
  overscroll-behavior: contain;
}
.mapa-equipo-miembros > .t {
  margin: 2px 4px 8px;
  font-size: 13px;
  font-weight: 700;
}
.mapa-equipo-miembro {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr);
  align-items: center;
  gap: 9px;
  width: 100%;
  min-height: 42px;
  padding: 5px 7px;
  border: 1px solid transparent;
  border-radius: var(--r-ctrl);
  background: transparent;
  color: var(--tinta);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.mapa-equipo-miembro:hover,
.mapa-equipo-miembro:focus-visible,
.mapa-equipo-miembro.activo {
  border-color: var(--hairline);
  background: var(--canto-s);
  outline: none;
}
.mapa-equipo-miembro:disabled {
  color: var(--subtinta);
  cursor: default;
}
.mapa-equipo-miembro:disabled:hover {
  border-color: transparent;
  background: transparent;
}
.mapa-equipo-miembro .ini {
  grid-row: 1 / span 2;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--canto-s);
  color: var(--canto-h);
  font-size: 10px;
  font-weight: 700;
}
.mapa-equipo-miembro .nom {
  min-width: 0;
  overflow: hidden;
  font-size: 13px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mapa-equipo-miembro .sin {
  margin-top: -7px;
  color: var(--subtinta);
  font-size: 11px;
}
.mapa-equipo-cerrar {
  position: absolute;
  z-index: 5;
  top: 16px;
  right: 16px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: rgb(255 255 255 / 96%);
  color: var(--tinta);
  cursor: pointer;
}
.mapa-equipo-cerrar:hover,
.mapa-equipo-cerrar:focus-visible {
  border-color: var(--canto);
  color: var(--canto-h);
  outline: none;
}
.mapa-equipo-cerrar .ico {
  width: 16px;
  height: 16px;
}

/* ============================================ QUIÉN ESTÁ EN UN EQUIPO

   Dos columnas iguales con los botones en medio, que es la forma que se pidió y
   la que hace evidente lo que se puede hacer: lo de la izquierda está dentro, lo
   de la derecha fuera, y en medio están las dos direcciones.

   `1fr auto 1fr`: las dos listas se reparten lo que queda después de los botones,
   así que se ven iguales de importantes. Con `auto` en las columnas, la que
   tuviera los nombres más largos se comería a la otra.

   Lo responsive de este bloque va AL FINAL del fichero, después de estas reglas:
   una media query no añade especificidad, así que una regla escrita más abajo la
   apaga en silencio. */
.equipo-dos {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 16px;
  align-items: start;
  margin-top: 16px;
}

/* Cada mitad es una superficie normal con su cabecera propia. Alto mínimo y no
   fijo: una columna vacía tiene que seguir siendo una diana de arrastre —si mide
   lo que su texto, soltar encima es apuntar a una línea— y una llena crece. */
.equipo-lado {
  display: flex;
  flex-direction: column;
  min-height: 220px;
}
.equipo-lado > .cabeza {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--hairline);
}
.equipo-lado > .cabeza .t {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
}
.equipo-lado > .cabeza .cuenta {
  font-size: 12.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
}
.equipo-lado > .cuerpo {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  flex: 1;
}
/* La columna que va a recibir lo que se suelta. Tinte del material y filete
   completo, **nunca una franja lateral** (Canto, regla 1): el borde se pinta por
   los cuatro lados y es el mismo hairline de siempre, solo que en acento. */
.equipo-lado.recibe {
  border-color: var(--canto);
  background: var(--canto-s);
}

/* Los dos botones del medio, en columna y centrados verticalmente sobre las dos
   listas. `align-self: center` y no `start`: es lo que hace que se lean como el
   puente entre las dos y no como el pie de la primera. */
.equipo-medio {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-self: center;
  max-width: 240px;
}
.equipo-medio .medicion-nota {
  margin: 4px 0 0;
}
/* El botón de meter apunta a la izquierda y el de sacar a la derecha: el glifo de
   volver ya es una flecha a la izquierda, y el de la cruz no es una flecha pero sí
   dice «fuera». Lo que no se hace es girar un glifo con `transform` — un icono
   espejado se lee como otro icono. */

/* ---------------- LA TARJETA DE UNA PERSONA ----------------

   Es un `<button>`: se llega con el tabulador, se elige con Enter y un lector de
   pantalla la anuncia como lo que es. `draggable` encima, que es el atajo con
   ratón.

   Fila de 40px y no de 32: aquí se agarra con el dedo o con el ratón para
   arrastrarla, y la densidad de una tabla de lectura no vale para una diana. */
.persona {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 40px;
  padding: 6px 10px;
  border: 1px solid transparent;
  border-radius: var(--r-ctrl);
  background: none;
  font: inherit;
  color: var(--tinta);
  text-align: left;
  cursor: grab;
}
.persona:hover {
  background: var(--panel);
}
.persona:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: -2px;
}
/* Elegida: tinte y peso, igual que una fila seleccionada de tabla. Y además el
   filete completo en acento, porque aquí la selección tiene consecuencias —el
   botón del medio mueve justo esto— y el tinte solo se pierde en una pantalla con
   sol de agosto. */
.persona.sel {
  background: var(--canto-s);
  border-color: var(--canto);
}
.persona.sel .nom {
  color: var(--canto-h);
  font-weight: 600;
}
/* Mientras se arrastra. Sin sombra ni giro: se apaga un poco y ya, que es lo que
   dice «esto es lo que llevas en la mano». */
.persona.arrastrando {
  opacity: 0.4;
}
/* Las iniciales, que V001 genera. Círculo neutro y no en acento: el acento es de
   lo accionable y lo vivo, y unas iniciales no son ninguna de las dos cosas. */
.persona .ini {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--hairline);
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 600;
  color: var(--subtinta);
}
.persona .quien {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 1px;
}
.persona .nom {
  font-weight: 500;
}
/* La segunda línea —en qué otros equipos está, si es jefe, si está de baja—. Se
   recorta con puntos suspensivos y no parte la tarjeta en dos líneas: lo que hay
   que leer de un vistazo es el nombre, y esto es el contexto. */
.persona .sub {
  font-size: 12px;
  color: var(--subtinta);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Por debajo de 900px las tres columnas no caben: las dos listas se apilan y los
   botones se ponen en fila entre ellas. El arrastre sigue funcionando —se suelta
   en la columna de abajo— y los botones siguen siendo el camino de verdad. */
@media (max-width: 900px) {
  /* Solo el mapa incrustado en la tarjeta. El ampliado comparte la clase base,
     pero debe conservar `inset: 0` y llenar todo el diálogo. Esta regla global
     le imponía 280px también a pantalla completa y dejaba media hoja blanca. */
  .equipo-mapa-panel > .equipo-mapa {
    height: 280px;
  }
  .equipo-ubicacion-panel > .cabeza {
    align-items: flex-start;
    flex-direction: column;
  }
  .equipo-dos {
    grid-template-columns: minmax(0, 1fr);
  }
  .equipo-medio {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    max-width: none;
  }
  .equipo-medio .medicion-nota {
    flex: 1 0 100%;
  }
}

/* ============ EL DETALLE DE LA FASE, EN ESTRECHO ============

   Va al final de la hoja a propósito: una media query no añade especificidad, y
   lo que se escriba después la apaga en silencio. Aquí abajo no hay nada que
   pueda pisarla.

   Por debajo de 1100px las dos columnas del detalle dejan de tener sentido —la
   de la información se come el mínimo de 260px y a las acciones les quedan
   trescientos— así que se apilan. Y con ellas se apilan las acciones: en una
   columna el filete vertical no separa nada, se gira y pasa a ser horizontal
   entre celda y celda, con el mismo hueco por los lados que tiene el de arriba
   por los extremos. */
@media (max-width: 1100px) {
  .fase-cuerpo {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }
  /* Apilado, el filete de al lado vuelve a ser el de encima: lo que hay que
     separar es el próximo paso de la información que ahora tiene debajo. */
  .fase-info {
    margin-top: 4px;
    padding-top: 14px;
    border-top: 1px solid var(--hairline);
  }
  .fase-cuerpo.sola .fase-info {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }
  .fase-info::before {
    display: none;
  }
  .fase-acciones .acciones {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0;
  }
  .fase-acciones .accion {
    padding: 14px 0;
  }
  /* Se apagan las cuatro reglas de columnas de arriba: en una sola columna la
     primera de cada fila es la primera de todas, y las demás llevan filete por
     arriba. El `:first-child` de abajo lo vuelve a quitar en la de verdad. */
  .fase-acciones .acciones[data-cols='2'] > .accion:nth-child(2n + 1)::before,
  .fase-acciones .acciones[data-cols='3'] > .accion:nth-child(3n + 1)::before,
  .fase-acciones .acciones[data-cols='4'] > .accion:nth-child(4n + 1)::before {
    display: block;
  }
  .fase-acciones .accion::before {
    /* Horizontal, y sin llegar a los lados: 12px por cada extremo, que es lo
       mismo que hace el filete de la cabecera de la tarjeta. */
    left: 12px;
    right: 12px;
    top: 0;
    bottom: auto;
    width: auto;
    height: 1px;
  }
  /* ---------- `.juntas` NO SE PARTE AQUÍ ----------

     La rejilla de arriba pasa a una columna, y para `.juntas` eso está mal: la
     «o» solo significa algo entre los dos botones, y en columna se queda entre
     dos filas de una lista que ya no es una alternativa. Así que la fila se
     mantiene, y con ella su relleno y su filete vertical — las tres reglas de
     escritorio, repetidas porque una media query no añade especificidad y las de
     arriba ya se han aplicado.

     El filete de la primera celda se apaga con una regla propia y no con la de
     `:first-child` de abajo: esa pierde por especificidad contra la de
     `[data-cols='2']` que acaba de encenderlo, y en fila eso dejaba un filete
     vertical colgando del borde de la tarjeta. */
  .fase-acciones .acciones.juntas .accion {
    padding: 0 13px;
  }
  .fase-acciones .acciones.juntas .accion::before {
    left: 0;
    right: auto;
    top: 4px;
    bottom: 4px;
    width: 1px;
    height: auto;
  }
  /* El relleno de la derecha se queda: es la mitad del hueco donde va la «o», y
     un `padding: 0` a secas lo dejaba en trece píxeles con la letra pegada al
     botón de al lado. Lo que hay que quitar aquí es el de la izquierda —cuelga
     del borde de la tarjeta— y el de arriba que pone la regla de una columna. */
  .fase-acciones .acciones.juntas > .accion:first-child {
    padding: 0 13px 0 0;
  }
  .fase-acciones .acciones.juntas > .accion:first-child::before,
  .fase-acciones .acciones.juntas .accion.con-o::before {
    display: none;
  }
  .fase-acciones .acciones > .accion:first-child {
    padding-top: 0;
  }
  .fase-acciones .acciones > .accion:first-child::before {
    display: none;
  }
  /* En una columna la marcha atrás deja de tener a la derecha con quién
     compartir la fila: pasa debajo del nombre de la fase y se alinea con él,
     porque un botón suelto a la derecha de nada parece perdido. */
  .fase-atras {
    justify-items: start;
    text-align: left;
    max-width: none;
  }
}

/* ---------------- LOS DOS CAMINOS, CUANDO YA NO CABEN EN FILA ----------------

   `.juntas` aguanta en fila hasta aquí (ver el bloque de 1100px). A los 620px ya
   no hay ancho para dos rótulos de veintitantos caracteres ni partiéndolos, así
   que pasa a columna — y ahí la «o» se recoloca en el hueco entre las dos filas,
   centrada, que es el mismo sitio que ocupaba: entre uno y otro.

   `flex-wrap: nowrap` con la dirección en columna, o `wrap` repartiría los dos
   botones en dos columnas laterales, que es justo lo que se acaba de deshacer. */
@media (max-width: 620px) {
  .fase-acciones .acciones.juntas {
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    row-gap: 0;
  }
  .fase-acciones .acciones.juntas .accion {
    padding: 18px 0 0;
    max-width: none;
  }
  .fase-acciones .acciones.juntas > .accion:first-child {
    padding: 0;
  }
  /* El botón ocupa el ancho: dos botones a media caja en una columna estrecha se
     leen como una tabla de dos filas, no como dos caminos. */
  .fase-acciones .acciones.juntas .accion .btn {
    width: 100%;
    justify-content: center;
    text-align: center;
  }
  /* En el MEDIO del hueco de 18px, no en su borde: `top: 0` es el borde de la
     caja de relleno, o sea justo donde acaba el botón de arriba, y allí la letra
     lo toca. La mitad del relleno la deja centrada entre los dos. */
  .fase-acciones .acciones.juntas .accion-o {
    left: 50%;
    top: 9px;
    height: auto;
    transform: translate(-50%, -50%);
  }
}

/* ---------------- LA HOJA DE LA CAMPANA, EN MÓVIL ----------------

   Al final de la hoja por lo de siempre: una media query no añade especificidad,
   así que lo que se escriba después la apaga en silencio. Aquí abajo no hay nada
   que pueda pisarla.

   Y hace falta una regla propia aunque `.hoja` ya tenga la suya: `.hoja.campana-hoja`
   son dos clases —(0,2,0)— y le gana a `.hoja` —(0,1,0)— esté donde esté, media
   query incluida. Sin esto, la hoja seguiría midiendo `100vw - 72px` en un móvil.

   En 390px de ancho no hay «arriba a la derecha» que valga: no cabe un panel de
   620px al lado de nada, así que se comporta como cualquier otra hoja —lo mismo
   que hace `.hoja`, con la misma medida y el mismo margen— y se apoya en el
   borde de arriba, que es de donde viene. `width: auto` y el hueco con `margin`,
   NO `calc(100vw - 16px)`, por la trampa de la barra de desplazamiento que está
   explicada en la regla de `.hoja`. */
@media (max-width: 620px) {
  .hoja.campana-hoja {
    width: auto;
    margin: 8px;
    max-height: calc(100vh - 16px);
    max-height: calc(100dvh - 16px);
  }
}

/* ---------------- LAS TRES COLUMNAS DE LA PORTADA, AL ESTRECHARSE ----------------

   Al final de la hoja por lo de siempre: una media query no añade especificidad,
   así que lo que se escriba después la apaga en silencio.

   `#v-panel .cifras` son un id más una clase —(1,1,0)— así que estas reglas tienen
   que llevar el id también: escritas como `.cifras` a secas perderían contra la
   regla de arriba estén donde estén, media query incluida. Es la misma trampa que
   ya está explicada en `.hoja.campana-hoja`.

   Los dos escalones salen del ancho de la tarjeta y no de un número redondo: una
   `.cifra` necesita ~240px para que «−199 726,89 €» a 24px no se salga de la caja
   (ver la regla de `.cifras`). Con el carril lateral desplegado, tres columnas
   dejan de llegar a esa medida alrededor de los 1100px, y dos alrededor de los
   680. Por debajo, una por fila. */
@media (max-width: 1100px) {
  #v-panel .cifras {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 680px) {
  #v-panel .cifras {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* =========================================================================
   EL DOCUMENTO DE LA MEDICIÓN

   El resultado de una visita, formateado para tenerlo delante mientras se pasa
   a la PrefWeb y para imprimirlo. La forma está tomada de los presupuestos de
   Incerco —el papel con el que se trabaja hoy— pero solo el ORDEN DE LECTURA:
   dos cajas de cabecera, una banda por posición, y el detalle debajo con
   rótulos en gris y cifras tabulares. Ni el naranja de sus bandas, ni el marco
   de la ficha técnica, ni el dibujo acotado.

   Las cinco reglas de Canto, y dónde se cumplen aquí:

     1. **Ni un `border-left`.** Las bandas de posición se tiñen de `--panel`
        —gris de superficie, no color— y sus filetes son `1px solid
        var(--hairline)` por los cuatro lados. Las dos cajas de la cabecera se
        separan con el `gap` de 1px dejando ver el fondo, que es el mismo truco
        de `.resumen` y existe justamente para no escribir un borde por casilla.
     2. y 3. **Ni una gota de acento**, y no por olvido: en este papel no hay
        nada que pulsar ni nada vivo. El verde saldría solo en el foco de los
        dos botones de arriba, que son de la aplicación y no del documento.
     4. El único estado que sale —fuera de escuadra— es una `.est` con glifo y
        texto, así que se lee igual impreso en blanco y negro.
     5. Cifras tabulares en todo lo que es una cota. La densidad aquí es la del
        papel y no la de la tabla de 32px: esto se lee de arriba abajo una vez,
        no se barre buscando una fila.
   ========================================================================= */

/* La barra de la aplicación, encima del papel. `.btn.volver` trae 16px de
   margen inferior para separarse de la ficha que lleva debajo; aquí la
   separación la pone la barra, así que se le quita para que los dos botones
   queden a la misma altura. */
.doc-barra {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.doc-barra .btn.volver {
  margin-bottom: 0;
}

/* El papel. `max-width` en la medida de un A4 a 96 ppp (210 mm ≈ 794px) más el
   relleno: no es decoración, es que lo que se ve en pantalla sea lo que sale
   por la impresora. Sin él, en un monitor ancho las posiciones se estirarían un
   metro y el salto de página no se parecería a nada. */
.doc {
  max-width: 860px;
  background: var(--blanco);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  padding: 28px 32px 24px;
}

/* El documento del pedido y su barra forman una única hoja centrada. */
#v-pedido .doc,
#v-pedido .doc-barra {
  margin-inline: auto;
}
#v-pedido .doc-barra {
  max-width: 860px;
}

/* El título del documento. El filete es de tinta y no de `--hairline`: es la
   línea que separa el título del cuerpo en cualquier papel, y a 1px sigue
   siendo un filete y no una franja de color (regla 1).

   En fila y no en bloque porque el documento del pedido cuelga aquí su píldora
   de estado —«En la recámara», «Enviado»—, que es lo primero que se mira al
   abrirlo. El de la medición trae un solo hijo y le da igual: un flex de un
   elemento se ve como el bloque de antes. */
.doc-tit {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 20px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--tinta);
}
.doc-tit h2 {
  margin: 0;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Las dos cajas de la cabecera: a la izquierda el documento, a la derecha con
   quién es. El reparto no es mitad y mitad porque la izquierda lleva siete
   pares y la derecha dos. */
.doc-cabecera {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 1px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  overflow: hidden;
  margin-bottom: 22px;
}
.doc-caja {
  background: var(--blanco);
  padding: 14px 16px;
}
.doc-caja h3 {
  margin: 0 0 10px;
  font-size: 15px;
  font-weight: 600;
}

/* El par rótulo–valor. En dos carriles y no con `space-between`, por lo mismo
   que `.ficha .dato`: separados por el ancho de la caja hay que barrer la línea
   entera para emparejar cada rótulo con su cifra. */
.doc-par {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  gap: 0 14px;
  font-size: 12.5px;
  padding: 3px 0;
}
.doc-par .k {
  color: var(--subtinta);
}
.doc-par .v {
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* Lo que se mira antes de empezar a teclear: cuántas posiciones hay y cuántas
   traen algo raro. */
.doc-recuento {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  font-size: 12.5px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
}

.doc-pie {
  margin-top: 22px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
  font-size: 11.5px;
  color: var(--subtinta);
}
.doc-pie p {
  margin: 0 0 6px;
}
.doc-pie p:last-child {
  margin-bottom: 0;
}

/* =========================================================================
   EL DOCUMENTO DE UN PEDIDO

   Reutiliza el armazón de arriba —`.doc`, `.doc-tit`, `.doc-cabecera`,
   `.doc-caja`, `.doc-par`, `.doc-recuento`, `.doc-pie`— porque es el mismo
   papel con otro contenido: si un día dejan de parecerse, lo que ha pasado es
   que uno de los dos se ha ido de la maqueta. Lo de aquí abajo es solo lo que
   el pedido tiene y la medición no: el membrete de la casa, la tabla de líneas
   y los importes.
   ========================================================================= */

/* ---------- EL MEMBRETE ----------

   Dos columnas: el emisor a la izquierda y el sello del documento a la derecha,
   que es el reparto del PDF. `align-items: start` para que el sello quede a la
   altura del nombre de la empresa y no centrado sobre tres renglones de señas. */
.doc-membrete {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.doc-emisor {
  min-width: 0;
}
.doc-emisor-nom {
  margin: 0 0 4px;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
/* Las señas van en gris y pequeñas: son necesarias y no son lo que se viene a
   leer. Mismo criterio —y mismo orden— que el `cabecera()` del PDF. */
.doc-senas {
  margin: 0;
  font-size: 12px;
  color: var(--subtinta);
  overflow-wrap: anywhere;
}
/* El sello no se estruja por debajo de lo que mide un número de pedido con su
   rótulo al lado; a partir de ahí baja a su propia línea (`flex-wrap` arriba). */
.doc-sello {
  min-width: 240px;
  flex: 0 1 auto;
}

/* ---------- LA CABECERA DEL PEDIDO ----------

   `iguales` reparte las dos cajas a mitades, al revés que en el documento de la
   medición: allí la izquierda lleva siete pares y la derecha dos, y aquí las dos
   llevan cinco. `ancha` es la caja de la obra, que cruza las dos columnas y se
   lleva de balde el filete de arriba, porque el hueco de 1px de la rejilla ya
   está pintado del color del hairline. */
.doc-cabecera.iguales {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.doc-caja.ancha {
  grid-column: 1 / -1;
}

/* ---------- LA TABLA DE LÍNEAS ----------

   Es la única tabla de la casa que no sale de `tabla()` de `util.js`, y por eso
   deshace tres cosas de `.tabla` que aquí estorban:

     · **La zebra.** DIS-002 la prohíbe y el 9-8-2026 se aceptó a propósito para
       las tablas de trabajo, donde la vista recorre treinta filas buscando una.
       Un pedido tiene ocho y se imprime: la banda gris no ayuda a leerlo y sí
       gasta tóner. Lo que separa las filas aquí es el filete, como en el PDF.
     · **El alto fijo y el `nowrap`.** Cada línea lleva dos cosas colgando —la
       referencia del catálogo bajo la descripción, el acabado bajo las cotas— y
       una descripción larga se envuelve en vez de ensanchar la tabla: en una
       pantalla la salida de un texto largo es a lo ancho, pero en un papel no
       hay a lo ancho.
     · **El hover.** No se pulsa ninguna fila. Un fondo que reacciona al ratón
       promete algo que no pasa. */
.doc-lineas {
  margin-bottom: 4px;
}
.doc-lineas thead th {
  background: var(--panel);
  border-bottom: 1px solid var(--tinta);
}
.doc-lineas td {
  height: auto;
  padding: 8px 12px;
  white-space: normal;
  vertical-align: top;
  border-bottom: 1px solid var(--hairline);
  /* Una línea partida entre dos hojas deja la cantidad en una y la cota en la
     otra, que es el error que este papel viene a evitar. */
  break-inside: avoid;
}
.doc-lineas tbody tr:nth-child(even):not(.act) td,
.doc-lineas tbody tr:not(.act):hover td {
  background: none;
}
/* El número de posición, en gris y estrecho: ordena la lectura y no es un dato
   que se copie a ningún sitio. */
.doc-lineas .doc-n {
  width: 34px;
  color: var(--subtinta);
}
/* La unidad va pegada a su cantidad —son una sola cifra leída— así que no lleva
   el aire de una columna propia por la izquierda. */
.doc-lineas .doc-ud {
  padding-left: 0;
  color: var(--subtinta);
  white-space: nowrap;
}
.doc-lineas td .sub {
  display: block;
  margin-top: 2px;
}

/* ================= EL ACABADO: LA ÚNICA COLUMNA QUE SE EDITA (V042) ==========

   La columna que dice con qué hay que fabricar la línea. Es la única del papel
   que se puede tocar, y por eso es la única que lleva un control dentro.

   El botón no lleva color de acento, y es deliberado: la regla 2 de Canto lo
   reserva para la acción principal, y aquí hay uno por fila. Lo que lo distingue
   de una celda cualquiera es el filete que sale al pasar por encima y el foco.
   Un verde repetido dieciocho veces en un papel que se imprime deja de señalar
   nada. */
.doc-acabado {
  min-width: 130px;
}
.acabado-btn {
  all: unset;
  box-sizing: border-box;
  display: flex;
  align-items: flex-start;
  gap: 6px;
  width: 100%;
  padding: 2px 4px;
  margin: -2px -4px;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: var(--r-ctrl);
}
.acabado-btn > .ico {
  flex: 0 0 auto;
  width: 13px;
  height: 13px;
  margin-top: 1px;
  margin-left: auto;
  color: var(--subtinta);
  opacity: 0;
}
.acabado-btn:hover {
  border-color: var(--hairline);
}
.acabado-btn:hover > .ico {
  opacity: 1;
}
.acabado-btn:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 1px;
}
/* La fila que algún aviso señala. Ámbar y NO rojo: aquí no se ha roto nada y el
   pedido se manda igual —veinte marcos blancos y uno bronce puede ser lo que el
   cliente encargó—. El rojo de esta casa habla de estado (Canto, regla 3).

   Y el color no va solo (regla 4 y FE-01): la frase del bloque de arriba dice
   cuántos acabados hay y en qué se salen, así que el fondo solo lleva el ojo a
   la fila que la frase ya nombra. Sin el bloque, esto sería color y nada más. */
.doc-lineas tr.ojo td {
  background: var(--ambar-s);
}
/* El filete del botón cuando su fila está señalada: el ámbar de Canto, no un
   tono inventado. Es lo que hace que se vea que ESA celda es la que hay que
   mirar, dentro de una fila que ya está en ámbar. */
.acabado-btn.ojo {
  border-color: var(--ambar);
}
/* Al imprimir no se señala nada, y no es que la señal se pierda: es que ya no
   viene a cuento. Lo que sale por la impresora es el papel del proveedor, y lo
   que en él tiene que estar es el acabado —que sí se imprime, en su columna—.
   La anomalía es una nota de la oficina para decidir antes de mandarlo, y el
   bloque que la explica tampoco se imprime: un fondo señalando una fila sin la
   frase que dice por qué sería color y nada más (Canto, regla 4). */
@media print {
  .doc-lineas tr.ojo td {
    background: none;
  }
  .acabado-btn > .ico {
    display: none;
  }
}

/* --------- LO QUE HAY QUE MIRAR ANTES DE MANDARLO ---------

   Va entre el recuento y la tabla, que es el único sitio por el que se pasa sin
   poder saltárselo. No se imprime: es una nota de la oficina para decidir, y en
   el papel del proveedor no pinta nada. */
.doc-avisos {
  margin: 10px 0 4px;
}
.doc-avisos .k {
  margin-bottom: 6px;
}
.doc-avisos .nota-aviso {
  margin-bottom: 6px;
}
@media print {
  .doc-avisos {
    display: none;
  }
}

/* ---------- LO ÚNICO PULSABLE DEL PAPEL: EL CÓDIGO DE LA OBRA ----------

   `.celda-btn` está declarado bajo `.tabla`, así que fuera de una tabla no lo
   alcanza y el navegador pintaría su botón gris de fábrica en medio del
   documento. Aquí se repite lo mismo —`all: unset` y el foco de Canto— y nada
   más: el peso lo pone `.cod`, que ya es el de un código en esta casa.

   El subrayado al pasar por encima es lo que lo distingue del texto de al lado.
   No lleva color: la regla 2 de Canto reserva el acento del material para lo
   accionable, y en un papel que se imprime un código en verde sería el único
   color de la hoja. */
.doc-ir {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.doc-ir:hover .cod {
  text-decoration: underline;
}
.doc-ir:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 2px;
  border-radius: 3px;
}
/* ---------- LOS TRES IMPORTES ----------

   A la derecha y en una columna estrecha, que es donde se busca un total. Los
   pares se estrechan a `max-content` para que el rótulo y la cifra queden
   pegados en vez de separados por el ancho del papel. */
.doc-totales {
  display: flex;
  flex-direction: column;
  align-items: end;
  margin: 12px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--tinta);
}
.doc-totales .doc-par {
  grid-template-columns: max-content minmax(0, max-content);
  gap: 0 20px;
}
.doc-totales .doc-par:last-child {
  font-size: 14px;
}

/* ---------- LA NOTA Y LA HISTORIA ----------

   Las dos son bloques de pie, y la diferencia entre ellas es de quién es cada
   texto: la nota se la lee el PROVEEDOR (va en el PDF, V038) y la historia es de
   la casa. Se separan con un filete y no con una caja, porque una caja alrededor
   de cada bloque es el adorno que Canto no quiere. */
.doc-nota,
.doc-historia {
  margin-top: 18px;
  padding-top: 12px;
  border-top: 1px solid var(--hairline);
}
.doc-nota p:last-child {
  margin: 6px 0 0;
  font-size: 13px;
  overflow-wrap: anywhere;
}
.doc-historia .doc-par {
  grid-template-columns: minmax(0, 150px) minmax(0, 1fr);
}

/* Aquí vivía `.ficha .tras-tabla`, la separación de la salida que va debajo de
   una tabla cuando la tabla está en la columna estrecha de una ficha y no dentro
   de una `.sup`. Existía por un solo botón —«Ver documento de medición», al pie
   de las visitas a medir— y el 2 sep. 2026 ese botón subió a la fila de acciones
   de la fase, así que la regla se quedó sin nadie a quien aplicarse. El único
   `tras-tabla` que queda es el del panel, y a ése lo alcanza la regla general
   (`.sup > .cuerpo > .tras-tabla`).

   Se anota en vez de borrarse a secas porque el hueco vuelve en cuanto alguien
   ponga una salida debajo de una tabla de una ficha: la regla general no llega
   ahí, y sin esto el botón se pega a la última fila y se lee como una fila más. */

/* ---------------- EL DOCUMENTO EN PANTALLA ESTRECHA ----------------

   Al final de la hoja por lo de siempre: una media query no añade
   especificidad, así que lo que se escriba después la apaga en silencio.

   Un solo escalón y sale de las dos rejillas del papel, no de un número
   redondo: por debajo de ~760px de contenido, las dos cajas de la cabecera
   dejan de tener sitio para su rótulo y su valor en la misma línea. */
@media (max-width: 900px) {
  .doc {
    padding: 20px 18px 18px;
  }
  .doc-cabecera,
  .doc-cabecera.iguales {
    grid-template-columns: minmax(0, 1fr);
  }
  /* El sello del pedido deja de tener suelo propio: con el membrete apilado, un
     mínimo de 240px lo obliga a desbordar en una pantalla de 390. */
  .doc-sello {
    min-width: 0;
  }
}

/* =========================================================================
   IMPRIMIR

   La única pantalla de la casa que se imprime es el documento de la medición,
   y el PDF que Orenga se lleva a la PrefWeb sale de aquí: `window.print()` con
   «Guardar como PDF». Por eso esta hoja no intenta imprimir la aplicación
   entera — se queda el papel y se va todo lo demás.

   Tres cosas que no son obvias:

     · **El lateral y la cabecera se esconden con `display: none`**, no con
       `visibility`: con `visibility: hidden` el hueco se queda y la primera
       página sale con 216px en blanco a la izquierda.
     · **`.app` es un grid de dos columnas.** Sin devolverlo a una sola, el
       papel se imprimiría en la columna derecha de una rejilla cuya primera
       columna ya no tiene nada dentro.
     · **`print-color-adjust`** en las bandas de posición y en la cabecera: el
       navegador quita los fondos al imprimir por defecto, y sin ellas las
       posiciones se quedan sin la banda que las separa. Son grises de
       superficie, así que gastan tinta de sobra y ganan mucho.
   ========================================================================= */

/* La hoja del sentinel en pantalla estrecha. Hace falta una regla propia aunque
   `.hoja` ya tenga la suya, por lo mismo que la de la campana:
   `.hoja.sentinel-hoja` son dos clases —(0,2,0)— y le gana a `.hoja` —(0,1,0)—
   esté donde esté, media query incluida.

   En 390px no hay «debajo del robot» que valga: se comporta como cualquier otra
   hoja. `width: auto` y el hueco con `margin`, NO `calc(100vw - 16px)`, por la
   trampa de la barra de desplazamiento explicada en la regla de `.hoja`. */
@media (max-width: 620px) {
  .hoja.sentinel-hoja {
    width: auto;
    margin: 8px;
    max-height: calc(100vh - 16px);
    max-height: calc(100dvh - 16px);
  }
}

/* ==================================================================== */
/* ============ EL REDISEÑO DE LA FICHA DE CLIENTE (28-8-2026) ========= */
/* ==================================================================== */
/*
   Seis piezas pedidas por Raúl, y van todas juntas al final de la hoja **a
   propósito**: una media query no añade especificidad, así que lo que se escriba
   después de las de arriba las apaga en silencio. Cada componente de aquí trae su
   propia media query al final de este bloque, que es la única forma de que este
   corte no se cuele por debajo de las que ya había.

   -------------------------------------------------------------------------- */

/* ============ 1 · LA CABECERA DE PANTALLA ============

   Aquí vivió `.barra-pantalla` del 28-8-2026 al 9-9-2026: la convención de
   Raúl de que un botón de dar de alta va centrado en la página y en la línea
   de la salida, en una fila propia debajo del título de cada pantalla. Sigue
   siendo la misma convención —qué botón, en qué permiso, sin `.chico`— pero
   la fila se fundió con la del título: ver `.cabecera .centro` (arriba del
   todo de esta hoja) y `#cabecera-acciones` en `index.html`. */

/* ============ 3 · LA CIFRA QUE DICE SI VA BIEN O MAL ============

   El verde y el rojo de los KPIs de la ficha (`cifraJuzgada` en `util.js`).

   **Nunca es sólo el color** (regla 4 de Canto, FE-01): lleva glifo delante —el
   de conforme o el de aviso, los mismos dos que `margenPildora`— y el motivo
   escrito en el `title`. Bajo daltonismo rojo-verde estos dos son el mismo gris,
   y sin el glifo la cifra no diría nada.

   Sin fondo, a diferencia de `.tend`: esto no es una píldora, es **la cifra
   misma**, y meterla en una cápsula de color la convertiría en una etiqueta de
   estado al lado de otras cifras desnudas. Lo que cambia es la tinta.

   Y son `--verde` y `--rojo`, los semánticos. El acento del material es para lo
   accionable y lo vivo (regla 2), no para juzgar. */
.cif-juicio {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.cif-juicio.bien {
  color: var(--verde);
}
.cif-juicio.mal {
  color: var(--rojo);
}
/* En `em`, igual que en `.tend`: el glifo crece con el número en vez de quedarse
   de adorno debajo de una cifra de 20px. */
.cif-juicio .ico {
  width: 0.62em;
  height: 0.62em;
  flex: none;
}

/* ============ 4 · EL GRÁFICO QUE SE ABRE ============

   La tarjeta «Lo que compra» entera es un botón: «al pulsarlo se hará popup».

   Tres cosas que no son obvias:

     · **Hereda tipografía y alineación.** Un `<button>` centra su contenido y usa
       la fuente del sistema, así que sin esto el resumen de dentro saldría
       centrado y con otra letra que el resto de la ficha.
     · **`display: contents` no vale.** Haría desaparecer la caja del botón y con
       ella el área pulsable. Es un `block` que ocupa el cuerpo entero.
     · **La lupa está siempre**, no aparece en el hover: esconder acciones en el
       hover es el prohibido nº 6 de DIS-002, y en una pantalla táctil no hay
       hover que valga. Lo único que cambia con el ratón es el fondo. */
.grafico-abrir {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  border-radius: var(--r-ctrl);
}
@media (hover: hover) {
  .grafico-abrir:hover {
    background: var(--panel);
  }
}
/* La pista de que esto se abre. En el sitio donde se busca una acción —arriba a
   la derecha del cuerpo— y en cuerpo pequeño: es una etiqueta, no un control
   aparte, porque el control es la tarjeta entera. */
.grafico-abrir .grafico-lupa {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 5px;
  font-size: 11.5px;
  color: var(--subtinta);
  margin-bottom: 8px;
}
.grafico-abrir .grafico-lupa .ico {
  width: 13px;
  height: 13px;
}
/* ---------- EL RESUMEN DE ESTA TARJETA VA A UNA COLUMNA ----------

   `.resumen` es `auto-fit minmax(140px, 1fr)`, o sea dos casillas por fila en una
   tarjeta de 396px. Y dos no caben: medido en el banco, la casilla queda en 196px
   y la píldora de la variación —«↑ +41.050,25 €» a 24px— pide 172 más los 26 del
   relleno, o sea 198. Se cortaba por dos píxeles, y una cifra cortada se lee como
   otra cifra: «+41.050,2».

   Es la misma trampa que `.cifras` documenta arriba —«240px y no 150: a 24px
   «−199 726,89 €» pide ~200px de ancho útil»— y aquí muerde en una rejilla más
   estrecha. Se arregla dándoles la fila entera a cada una, que en 364px sobra.
   No se arregla encogiendo el número: el cuerpo de la variación es una decisión
   tomada y escrita en `.tend`. */
.grafico-abrir .resumen {
  grid-template-columns: 1fr;
}

/* Dentro de la tarjeta el gráfico no crece con el cuerpo como en las demás
   superficies: aquí el cuerpo lo llena el botón, y `.cols` con `flex: 1 1 auto`
   dentro de un bloque normal se queda en su mínimo igualmente. Se le da un alto
   fijo y suficiente. */
.grafico-abrir .cols {
  height: 150px;
  min-height: 150px;
  flex: none;
}

/* ============ 5 · EL GRÁFICO EN GRANDE ============

   Dentro de `#d-compras`. **Es el mismo `columnasMes` que la tarjeta**, sólo que
   con sitio: dos copias del dibujo es exactamente como acaban con dos alturas
   distintas para el mismo mes, que es lo que su cabecera avisa.

   El alto sale de la ventana y no de un número fijo: con 24 columnas y 300px las
   barras cortas se quedan en la raya de 3px del mínimo. `min()` con `vh` para que
   en un portátil bajo la hoja no se salga de la pantalla. */
.grafico-grande .cols {
  height: min(46vh, 420px);
  min-height: 260px;
  flex: none;
  margin-top: 14px;
  gap: 6px;
}
/* Con sitio, la etiqueta de cada columna puede ir al cuerpo del texto normal en
   vez de a los 10,5px de una tarjeta de 320px. Es la mitad de la razón de abrir
   esto en grande: aquí los céntimos caben. */
.grafico-grande .col .v {
  font-size: 12px;
}
.grafico-grande .col .m {
  font-size: 12px;
}

/* El hueco que le hace falta al pie de una tarjeta.

   `class="acciones"` a secas —el que usan las diez tarjetas que cierran con un
   botón— **no tenía ninguna regla**: la que hay es `.ficha .acciones`, y este va
   detrás de una tabla, fuera de un `.ficha`, así que el botón quedaba pegado al
   filete de la última fila y parecía una celda más de la tabla.

   Se acota a `.sup > .cuerpo` para no pisar `.ficha .acciones` ni las de una hoja,
   que ya tienen las suyas. */
.sup > .cuerpo > .acciones {
  margin-top: 14px;
}
/* ============ Y LO RESPONSIVE DE LAS SEIS ============

   Va aquí abajo, detrás de sus propias reglas base, por lo de siempre: una media
   query no añade especificidad y lo que se escriba después la apaga. */

/* La cabecera con el acto dentro no cabe en una fila a partir de aquí: título,
   acto y campana piden ~500px sin contar el subtítulo. Se apila —cada uno ya
   se va a su propia línea por el `flex-wrap` de `.cabecera`— y el acto se
   estira al ancho del bloque: un botón centrado sobre una columna de 390px ya
   está centrado. */
@media (max-width: 860px) {
  /* Dos columnas —título y campana— y el acto en una fila entera debajo. Las tres
     posiciones van escritas porque el reparto automático, con el acto ocupando la
     fila, mandaría la campana a una tercera. */
  .cabecera {
    grid-template-columns: 1fr auto;
  }
  .cabecera > :first-child {
    grid-area: 1 / 1;
  }
  .cabecera > .der {
    grid-area: 1 / 2;
  }
  .cabecera .centro {
    grid-area: 2 / 1 / 3 / -1;
    width: 100%;
  }
  /* Instaladores es la única pantalla con dos altas: que se repartan en dos
     filas en vez de estrujarse, como cualquier otro grupo que algún día lleve
     más de un botón. */
  .cabecera .centro > .acciones-de {
    width: 100%;
    flex-wrap: wrap;
  }
  .cabecera .centro .btn {
    flex: 1 1 auto;
  }
}
/* El índice de Ajustes deja de ser columna: a 390px un carril de 190px se come
   media pantalla. Pasa a una tira de rótulos arriba, en el flujo y SIN pegarse —
   el lateral ya es una barra `sticky` clavada en ese mismo borde, y dos cosas
   pegadas al mismo sitio se tapan. Y por esa barra, el aire que se deja al saltar
   a una sección es su alto y no los 16px del escritorio. */
@media (max-width: 860px) {
  .ajustes-pagina {
    display: block;
  }
  .ajustes-indice {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 14px;
  }
  /* En fila y sin el carril que los alineaba, los rótulos necesitan su propio
     filete para leerse como piezas sueltas y no como un renglón de texto. */
  .ajustes-indice-it {
    border: 1px solid var(--hairline);
  }
  .ajustes-secciones > [data-indice] {
    scroll-margin-top: 64px;
  }
}
@media (max-width: 620px) {
  /* El gráfico grande, en un móvil, con el alto de la tarjeta: `46vh` sobre una
     pantalla apaisada de 390px deja barras de 150px con la hoja desbordada. */
  .grafico-grande .cols {
    height: 240px;
    min-height: 240px;
  }
}

@media print {
  html,
  body {
    background: var(--blanco);
  }
  /* El armazón de la aplicación no se imprime. Los dos que no son obvios y que
     salieron impresos en la primera prueba: `.nota-aviso` —el cartel de «se ha
     caído la sesión», que en un papel no significa nada— y `.plegar`, la flecha
     del carril, que sin carril es una flecha suelta. */
  .lateral,
  .cabecera,
  .nota-aviso,
  .plegar,
  .doc-barra,
  dialog {
    display: none !important;
  }
  .app {
    display: block;
  }
  /* El lienzo es la columna 2 de la rejilla y trae el margen de la página; con
     `.app` en bloque ya no hay columna 2, y el margen lo pone `@page`. */
  .lienzo {
    grid-column: auto;
    padding: 0;
  }
  /* El papel deja de ser una tarjeta: el marco y el redondeo los pone la hoja
     de verdad, y una caja dibujada dentro de otra caja es un marco de más. */
  .doc {
    max-width: none;
    border: 0;
    border-radius: 0;
    padding: 0;
  }
  .doc-cabecera,
  .doc-lineas thead th {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }
  /* La cabecera de la tabla del pedido se repite en cada hoja, que es lo que
     hace legible la segunda: ocho líneas de cifras sin rótulos encima no dicen
     cuál es la cantidad y cuál el importe. Es lo que el PDF hace con
     `cabeceraDeTabla` en cada página nueva. */
  .doc-lineas thead {
    display: table-header-group;
  }
  .doc-lineas thead th {
    position: static;
  }
  /* Las dos columnas se recuperan, y esta es la razón por la que el bloque de
     imprimir va DESPUÉS del responsive y no antes: el papel de la impresora mide
     unos 700px de contenido útil, así que el escalón de 900px se cumple y la
     media query de arriba había apilado la cabecera y los campos de cada
     posición. El resultado eran dos hojas para cuatro ventanas.

     No es una excepción al escalón: ahí el motivo era que en un móvil no caben
     dos carriles, y en un A4 sí caben —es exactamente el ancho para el que está
     escrito el documento—. */
  .doc-cabecera,
  .doc-cabecera.iguales {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* El sello vuelve a tener suelo propio: en un A4 hay ancho para el membrete y
     su número al lado, y la media query de los 900 se cumple al imprimir. */
  .doc-sello {
    min-width: 240px;
  }
  /* ---------- APRETAR, QUE EL PAPEL SE PAGA POR HOJAS ----------

     En pantalla los huecos son de lectura; aquí son hojas. La primera prueba
     sacó DOS páginas para cuatro ventanas, y la segunda no llevaba más que el
     pie: sesenta píxeles de separaciones. Todo lo de abajo es la misma medida
     un escalón más corta, sin cambiar ni un tamaño de letra — lo que se aprieta
     es el aire, no lo que hay que leer. */
  .doc-tit {
    margin-bottom: 14px;
    padding-bottom: 10px;
  }
  .doc-cabecera {
    margin-bottom: 14px;
  }
  .doc-caja {
    padding: 12px 14px;
  }
  .doc-recuento {
    margin-bottom: 8px;
  }
  .doc-pie {
    margin-top: 14px;
    padding-top: 10px;
  }
  .doc-membrete {
    margin-bottom: 12px;
  }
  .doc-lineas td {
    padding: 5px 12px;
  }
  .doc-nota,
  .doc-historia {
    margin-top: 12px;
    padding-top: 9px;
  }
  @page {
    margin: 14mm;
  }
}

/* ==================== LA PUESTA EN MARCHA DE LA CASA ====================

   La lista de lo que hay que rellenar para poder trabajar, arriba del todo en la
   portada. Petición de Raúl del 9-9-2026.

   Vacío no ocupa: el hueco existe siempre en el HTML del panel para que su
   petición —que llega después— tenga dónde pintar, y una casa configurada no
   tiene por qué pagar un renglón en blanco en la pantalla que más se mira. */
#panel-arranque:empty {
  display: none;
}
#panel-arranque {
  margin-bottom: 14px;
}

/* El borde entero y no una franja lateral: los cuatro lados iguales, como todo
   en esta casa. Que frena se dice con el borde rojo Y con el cartel de dentro,
   nunca solo con el color. */
.sup.arranque.frena {
  border-color: #f0c4c0;
}
.arranque-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.arranque-paso {
  display: grid;
  grid-template-columns: 92px 1fr auto auto;
  align-items: center;
  gap: 0 14px;
  padding: 9px 0;
  border-bottom: 1px solid var(--hairline);
}
.arranque-paso:last-child {
  border-bottom: 0;
}
.arranque-paso:first-child {
  padding-top: 0;
}

/* Glifo y palabra, siempre los dos: el color solo no dice nada a quien no lo
   distingue, y en una lista de ocho renglones es lo único que separa «ya está»
   de «esto frena un trabajo». */
.arranque-paso .mx {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.arranque-paso .mx .ico {
  width: 13px;
  height: 13px;
}
.arranque-paso.bloquea .mx {
  color: var(--rojo);
}
.arranque-paso.conviene .mx {
  color: var(--ambar);
}

.arranque-paso .cuerpo {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.arranque-paso .cuerpo .t {
  font-size: 13px;
  font-weight: 590;
}
.arranque-paso .cuerpo .nota {
  font-size: 11.5px;
  color: var(--subtinta);
}
.arranque-paso .cuenta {
  font-size: 12px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

@media (max-width: 720px) {
  .arranque-paso {
    grid-template-columns: 1fr auto;
    gap: 4px 10px;
  }
  .arranque-paso .mx {
    grid-column: 1 / -1;
  }
}

/* ==================== EL CALENDARIO DE LA MEDICIÓN ====================

   El mes entero para elegir el día, y el día abierto con sus tres franjas
   (petición de Raúl del 9-9-2026). Sustituye a dos cosas a la vez: la rejilla de
   la semana con una pastilla por persona libre, y el formulario de «Otra fecha»
   que preguntaba lo mismo otra vez con un campo de fecha, tres botones y un
   desplegable. Preguntaban lo mismo por dos caminos, y el de arriba solo llegaba
   hasta donde llegaba la ventana de huecos del servidor. */
.agenda {
  border: 1px solid var(--hairline);
  border-radius: var(--r-sup);
  background: var(--blanco);
  overflow: hidden;
}
.agenda-cab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--hairline);
}
.agenda-cab .rango {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* --- el mes ---

   Siete columnas iguales con `table-layout: fixed`: el ancho de una columna no
   puede depender de si ese día lleva un 7 o un 28, que es lo que descuadraría la
   rejilla al pasar de mes. */
.agenda-rejilla table {
  border-collapse: collapse;
  width: 100%;
  table-layout: fixed;
}
.agenda-rejilla thead th {
  padding: 8px 0 4px;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--subtinta);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
/* El subrayado de puntos de `<abbr>` sobre «lun» parece un enlace. El nombre
   entero sigue en el `title` y en el árbol de accesibilidad. */
.agenda-rejilla thead abbr {
  text-decoration: none;
}
.agenda-rejilla td {
  padding: 2px;
}

/* Un día. Número tabular y nada más dentro: lo que se elige aquí es la fecha, y
   cualquier adorno competiría con el número. */
.mes-dia {
  display: block;
  width: 100%;
  border: 1px solid transparent;
  border-radius: var(--r-ctrl);
  background: none;
  padding: 9px 0;
  font: inherit;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--tinta);
  text-align: center;
  cursor: pointer;
}
.mes-dia:hover {
  border-color: var(--canto);
  color: var(--canto-h);
}
.mes-dia:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 1px;
}
/* Los días del mes de al lado y los fines de semana **se pulsan igual**: aquí se
   agenda el sábado que pidió el cliente y se corrige lo que se dio ayer por
   teléfono. Se dicen más flojos, que es «este no es el sitio habitual», no «esto
   no se puede». */
.mes-dia.fuera {
  color: var(--subtinta);
}
.mes-dia.finde {
  background: var(--panel);
}
/* Hoy, en el acento y sin fondo: el fondo es del día ELEGIDO, y las dos cosas
   tienen que poder verse a la vez sin confundirse. */
.mes-dia.hoy {
  color: var(--canto);
  font-weight: 700;
}
/* Elegido: el mismo marcado que `.btn.sec.on`, que es como esta casa dice «esto
   está pulsado». Va el último para ganar a hoy y al fin de semana. */
.mes-dia.on {
  border-color: var(--canto);
  background: var(--canto-s);
  color: var(--canto-h);
  font-weight: 600;
}

/* En la instalación solo se pueden elegir las fechas que devuelve el motor. */
.mes-dia:disabled {
  cursor: default;
  opacity: 0.38;
}
.mes-dia:disabled:hover {
  border-color: transparent;
  color: inherit;
}
.mes-dia.inst-disponible:not(.on) {
  border-color: var(--canto);
  color: var(--canto-h);
  font-weight: 600;
}

/* --- el mes holgado: el de agendar la instalación ---

   La misma rejilla, con la casilla lo bastante alta para que quepa una palabra
   debajo del número. Es una variante y no otro calendario: el de la medición
   sigue siendo compacto porque allí TODOS los días se pueden pulsar y no hay
   nada que distinguir; aquí solo se pueden pulsar unos pocos y el resaltado por
   sí solo no dice cuáles ni por qué. */
.agenda.holgada .agenda-rejilla td {
  padding: 3px;
}
.agenda.holgada .mes-dia {
  min-height: 52px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 6px 2px;
}
.agenda.holgada .mes-num {
  font-size: 15px;
}
/* Lo que la casilla dice de sí misma. `nowrap` y recortado: si un día no cabe
   la palabra, se corta ella, no la rejilla. */
.mes-pie {
  font-size: 10px;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  max-width: 100%;
}
/* En un móvil la casilla es una séptima parte de 374px: ahí «Disponible» solo
   cabe entera bajando de cuerpo. Misma razón que en `#d-instalacion` para estar
   pegada a lo que corrige y no arriba con las demás reglas de móvil. */
@media (max-width: 620px) {
  .agenda.holgada .mes-dia {
    min-height: 46px;
  }
  .agenda.holgada .mes-num {
    font-size: 14px;
  }
  .mes-pie {
    font-size: 8.5px;
  }
}

/* --- del mes al día ---

   Al pulsar un día, el mes se va y en su sitio entran las franjas y quién va. Es
   un cambio de pantalla entero dentro de la misma hoja, y sin nada entre medias
   quien pulsó no sabe si ha llegado a donde quería o si se le ha movido algo por
   debajo. La entrada lo dice: viene de la derecha, que es hacia donde avanza el
   formulario.

   Entra, resuelve y se para: 200ms una vez, no un movimiento que siga ahí. Con
   `prefers-reduced-motion` no se pone nada — lo apaga la regla global del
   principio del fichero.

   El `clip` mientras dura es lo que evita que los 12px de la derecha saquen una
   barra de deslizamiento horizontal en la hoja durante la quinta parte de un
   segundo. `clip` y no `hidden`: no convierte el cuerpo en contenedor de
   desplazamiento y deja en paz lo que hay dentro. */
.entra-dia {
  animation: agenda-entra 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.hoja-cuerpo:has(> .entra-dia) {
  overflow-x: clip;
}
@keyframes agenda-entra {
  from {
    opacity: 0;
    transform: translateX(12px);
  }
}

/* --- los equipos que pueden ir ese día ---

   Una lista de verdad (`<ul>` de `<li>`) y no un montón de cajas: son los
   equipos libres, y quien va con lector de pantalla tiene que poder saber
   cuántos hay antes de recorrerlos. Misma forma que `.franja-casilla` —botón
   ancho, nombre a la izquierda, detalle debajo— porque es la misma pregunta:
   cuál de estos. */
.inst-equipos {
  margin-top: 14px;
}
.inst-equipos-tit {
  margin: 0 0 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--tinta);
}
.inst-equipos-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.equipo-casilla {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  width: 100%;
  text-align: left;
  font: inherit;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--panel);
  padding: 9px 12px;
  cursor: pointer;
}
.equipo-nom {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--tinta);
}
.equipo-gente {
  font-size: 12px;
  color: var(--subtinta);
}
/* Sin nadie en la lista no se apaga el botón: el equipo está operativo —lo dice
   el servidor— y lo que falta es el dato de quién, no la posibilidad de
   mandarlo. Lo que se afloja es el rótulo. Mismo criterio que `.franja-casilla.sin-nadie`. */
.equipo-gente.sin-nadie {
  font-style: italic;
}
.equipo-casilla:hover {
  border-color: var(--canto);
}
.equipo-casilla:hover .equipo-nom {
  color: var(--canto-h);
}
.equipo-casilla:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 1px;
}
.equipo-casilla.on {
  border-color: var(--canto);
  background: var(--canto-s);
}
.equipo-casilla.on .equipo-nom,
.equipo-casilla.on .equipo-gente {
  color: var(--canto-h);
}
/* La píldora del que propone el motor va NEUTRA a propósito: el acento de esta
   hoja es el del día y el del equipo elegido, y un segundo verde compitiendo
   diría que ya está elegido cuando solo está sugerido (Canto, regla 2). */
.equipo-casilla .est {
  background: var(--blanco);
  font-weight: 500;
}

/* --- el día abierto ---

   Las tres franjas una debajo de otra y no en fila: de cada una se lee quién
   queda libre, y dos nombres no caben de lado en el ancho de un tercio. */
.franjas {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px;
}
.franja-casilla {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  text-align: left;
  font: inherit;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  background: var(--panel);
  padding: 10px 12px;
  cursor: pointer;
}
.franja-casilla .k {
  font-size: 13px;
  font-weight: 600;
  color: var(--tinta);
}
.franja-casilla .q {
  font-size: 12px;
  color: var(--subtinta);
  text-align: right;
}
.franja-casilla:hover {
  border-color: var(--canto);
}
.franja-casilla:hover .k {
  color: var(--canto-h);
}
.franja-casilla:focus-visible {
  outline: 2px solid var(--canto);
  outline-offset: 1px;
}
.franja-casilla.on {
  border-color: var(--canto);
  background: var(--canto-s);
}
.franja-casilla.on .k,
.franja-casilla.on .q {
  color: var(--canto-h);
}
/* Sin nadie libre **no se apaga el botón**: doblar a alguien en la misma mañana
   es legal y a veces es lo correcto —dos obras en la misma calle—, y un festivo o
   unas vacaciones dejan el día sin nadie sin que eso impida agendarlo. Lo que se
   afloja es el rótulo, para que la franja que sí tiene gente destaque sobre la
   que no. Quien lo dice de verdad es el «Nadie libre» escrito al lado. */
.franja-casilla.sin-nadie .k {
  color: var(--subtinta);
  font-weight: 500;
}

/* Qué falta por elegir, debajo del calendario, algo más separado que una nota
   suelta: la caja de encima es grande. */
.agenda + .medicion-nota,
.medicion-dia + .medicion-nota {
  margin-top: 12px;
}

/* Lo elegido, dicho entero: el día, la franja y quién van cada uno en su sitio,
   así que sin esto lo escogido solo se sabe recorriendo los tres hacia atrás. */
.agenda-elegido {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  margin: 12px 0 0;
  padding: 10px 12px;
  font-size: 13px;
  border: 1px solid var(--canto);
  background: var(--canto-s);
  color: var(--canto-h);
  border-radius: var(--r-ctrl);
}
.agenda-elegido .ico {
  width: 14px;
  height: 14px;
  color: var(--canto);
  margin-top: 2px;
}

/* El QR del segundo factor. Tamaño fijo y en módulos enteros: un QR escalado a
   un tamaño que no es múltiplo de su cuadrícula sale con los cuadros de distinto
   grosor, y eso es justo lo que hace que la cámara no lo coja a la primera.
   Fondo blanco propio: el SVG lo trae, pero si algún día se sirve recortado, el
   margen de silencio sigue estando. */
.mfa-qr {
  display: block;
  width: 208px;
  height: 208px;
  margin: 12px 0;
  background: #fff;
  border: 1px solid var(--hairline);
  border-radius: var(--r-ctrl);
  image-rendering: pixelated;
}

/* ==================== LA HUELLA O LA CARA ====================

   En la pantalla de entrar: el botón de la llave arriba, su aviso pegado debajo
   —la respuesta de ESE botón, no un cartel de la pantalla— y el rótulo que separa
   las dos puertas. */
.llave-aviso {
  margin-top: 10px;
}
.llave-o {
  margin: 14px 0 12px;
  text-align: center;
  font-size: 12px;
  color: var(--subtinta);
}
/* La lista de equipos en Ajustes: una fila por llave, compacta, con el botón de
   quitar al final de su renglón. */
.llaves-lista {
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
}
.llaves-lista:empty {
  display: none;
}
.llaves-fila {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px 12px;
  padding: 8px 0;
  border-top: 1px solid var(--hairline);
}
.llaves-fila:first-child {
  border-top: 0;
}
.llaves-nombre {
  font-weight: 600;
}
.llaves-uso {
  grid-column: 1;
  font-size: 12px;
  color: var(--subtinta);
  font-variant-numeric: tabular-nums;
}
.llaves-fila > .btn {
  grid-column: 2;
  grid-row: 1 / span 2;
}
.llaves-este {
  margin-left: 6px;
  font-size: 11px;
  font-weight: 400;
  color: var(--subtinta);
}

/* La entrada a la izquierda en pantalla ancha: los logos ocupan la mitad
   derecha (`.entrada-marcas`, en Canto).

   En la oficina, sin el verde de Canto (Raúl, 27-9-2026): la mitad de los logos
   es blanca y los logos van sobre ella, sin placa. */
@media (min-width: 1100px) {
  #p-entrar {
    margin-right: 50%;
  }
  #p-entrar .entrada-marcas {
    background: var(--blanco);
  }
  #p-entrar .entrada-placa {
    padding: 0;
    border-radius: 0;
    background: none;
  }
}

/* En el móvil los logos van encima de la entrada y fuera del flujo, así que el
   formulario se queda donde estaría sin ellos. El relleno de arriba y abajo es
   el sitio de los logos, y va por igual en los dos lados para que el centro de
   la caja no se mueva. */
@media (max-width: 1099.98px) {
  #p-entrar {
    padding-block: 96px;
  }
  #p-entrar .portal-caja {
    position: relative;
  }
  #p-entrar .entrada-marcas {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    margin: 0 0 28px;
  }
}
