  /* Los tokens de COLOR viven en /static/tokens.css (docs/68): una sola fuente
     de verdad compartida con la landing y las guías, vigilada por
     tests/test_tokens.py. Acá solo quedan los de ESPACIADO/TIPOGRAFÍA/RADIO,
     que son de la app y no de marca. */
  :root{
    /* La escala de espaciado. Antes convivían 11/14/16/18/20/22/26px elegidos
       de a uno por pantalla; estos cinco escalones cubren todo lo que había y
       hacen que dos cards distintas respiren igual. */
    --e1:6px; --e2:10px; --e3:14px; --e4:20px; --e5:28px;
    /* La escala de TAMAÑO DE LETRA. Antes había diecinueve tamaños distintos
       --de 8,5 a 34px, elegidos de a uno por pantalla-- y ocho de ellos vivían
       a medio píxel del de al lado: 13 y 13,5 convivían en la misma tabla, 12 y
       12,5 en el mismo renglón de ayuda. Una diferencia que nadie puede ver no
       es jerarquía, es ruido, y obliga a elegir cada vez.
       Son ocho escalones y cada uno es un PAPEL, no un número:
         --t-xs    el rótulo de columna, la píldora, el identificador técnico
         --t-sm    la letra chica: la ayuda, el dato de contexto
         --t-md    el cuerpo: una fila de tabla, un párrafo, un botón chico
         --t-lg    el título de una tarjeta, el botón de página
         --t-h2    el encabezado de una sección
         --t-h1    el título de la pantalla
         --cifra   el número que la pantalla vino a contestar
         --cifra-g el mismo, cuando es el ÚNICO (el neto del dueño) */
    --t-xs:11px; --t-sm:13px; --t-md:14.5px; --t-lg:15px;
    --t-h2:19px; --t-h1:23px; --cifra:27px; --cifra-g:34px;
    /* Y los dos que NO son de la escala, a propósito: la letra del CALENDARIO.
       Esos tamaños no eligen una jerarquía, eligen caber -- la tarifa, el
       min-stay y el glifo de estado viven adentro de una celda de día que a
       31 columnas mide poco más de treinta píxeles. Subirlos a `--t-xs` rompe
       la celda, así que se quedan, pero con nombre. */
    --t-celda:10px; --t-celda-min:9px;
    /* La escala de RADIO. Mismo problema: convivían 2/3/4, 7/8/9/10 y 12/14/16,
       o sea tres grupos de esquinas que se leen igual escritos con nueve
       valores. Cuatro escalones: el filo (una barra, un punto de color), el
       control (un botón chico, un campo), la caja (una card, una tabla) y la
       píldora. */
    --r-xs:3px; --r-sm:8px; --r-md:14px; --r-pill:999px;
    --sans:"Manrope", system-ui, sans-serif;
  }
  *{box-sizing:border-box; margin:0; padding:0}
  body{
    /* Cáscara moderna 21-sep-2026: el fondo de la app es el gris cálido
       --bg-app, no el blanco de las tarjetas -- así las tarjetas (--bg) flotan
       encima con sombra en vez de competir con un hairline. */
    background:var(--bg-app); color:var(--ink); font-family:var(--sans); font-weight:400; line-height:1.5;
    -webkit-font-smoothing:antialiased; min-height:100vh; position:relative;
  }
  /* El contenedor va a TODO el ancho de la pantalla: el tope de 1080px dejaba
     tiras blancas a los lados en cualquier monitor grande y le robaba columnas
     justo a lo que más las necesita -- el calendario de 31 días, la tabla de
     comprobantes, los reportes. Lo único que queda es el aire lateral para que
     el texto no pegue contra el borde. Las pantallas que SÍ quieren una columna
     angosta la piden ellas (`.auth`, `.angosto`), no el marco. */
  .wrap{position:relative; z-index:1; padding:0 32px}
  a{color:var(--terra-text)}
  /* Un solo acento por pantalla (cáscara moderna, ⚖ fundador 21-sep-2026,
     maqueta A §1): los enlaces de ACCIÓN dentro del contenido van en tinta con
     subrayado fino -- el terracota deja de competir con el botón primario y
     queda solo donde ya vivía en docs/68 §4 (logo, barras de progreso, focus,
     `accent-color`). Los botones, badges y tabs no entran: ya tienen su propio
     tratamiento. */
  main a:not(.btn):not(.badge):not(.chips a):not(.fila-ir):not(.fila-ver):not(.accion-pendiente){color:var(--ink); text-decoration:underline;
    text-decoration-color:var(--line-strong); text-underline-offset:3px; text-decoration-thickness:1px}
  main a:not(.btn):not(.badge):not(.chips a):not(.fila-ir):not(.fila-ver):not(.accion-pendiente):hover{text-decoration-color:var(--ink)}
  /* `.accion-pendiente` (⚖ 24-sep-2026, junto con `.fila-ver`): el enlace de
     fila que SÍ pide algo del host -- conectar un canal, completar un vínculo
     -- y por eso se queda en el acento de marca, para no confundirse con el
     chevrón de "solo mirar" de al lado. */
  .accion-pendiente{color:var(--accent); font-weight:600; text-decoration:none}
  .accion-pendiente:hover{text-decoration:underline}
  /* `.fila-ir` es el `<a>` real que hace accesible una `.fila-enlace` (revisión
     de #417): `.fila-enlace` solo agrega `data-href` + un click delegado en
     `nav.js` -- sin JS, o por teclado, o para un lector de pantalla, la fila
     NO tiene ningún elemento focusable, así que el destino tiene que seguir
     siendo un `<a href>` de verdad. Va sin subrayado en reposo (la fila ya se
     lee como fila) y CON subrayado en hover/foco, que es la señal de que ahí
     hay algo que se activa.
     `.fila-ver` entra a la misma excepción (⚖ 24-sep-2026): es el chevrón de
     fila de más abajo, que trae su propio color y no lleva subrayado. */
  .fila-ir{color:inherit; text-decoration:none}
  .fila-ir:hover, .fila-ir:focus-visible{text-decoration:underline;
    text-decoration-color:var(--line-strong); text-underline-offset:3px}
  /* Texto para lector de pantalla que no se ve en la fila (⚖ fundador
     24-sep-2026, junto con `.fila-ver`, más abajo): el patrón de siempre --
     `clip`/`overflow:hidden` en vez de `display:none`, que un lector de
     pantalla sí anuncia. */
  .visually-hidden{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
  }
  /* El chevrón de fila (⚖ fundador 24-sep-2026): el "Abrir →"/"Ver X →" que se
     repetía una vez por fila en las listas y tablas competía por atención con
     lo que sí pide algo del host -- veinte filas con veinte enlaces en la
     tinta del cuerpo no tienen ningún acento, se leen como veinte avisos. Baja
     a un chevrón discreto en --mute; la fila entera avisa que responde
     oscureciéndolo a --ink en su hover, no el enlace suelto. El texto sigue
     accesible -- lector de pantalla y buscador de texto del navegador lo
     siguen viendo -- solo se esconde visualmente (`.visually-hidden`).
     Las acciones que SÍ piden algo del host (completar un dato, conectar un
     canal) no son este patrón y se quedan en --accent donde ya estaban. */
  .fila-ver{
    display:inline-flex; align-items:center; color:var(--mute);
    text-decoration:none; font-size:var(--t-md); transition:color .15s ease;
  }
  .fila-ver::after{content:"\203A"; font-size:1.15em; line-height:1}
  .cmp-fila:hover .fila-ver, tr:hover .fila-ver,
  .fila-ver:hover, .fila-ver:focus-visible{color:var(--ink)}

  /* ---------- nav ---------- */
  /* `nav.top` sobrevive SOLO para las pantallas sin secciones (login, código,
     el recorrido del dueño): ahí no hay sidebar que colgar la marca, y la
     barra de arriba con el logo y las migas es todo el chrome que hay. */
  nav.top{display:flex; align-items:center; gap:var(--e3); padding:var(--e4) 0; border-bottom:1px solid var(--line)}
  /* Salir se va al borde derecho: es lo último de la barra, no lo que sigue a
     las migas. */
  nav.top > .salir{margin-left:auto}
  .brand{display:flex; align-items:center; gap:9px}
  .brand a{display:flex; align-items:center; gap:9px; color:inherit; text-decoration:none}
  .brand svg{flex:none}
  .brand .word{font-weight:400; font-size:var(--t-h2); letter-spacing:-.02em}
  .brand .word b{color:var(--accent); font-weight:400}
  .crumbs{color:var(--mute); font-size:var(--t-md)}
  /* El aviso de la cuenta que mira pero no guarda: una franja sobre el
     contenido, en el mismo tono que las tarjetas, sin rojo -- no es un error,
     es un estado que se destraba pagando. */
  .sin-escritura{margin:0 0 var(--e3); padding:var(--e2) var(--e3); border:1px solid var(--line);
    border-radius:var(--r-sm); background:var(--soft); font-size:var(--t-md)}
  .sin-escritura a{color:var(--terra-text); font-weight:600; text-decoration:none}
  /* La cédula y su "Verificar en Hacienda" en la misma fila; el resultado
     debajo, en el tono de la ayuda. Verde no: el "listo" es una hairline
     terracota como todo lo que confirma en la app. */
  .precheck-fila{display:flex; gap:8px; align-items:center}
  .precheck-fila input{flex:1}
  .precheck-ok{border-left:2px solid var(--terra); padding-left:var(--e2)}
  .precheck-aviso{border-left:2px solid var(--line); padding-left:var(--e2)}
  .crumbs a{color:var(--mute); text-decoration:none}
  .crumbs a:hover{color:var(--terra-text)}

  /* ---------- sidebar ---------- */
  /* La navegación pasó de píldoras horizontales a una columna fija a la
     izquierda (⚖ fundador 25-ago, cierra la duda de `docs/48`). El motivo no es
     estético: las píldoras solo podían mostrar UN árbol a la vez -- el del
     emisor o el operativo -- y por eso la bandeja de mensajes, que vive en el
     otro, era indescubrible. En vertical entran los dos con sus rótulos y sin
     robarle alto a la primera pantalla, que era el otro costo de las píldoras.
     Ancho fijo de 242px: entra "Cierre del período" sin cortarse y deja el
     resto del monitor para el calendario de 31 días y las tablas anchas.
     Las migas NO se absorben: el sidebar dice en qué sección estás, no de qué
     cuenta ni de qué contribuyente cuelga la pantalla, y varias son links de
     vuelta (el detalle de una reserva, la confirmación de un gasto). Quedan
     como una línea fina arriba de la columna de contenido, sin la barra ni el
     borde que tenían cuando compartían fila con el logo. */
  /* `padding-top:60px` es el hueco de la barra superior oscura (`.barra-sup`,
     PR 2 de la cáscara moderna): ella es `position:fixed` y no reserva su
     propio espacio, así que la columna entera tiene que bajar 60px o el
     sidebar y el contenido arrancarían debajo de la barra. */
  .shell{display:flex; align-items:flex-start; padding-top:60px}
  /* El RIEL (PR 3 de la cáscara moderna, `decision-cascara-moderna-21sep`):
     ancho FIJO, SIEMPRE -- no hay plegado que abrir y no hay que esperar a
     901px para que se vea así. Hasta este PR la columna nacía en 242px con
     texto al lado del ícono y un botón la achicaba a un riel de 58px sin
     rótulo; el fundador pidió la variante angosta de entrada, con un rótulo
     chico DEBAJO del ícono (ninguna app comparada la tenía sin texto). Con
     eso el botón de plegar y sus dos anchos dejan de tener sentido: queda un
     solo ancho y fondo BLANCO -- ya no el crema de la columna vieja, que
     ahora es apenas el tinte del hover.

     ⚠ 88px (revisión del fundador sobre la primera versión) se quedaba corto
     para dos líneas de rótulo en 11px sin recortar palabras enteras
     ("Propiedades", "Comprobantes"): 96px daba el aire que faltaba sin
     volverse una columna ancha.

     ⚖ Fundador 24-sep-2026 (revisión de #447, grupos plegables): 96px se
     quedó corto DE NUEVO -- ahora para el ENCABEZADO de grupo, que en
     VERSALITAS ("PROPIEDADES") es más ancho que la misma palabra en la fila
     hoja. El fundador pidió ensanchar el riel en vez de abreviar el
     encabezado.

     Medido en vivo (Chrome, sobre el markup REAL del encabezado -- ícono +
     `.grupo-h-txt` con la flecha adentro, Manrope 700, `letter-spacing:
     .07em`): con 96px de riel (76px de contenido) "PROPIEDADES" parte en dos
     renglones ("PROPIEDAD"/"ES"); a partir de 120px de riel (100px de
     contenido) entra ENTERA con la flecha pegada al lado, en un solo
     renglón. 124px es ese piso más 4px de margen -- para variación de
     métricas de fuente entre navegadores, no para "aire" a propósito -- y es
     apenas 28px más que antes. El resto de las filas (rótulos más cortos,
     `rotulo_riel`) quedan con margen de sobra: es el "respirar" que pidió el
     fundador, no un ancho pensado para ellas -- el número lo fija la palabra
     más larga.

     ⚠ `overflow-y:auto` vivió ACÁ hasta la revisión de #416, y era un bug:
     un eje en `auto` obliga al OTRO eje a resolver también en `auto` (nunca
     quedan mixtos, CSS Overflow §3), así que el `<aside>` quedaba con
     `overflow-x:auto` de rebote -- y el `.pick-menu` de "Cuenta"
     (`position:absolute`, cuelga hacia la DERECHA, más abajo) se sale del
     ancho de 96px a propósito. Con el aside recortando ese eje, el menú
     generaba SCROLL HORIZONTAL adentro de la columna en vez de mostrarse
     por encima del contenido -- en escritorio quedaba invisible; en móvil no
     pasaba porque ahí abre hacia arriba, adentro del propio ancho. El scroll
     VERTICAL que las catorce secciones siguen necesitando se mudó a
     `.riel-secciones` (`_sections_nav.html`), que envuelve SOLO lo
     navegable y no al pie -- así el pie queda fuera del contenedor que
     recorta, y su menú puede desbordar el ancho del riel sin que nada lo
     tape. */
  /* `z-index:20` (bug visto por el fundador tras #416): `position:sticky` NO
     es solo "se pega al hacer scroll" -- crea su PROPIO contexto de
     apilamiento (CSS Positioned Layout §z-index), y ese contexto pinta como
     bloque al nivel que le toque entre sus hermanos. El `.pick-menu` de
     "Cuenta" de acá abajo tiene `z-index:5`, pero ese 5 solo compite DENTRO
     del `<aside>` -- hacia afuera, el `<aside>` entero corre a `z-index:auto`
     (=0), y las tarjetas de `main`, que vienen DESPUÉS en el DOM, ganan el
     empate y tapan el menú abierto. Antes de #416 el menú abría hacia abajo
     DENTRO del riel de 242px y nunca llegaba a superponerse a `main`, por eso
     el bug no existía. 20 lo pone por encima de todo `main` y por debajo de
     `.barra-sup` (30, fija arriba de todo) y de las migas móviles (29, ver
     `@media(max-width:640px)` más abajo) -- ninguna de las dos vive dentro
     del `<aside>`, así que no compite con su `.pick-menu` interno. */
  /* ⚖ 30-sep-2026 (rediseño, docs/96 etapa 1b): el riel baja a 112px, el ancho
     de la maqueta. Ya no hay encabezado de grupo en versalitas que fije el piso
     de 124px: la palabra más larga ("Propiedades") entra en un renglón a 12px. */
  aside.lateral{
    flex:none; width:112px; position:sticky; top:60px; height:calc(100vh - 60px); overflow:visible;
    background:var(--bg); border-right:1px solid var(--line); z-index:20;
    padding:14px var(--e2); display:flex; flex-direction:column; gap:var(--e3);
  }
  /* El envoltorio scrolleable (⚖ revisión sobre #416, ver el comentario de
     arriba): `flex:1` le da TODO el alto que sobra dentro del `<aside>` de
     alto fijo, y `min-height:0` es lo que le permite ENCOGERSE por debajo
     del alto de su contenido en vez de estirar al padre -- sin esa línea, un
     hijo `flex:1` con contenido más alto que el espacio disponible fuerza al
     contenedor a crecer (el comportamiento por default de flexbox), que es
     exactamente el desborde que este cambio vino a resolver. */
  .riel-secciones{flex:1; overflow-y:auto; min-height:0}
  /* La PISTA de scroll (⚖ fundador 22-sep-2026, revisión de "secciones al
     riel"): con las dos filas nuevas una cartera de un solo contribuyente ya
     desborda los 900px de siempre, y una cartera de verdad -- varios
     contribuyentes, árbol fiscal + operativo -- desborda bastante más. Medido
     en vivo (JS, `getBoundingClientRect`): achicar la fila de 48px a 44px NO
     alcanza para las carteras reales -- en la cuenta de Heidy (4
     contribuyentes) el desborde bajó de 211px a 131px, pero seguía habiendo
     scroll. Achicar la fila es una pelea que se pierde apenas la cartera
     crece; lo que no se queda corto es avisar que hay más abajo, no perseguir
     un presupuesto que una cuenta más grande vuelve a romper igual.

     El truco es el clásico de "scroll shadows", sin una línea de JavaScript
     (mismo criterio que el resto de este nav: `<details>`, cero JS donde
     alcanza con CSS): CUATRO fondos apilados -- dos sólidos con
     `background-attachment:local` (se mueven CON el contenido, así que la
     sombra que tapan se esconde sola en cuanto ese borde es el final real de
     la lista) y dos sombras con `attachment:scroll` (se quedan fijas en el
     borde del contenedor, debajo de los sólidos). Con la lista más corta que
     el contenedor no hay nada que desborde: el sólido local cubre la sombra
     entera y no se ve nada nuevo. Con desborde, el sólido se corre CON el
     scroll y destapa la sombra fija justo del lado que sigue teniendo
     contenido -- ninguna regla pregunta "¿hay overflow?", el navegador lo
     resuelve solo con dónde queda cada capa. 16px de alto (el pedido del
     fundador) y el color sale de `--bg` -- no un blanco fijo -- para que
     funcione en los dos temas sin una segunda regla. */
  .riel-secciones{
    background:
      linear-gradient(var(--bg) 40%, rgba(0,0,0,0)) local,
      linear-gradient(rgba(0,0,0,0), var(--bg) 60%) local,
      radial-gradient(farthest-side at 50% 0, rgba(27,38,36,.16), rgba(27,38,36,0)) scroll,
      radial-gradient(farthest-side at 50% 100%, rgba(27,38,36,.16), rgba(27,38,36,0)) scroll;
    background-repeat: no-repeat;
    background-size: 100% 16px, 100% 16px, 100% 8px, 100% 8px;
    background-position: top, bottom, top, bottom;
  }
  /* Solo para lectores de pantalla: sigue haciendo falta en el riel -- el menú
     móvil (`.menu-btn`) y el selector de propietario de ahí adentro no tienen
     texto visible propio en todos sus estados, y `title` no es un nombre
     accesible confiable en todos los lectores. */
  .solo-lectores{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
  }
  /* Cada fila del riel: ícono arriba, rótulo de 11px (`--t-xs`) debajo,
     centrado -- reemplaza el layout horizontal de la columna vieja SOLO
     adentro del `<aside>` (⚖ fundador 21-sep, maqueta B: "rótulo chico debajo
     del ícono, no ícono solo"). `nav.sections a` sigue siendo la regla base
     (la comparten `test_candados_plan.py` y el menú deslizable de móvil, que
     NO pasa por acá -- vive bajo `@media(max-width:900px)`, más abajo, y ahí
     el layout sigue siendo la fila horizontal con el texto entero). */
  @media (min-width:901px){
    /* Todo acá va prefijado con `aside.lateral` A PROPÓSITO, y no es cosmético:
       son overrides de reglas que `nav.sections a{...}` y compañía declaran
       MÁS ABAJO en esta misma hoja (la base que `test_candados_plan.py`
       lee), y en CSS un empate de especificidad lo gana la regla que aparece
       DESPUÉS en el archivo -- sin el prefijo, la base (que viene después)
       tapa a este bloque (que va antes) y el riel nunca se ve. Con el
       prefijo, este bloque es más específico y gana sin importar el orden --
       el mismo truco que `.shell.plegado nav.sections ...` usaba antes de
       este PR, adaptado: ya no hace falta el estado, el riel es el único
       estado de escritorio. */
    /* El `gap` entre cima/grupos/pie se apaga acá: son 14px (`--e3`) que la
       columna ancha se podía permitir y el riel de 840px disponibles (900 de
       viewport) no. La separación entre bloques la dan ahora `nav.sections.
       cima{margin-bottom:8px}` y la hairline de `.riel-pie` -- las dos
       medidas ya en el presupuesto que fija ese comentario. */
    aside.lateral{gap:0}
    /* Cada fila: ícono arriba, rótulo debajo, centrado, 48px de alto FIJO
       (⚖ segunda revisión del fundador: con `min-height` implícito por
       contenido, una fila de una sola línea --la mayoría-- se quedaba en
       ~37px --20 de ícono + 4 de aire + una línea de 11px-- y el sobrante se
       lo comía `margin-top:auto` en `.riel-pie`, dejando ~200px de aire
       muerto entre "Reseñas" y el pie. `min-height:48px` + `justify-content:
       center` reparte ese aire ADENTRO de cada fila --6 a 8px arriba y abajo
       del ícono+rótulo, según si el rótulo ocupa una línea o dos-- en vez de
       dejarlo junto en un solo bloque al final. Sigue sin padding propio: el
       centrado ya hace ese trabajo, y el aire entre filas lo da
       `.grupo-items{gap:1px}` (regla base, sin tocar). */
    aside.lateral nav.sections a, aside.lateral nav.sections a.oferta,
    aside.lateral #instalar-app{
      flex-direction:column; justify-content:center; gap:4px; min-height:48px;
      padding:0; text-align:center;
    }
    aside.lateral nav.sections .ico{width:20px; height:20px}
    /* La nota de iPhone no cabe en 124px de ancho -- once palabras en 11px
       ocuparían media columna sobre las filas de abajo. Queda solo en el menú
       móvil (mismo `<p>`, otro `@media`, más abajo): ahí es donde vive un
       iPhone real, así que nunca deja de estar donde el usuario que la
       necesita la puede ver (ver el comentario de `_instalar.html`). */
    aside.lateral #instalar-nota{display:none}
    /* ⚠ SIN `text-overflow:ellipsis` ni `-webkit-line-clamp` (revisión del
       fundador sobre la primera versión): un rótulo cortado a la mitad de una
       palabra ("Propiedad…", "Comproba…") se lee peor que dos líneas
       completas. `white-space:normal` deja que el texto envuelva de verdad;
       `overflow-wrap:anywhere` es lo que permite partir DENTRO de una palabra
       larga cuando hace falta.

       ⚖ 24-sep-2026, revisión UX (riel de cinco destinos planos): con
       CINCO rótulos en vez de trece, ninguno necesita acortarse a mano --
       "Fiscal", "Operación", "Propiedades", "Ajustes" y "Hoy" entran todos en
       una o dos líneas sin ayuda (`rotulo_riel` se retira de
       `_sections_nav.html`). 104px de ancho útil sigue siendo el que fija
       "Propiedades" -- la palabra más larga --, y el resto queda con margen
       de sobra. */
    aside.lateral .txt{
      font-size:12px; line-height:1.1; min-width:0; max-width:92px;
      white-space:normal; overflow-wrap:anywhere;
    }
    /* El separador entre destinos del riel (⚖ 24-sep-2026): 12px + la línea,
       igual que separaba antes un encabezado de grupo del siguiente. */
    aside.lateral nav.sections .grupo + .grupo{
      border-top:1px solid var(--line); margin-top:12px; padding-top:12px;
    }
    /* La fila activa (⚖ fundador 21-sep, maqueta B): superficie `--ok-bg` con
       ícono y rótulo en `--ok`, radio 12 -- ya no la píldora terracota llena
       de la columna vieja (que sigue viva para el menú de MÓVIL, más abajo,
       donde el layout es otro). El hover pasa a `--crema`: un tinte, no el
       salto a blanco que tenía sentido cuando el fondo de la columna era
       crema y el `--bg` de la fila la levantaba -- acá el fondo YA es blanco. */
    aside.lateral nav.sections a:hover, aside.lateral nav.sections a.oferta:hover{
      background:var(--crema); color:var(--terra-text);
    }
    /* La fila activa es la píldora en tinte océano de la maqueta (`--brand-tint`,
       texto y ícono en `--accent`): el verde de `--ok` era el de un estado, y el
       destino en el que uno está no es un estado. */
    aside.lateral nav.sections a.on, aside.lateral nav.sections a.on:hover{
      background:var(--brand-tint); color:var(--accent); border-radius:14px; font-weight:600;
    }
  }
  /* `min-width:0` no es cosmético: sin él una tabla ancha estira el flex-item y
     el `body` vuelve a scrollear horizontal en vez de scrollear la tabla dentro
     de su `.tabla-scroll`. */
  .col{flex:1; min-width:0}
  /* La barra superior oscura (PR 2/4 de la cáscara moderna, `decision-cascara-
     moderna-21sep`): fija y de borde a borde, por ENCIMA del sidebar (que por
     eso arranca a `top:60px`, más arriba). Hasta este PR era una fila más
     DENTRO de `.col` -- las migas y el cierre de sesión -- y ahora es el
     chrome de toda pantalla con sidebar: se lleva consigo la marca y el
     selector de contribuyente, que vivían en la columna.
     `z-index` por encima del `.busqueda-panel` (6) y del `.pick-menu` (5) de
     sus propios desplegables, que cuelgan hacia abajo y tienen que quedar
     encima del contenido, no de esta barra. */
  .barra-sup{
    position:fixed; top:0; left:0; right:0; z-index:30; height:60px; margin:0;
    display:flex; align-items:center; gap:var(--e3); padding:0 var(--e4);
    background:var(--dark); color:var(--on-dark);
  }
  .barra-sup .brand{padding:0; min-width:0; flex:none}
  /* El primer y el tercer trazo del isotipo son océano (`stroke="#10444F"`,
     fijo en el SVG): sobre la barra `--dark` (#0B343D) dan 1.17:1 y
     desaparecen. Toman el arena del texto; el del medio es ámbar y se queda
     (7.29:1 sobre oscuro). */
  .barra-sup .brand svg path:first-of-type,
  .barra-sup .brand svg path:last-of-type{stroke:var(--on-dark)}
  /* La "Tribu" en negrita (`.word b`) es océano sobre claro: sobre `--dark`
     no se vería, así que toma el ámbar de marca (`--on-dark-accent`, 7.29:1).
     Revisión de diseño/seguridad sobre el PR 2, 21-sep; paleta 28-sep. */
  .barra-sup .brand .word b{color:var(--on-dark-accent)}
  .barra-sup form{margin:0}
  @media (max-width:640px){ .barra-sup .emisor-pick{display:none} }
  /* ⚖ Fundador 24-sep-2026, revisión UX (docs/70 principios 1/2/4): las MIGAS
     salen de la barra superior. Repetían el selector de contribuyente de al
     lado y el riel ya dice en qué sección estás -- las tres cosas nombraban lo
     mismo con tres letras distintas. El bloque de migas ya no se pinta acá
     (ver más abajo, en la rama sin sidebar): las 66 plantillas que lo
     sobreescribían dejan de hacerlo.

     El chip del tipo de cambio (`.tc-hoy`) TAMBIÉN sale de esta barra: dejó de
     ser un dato de toda pantalla y pasó a ser el pie de página de las DOS
     pantallas donde de verdad se usa (Comprobantes, Cierre del mes) -- ver
     `.tc-linea` en el bloque de esas pantallas, más abajo. */

  /* El buscador global (B2, `docs/6`), centrado en la barra (⚖ fundador
     21-sep, maqueta B): `margin:0 auto` en un hijo flex reparte el espacio que
     sobra en partes iguales a los dos lados, así que queda al medio entre el
     cluster de la izquierda (marca, selector) y el de la derecha (campana,
     avatar) sin importar cuánto pese cada uno. La lupa sigue siendo un SVG
     inline en el fondo del input -- el nav no tiene sistema de íconos propio,
     y uno solo para esto sería una dependencia nueva.

     ⚖ 24-sep-2026, revisión UX: con las migas y el chip del dólar retirados de
     esta barra, `.busqueda-global` puede faltar del todo (un rol sin
     `FISCAL_CAPABILITY` ni `CARTERA_CAPABILITY`, `app.deps._nav_emisor`). Sin
     ella no queda nada con `margin:auto` que empuje campana y avatar contra el
     borde derecho -- así que esos dos cargan la MISMA regla en cadena, más
     abajo (`.menu-campana`/`.menu-usuario`): el primero de los tres que
     aparece en el DOM es el que empuja, sea cual sea. */
  .busqueda-global{position:relative; flex:none; margin:0 auto; width:min(480px, 38vw)}
  .busqueda-input{
    width:100%; height:40px; padding:8px 12px 8px 34px; border:1px solid transparent;
    border-radius:var(--r-pill); background:rgba(255,255,255,.10) no-repeat 10px center;
    background-size:14px; font-size:var(--t-sm); color:var(--on-dark);
    font-family:inherit; transition:width .15s ease;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23B1BEBB' stroke-width='1.6'%3E%3Ccircle cx='7' cy='7' r='5.2'/%3E%3Cline x1='11' y1='11' x2='14.5' y2='14.5'/%3E%3C/svg%3E");
  }
  .busqueda-input::placeholder{color:var(--on-dark-mute)}
  .busqueda-input::-webkit-search-cancel-button{cursor:pointer}
  .busqueda-input:focus{outline:none; border-color:var(--on-dark-accent); box-shadow:var(--focus-ring)}
  /* El panel flota debajo de la caja, misma factura que `.pick-menu` del
     avatar (hairline + sombra + `--r-md`): son los dos únicos desplegables de
     esta barra y comparten look. Vacío no ocupa nada -- lo llena `hx-swap`. */
  .busqueda-panel{
    position:absolute; z-index:6; top:calc(100% + 6px); left:0; width:320px;
    max-width:calc(100vw - 32px); max-height:70vh; overflow-y:auto;
    background:var(--bg); border:1px solid var(--line); border-radius:var(--r-md);
    box-shadow:0 10px 26px rgba(27,38,36,.07);
  }
  .busqueda-panel:empty{display:none; border:0; box-shadow:none}
  .busqueda-grupo + .busqueda-grupo{border-top:1px solid var(--line)}
  .busqueda-etiqueta{
    padding:var(--e1) 12px 4px; margin:0; font-size:var(--t-xs); color:var(--mute);
    text-transform:uppercase; letter-spacing:.04em;
  }
  .busqueda-items{list-style:none; margin:0; padding:0 var(--e1) var(--e1)}
  .busqueda-items a{
    display:flex; flex-direction:column; gap:2px; padding:8px 12px;
    border-radius:var(--r-sm); text-decoration:none; color:var(--ink);
  }
  .busqueda-items a:hover{background:var(--soft)}
  .busqueda-titulo{font-size:var(--t-md)}
  .busqueda-subtitulo{font-size:var(--t-sm); color:var(--mute)}
  .busqueda-vacio{margin:0; padding:var(--e2) 12px; font-size:var(--t-sm); color:var(--mute)}
  /* A 640px la caja ya no cabe entera al lado de la marca, la campana y el
     avatar: se achica a solo la lupa y crece con el foco, mismo gesto de
     siempre -- acá el `width` de partida es el que cambia. */
  @media (max-width:640px){
    .busqueda-global{width:40px; flex:none; margin-left:auto}
    .busqueda-input{width:40px; padding-left:34px; color:transparent}
    .busqueda-input::placeholder{color:transparent}
    .busqueda-input:focus{width:calc(100vw - 120px); color:var(--on-dark)}
    .busqueda-input:focus::placeholder{color:var(--on-dark-mute)}
    .busqueda-panel{max-width:calc(100vw - 24px)}
  }

  /* El selector de emisor (docs/40 fila 1). Solo sale cuando la cuenta tiene
     más de uno: en la del propietario solo no hay adónde saltar. Hasta el
     PR 2 vivía en el sidebar y ocupaba toda la columna; ahora es una pastilla
     más de la barra de arriba, al lado de la marca (⚖ fundador 21-sep,
     `decision-cascara-moderna-21sep`). */
  /* El selector de contribuyente: un `<details>` con enlaces de verdad, cero
     JavaScript. Hasta el 24-sep compartía esta regla con el menú "Cuenta" del
     pie del riel, retirado en la revisión UX de esa fecha (sus filas subieron
     al riel como la pastilla "Ajustes"). */
  .emisor-pick details{position:relative}
  .emisor-pick summary{
    list-style:none; cursor:pointer; display:flex; align-items:center; justify-content:space-between; gap:9px;
    padding:8px 12px; border:1px solid var(--line); border-radius:var(--r-sm); background:var(--bg);
    font-size:var(--t-md); font-weight:500; transition:border-color .15s ease;
  }
  .emisor-pick summary::-webkit-details-marker{display:none}
  .emisor-pick summary:hover{border-color:var(--accent)}
  .emisor-pick details[open] summary{border-color:var(--accent)}
  /* El panel lo comparten el selector de contribuyente y el "Más" de la barra
     inferior de móvil (`.barra-movil`, más abajo): hasta el 29-sep-2026 estas
     reglas eran SOLO de `.emisor-pick`, así que "Más" abría sus filas como
     texto suelto sobre el contenido -- sin fondo, sin borde, con el dibujo de
     pastilla de 56px de la propia barra -- y en el teléfono en vertical
     "Sitio web" y "Ajustes" no se veían (⚖ fundador: "en vertical no muestra
     el menú completo"; en horizontal un iPhone grande pasa de 900px y ve el
     riel de escritorio, por eso ahí sí). */
  .emisor-pick .pick-menu, .barra-movil .pick-menu{
    position:absolute; z-index:5; left:0; right:0;
    background:var(--bg); border:1px solid var(--line); border-radius:var(--r-md); padding:var(--e1);
    box-shadow:0 10px 26px rgba(27,38,36,.07);
  }
  .emisor-pick .pick-menu a, .barra-movil .pick-menu a{
    display:block; padding:8px 12px; border-radius:var(--r-sm); text-decoration:none;
    color:var(--ink); font-size:var(--t-md);
  }
  .emisor-pick .pick-menu a:hover, .barra-movil .pick-menu a:hover{background:var(--soft)}
  .emisor-pick .pick-menu a.actual, .barra-movil .pick-menu a.actual{color:var(--terra-text); font-weight:600}
  /* El selector vive arriba de su columna: despliega hacia ABAJO. */
  .emisor-pick summary::after{content:"▾"; color:var(--mute); font-size:var(--t-xs); line-height:1}
  .emisor-pick .pick-menu{top:calc(100% + 6px)}
  /* La invitación cuando no hay contribuyente elegido: gris y sin el peso de un
     nombre propio, para que no se lea como si ya hubiera uno puesto. `--mute`
     sobre `--crema` da 6.59:1, arriba de AA. */
  .emisor-pick .sin-elegir{color:var(--mute); font-weight:400}
  /* En la barra oscura es una pastilla translúcida, no la caja hairline del
     resto de la app (⚖ fundador 21-sep, PR 2): mismo control, otro fondo. El
     `.pick-menu` se queda BLANCO -- como el de la campana y el avatar, que ya
     cuelgan de esta misma barra -- así que no lleva override de color acá.
     `left:0; right:auto` porque el pill es angosto (ancho por contenido) y el
     menú no puede quedar comprimido a su ancho como en la columna. */
  .barra-sup .emisor-pick summary{
    border-color:transparent; border-radius:var(--r-pill);
    background:rgba(255,255,255,.10); color:var(--on-dark);
    /* Alto fijo, igual al de la campana y el avatar de al lado (38px, #413):
       sin esto un nombre largo -- ver `.emisor-pick-nombre` -- estiraba la
       pastilla en vez de recortarse, y desbordaba los 60px de la barra
       (visto en staging con "Villas del Coco S.A." a 1200px, 21-sep). */
    height:38px;
  }
  .barra-sup .emisor-pick summary:hover,
  .barra-sup .emisor-pick details[open] summary{border-color:rgba(255,255,255,.35)}
  .barra-sup .emisor-pick summary::after{color:var(--on-dark-mute)}
  .barra-sup .emisor-pick .sin-elegir{color:var(--on-dark-mute)}
  /* El nombre del contribuyente es de UNA línea, con elipsis si no entra --
     el `max-width` es un tope duro (no depende del reparto flex de
     `.barra-sup`), así que un nombre largo nunca empuja al buscador ni a las
     migas; el nombre completo queda en el `title` del `summary` (arriba) para
     el hover. `20vw` es lo que le toca ceder a angosto (1024-1200px); `22ch`
     alcanza para no recortar nombres cortos en pantallas anchas. */
  .barra-sup .emisor-pick-nombre{
    display:block; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;
    max-width:min(22ch, 20vw);
  }
  /* El menú desplegable NO comparte el `max-width` del pill: acá un nombre
     largo puede envolver en dos líneas (`.pick-menu a` no lleva
     `white-space:nowrap`), pero el ANCHO del menú tiene que quedar acotado --
     sin tope, un nombre largo sin espacios lo estira más allá del viewport
     (mismo cálculo que `.busqueda-panel`, arriba). `overflow-wrap` es la red
     para ese caso límite. */
  .barra-sup .emisor-pick .pick-menu{
    left:0; right:auto; min-width:220px; max-width:min(320px, calc(100vw - 32px));
  }
  .barra-sup .emisor-pick .pick-menu a{overflow-wrap:anywhere}

  /* La CIMA y el PIE de la columna (⚖ fundador 2-sep-2026). Son dos `<nav>`
     hermanos del de los grupos --el selector de contribuyente se mete entre la
     cima y ellos-- y llevan la misma clase `sections`, así que sus filas heredan
     tinta, hover, píldora activa y el clipeo del riel plegado sin una regla
     nueva. Lo único que hace falta es el aire que los separa del bloque de al
     lado, y una hairline en el pie: lo de abajo es de OTRO sujeto (la cuenta) y
     sin la línea se leería como una fila más del árbol del contribuyente.

     El margen bajó de `--e3` (14px) a 8px en la revisión del fundador sobre la
     primera versión del riel: con catorce secciones + pie, 900px de alto
     (840 de `<aside>`) no sobran para el aire generoso de la columna vieja de
     242px -- ver la cuenta completa del presupuesto en `.riel-pie`, abajo.

     ⚖ 22-sep-2026: la cuenta bundle de un solo contribuyente sube a DIECISÉIS
     filas visibles -- a 900px de alto ya no entran sin scroll. No se tocó
     este presupuesto para que entraran: el que scrollea es `.riel-secciones`
     (ver su comentario, más arriba), y el pie queda AFUERA de ese contenedor
     a propósito, así que sigue visible sin necesidad de bajar el alto de
     fila. Verificado con capturas a 1440×900 y 1440×800
     (`docs/ui-maqueta-21sep/secciones-al-riel/`).

     ⚖ 24-sep-2026 (grupos plegables): la cima vuelve a DOS filas -- las dos
     que subieron acá el 22-sep bajan del riel a sus grupos
     (`GRUPO_FISCAL`/`GRUPO_PROPIEDADES`, ver `app.deps._cima`) en vez de
     vivir sueltas acá -- pero el presupuesto de scroll sigue haciendo falta:
     agrupadas o no, catorce filas de sección siguen siendo catorce filas.
     Verificado con capturas a 1440×900 y a 375px
     (`docs/ui-maqueta-21sep/grupos-plegables-24sep/`).

     ⚠ Ninguna etiqueta de sección se nombra LITERAL en este comentario a
     propósito (⚖ control real, 24-sep-2026): esta hoja de estilos es inline
     en TODA pantalla con sidebar, así que un string en español que viviera
     acá aparecería también en la versión en inglés -- `tests/test_i18n.py`
     lo agarró exactamente así la primera vez que este párrafo nombraba, entre
     comillas, la fila de la ocupación del portafolio. */
  nav.sections.cima{margin-bottom:8px}
  /* El envoltorio del pie entero (`.riel-pie`, `_sections_nav.html`): el chip
     de plan y el `<nav class="pie">` (oferta + menú de la cuenta) son
     hermanos condicionales -- una cuenta bundle sin oferta puede no tener
     ninguno de los dos, o solo uno -- así que lo que se separa del resto con
     la hairline de abajo es este `<div>` entero y no cada pieza suya por
     separado.

     ⚖ Dos revisiones seguidas del fundador, la segunda sobre la primera:
     1) La primera versión tenía `position:sticky; bottom:0` acá, y TAPABA las
        últimas filas de "Operación de la cuenta" a 900px de alto -- el pie
        flotaba encima del contenido en vez de venir DESPUÉS. Se arregló
        achicando las filas a 48px fijos para que el contenido entrara en el
        alto disponible sin superponerse (`nav.sections a`, en el `@media` de
        escritorio, más arriba).
     2) Con eso resuelto apareció OTRO bug (revisión sobre #416): el scroll
        que sostenía ese presupuesto vivía en el `<aside>` entero
        (`overflow-y:auto`), y eso recortaba en el eje horizontal también
        (`overflow-x` se resuelve a `auto` de rebote) -- el `.pick-menu` de
        "Cuenta", que cuelga hacia la DERECHA y se sale de los 96px del
        riel, generaba scroll horizontal adentro de la columna en vez de
        mostrarse por encima. La solución final: el scroll se mudó a
        `.riel-secciones` (envuelve SOLO lo navegable), el `<aside>` volvió a
        `overflow:visible`, y `.riel-pie` quedó AFUERA del contenedor que
        recorta -- ya no necesita `margin-top:auto` para pegarse abajo:
        `.riel-secciones{flex:1}` ocupa todo el alto que sobra y lo empuja
        solo. Si una cartera desborda su presupuesto de todos modos (una
        cuenta más grande, una fuente del sistema más alta),
        `.riel-secciones` es quien scrollea -- el pie, afuera de ese
        contenedor, no se mueve ni se tapa. */
  .riel-pie{border-top:1px solid var(--line); padding-top:8px}
  /* Antes tenía su propio margen/padding/hairline -- eran los que separaban
     el pie del resto de la columna. Esa hairline ahora vive en `.riel-pie`
     (una sola vez, arriba del todo), así que acá alcanza con el `flex` que
     ya traía `.grupo-items`: sin él, la oferta y el menú de la cuenta --
     cuando conviven-- quedarían pegados sin aire entre sí. */
  nav.sections.pie{margin:0; padding:0; border:0; gap:1px}
  /* ⚖ 24-sep-2026, revisión UX (docs/70): el chip de plan (#B7) y el menú
     "Cuenta" del pie SE RETIRAN -- sus filas suben al riel como la pastilla
     "Ajustes" (`app.roles.GRUPO_AJUSTES`), y el pie queda con la única fila
     que le hacía falta seguir teniendo suelta: la oferta de upgrade
     (`nav.sections a.oferta`, arriba) y, debajo, "Instalar en el teléfono". */

  /* "Instalar en el teléfono" (`_instalar.html`), la fila que queda en el
     pie: ícono + rótulo en `--mute`, sin caja propia. Bug visto por el
     fundador tras la cáscara moderna: es un botón pelado, y sin esta regla
     hereda el botón primario GLOBAL de más abajo en esta hoja (`button{...}`)
     -- píldora terracota, `padding:12px 26px`, `font-size:var(--t-lg)` -- que
     en un riel angosto partía el rótulo en varias líneas y tapaba el borde.
     El selector por ID es lo que le gana en especificidad a `button{...}`,
     que declara las mismas propiedades; `background:none`, `border:0` y
     `margin-top:0` son la parte que un enlace o una fila de sección no
     necesitan, pero un botón sí. Estas tres reglas vivían en un `<style>`
     provisional adentro de `_instalar.html`; se mudan acá con la
     consolidación del CSS que ese archivo venía anunciando.

     ⚠ Por eso arriba se evita escribir la etiqueta `summary` entre ángulos:
     `tests/test_canales_por_emisor.py` y compañía leen el rótulo del
     selector de contribuyente buscando esa etiqueta abierta y su cierre --
     la misma subcadena suelta en un comentario de esta hoja engancha ese
     regex y le hace tragarse todo el CSS de acá hasta el próximo cierre
     real (mismo cuidado que el truco de `span.grupo-h`, más arriba). */
  #instalar-app{
    display:flex; align-items:center; gap:var(--e2); width:100%;
    background:none; border:0; text-align:left;
    color:var(--mute); padding:7px var(--e2); border-radius:var(--r-sm);
    font-size:var(--t-md); font-weight:500; font-family:inherit; cursor:pointer;
  }
  /* `filter:none; transform:none` no son de más: sin ellos el `button:hover`
     global (`filter:brightness(1.08); transform:translateY(-1px)`) le sigue
     ganando a ESTA regla en la propiedad `filter`/`transform` -- un ID le
     gana a un elemento en TODO lo que las dos reglas comparten, pero una
     propiedad que solo declara la regla de menor especificidad no tiene con
     qué competir y se aplica igual. */
  #instalar-app:hover{background:var(--crema); color:var(--terra-text)}
  /* El `[hidden]` explícito no es de más (mismo motivo que tenía en
     `_instalar.html`): el selector por ID de arriba también le gana en
     especificidad al `[hidden]{display:none}` que el navegador aplica por
     omisión, así que sin esta línea la fila se vería siempre, prendida o no. */
  #instalar-app[hidden]{display:none}
  /* La nota de iPhone (`#instalar-nota`) no entra en el riel de 96px sin
     ocupar media columna -- sale del riel de escritorio y queda solo en el
     menú móvil, donde sí hay ancho completo (ver el `@media(max-width:900px)`
     de más abajo y el comentario de `_instalar.html`). */
  #instalar-nota{margin-top:6px; color:var(--mute); font-size:11px; line-height:1.45}
  #instalar-nota[hidden]{display:none}

  /* Los cinco destinos del riel (⚖ fundador 24-sep-2026, revisión UX). Cada
     `.grupo` es UN `<a>` -- ya no hay encabezado de grupo ni `<details>` que
     plegar, así que `.grupo-h`/`.grupo-h-flecha`/`.grupo-suelto` se retiran
     enteros (vivieron entre el 24-sep de los grupos plegables y esta misma
     revisión, unas horas después). Lo que separa un destino del siguiente es
     la hairline de `.grupo + .grupo`, más abajo -- no un rótulo. */
  nav.sections{display:flex; flex-direction:column}
  nav.sections .grupo-items{display:flex; flex-direction:column; gap:1px}
  /* Las pantallas de un mismo destino, listadas DEBAJO de él en el menú de
     MÓVIL (⚖ fundador: "el menú hamburguesa tiene que listar los cinco
     destinos y, debajo del activo, sus pestañas") -- ver el comentario largo
     de `_sections_nav.html`. En escritorio esta lista no hace falta: ahí la
     misma navegación vive en la barra de tabs subrayados arriba del contenido
     (`_tabs_grupo.html`, `app.roles.tabs_del_grupo`), así que se esconde por
     default y solo se muestra bajo 900px -- y solo para el grupo que el
     servidor ya marcó activo (`_sections_nav.html` no la imprime para los
     otros tres). Sangrada un poco a la derecha del ícono de arriba, para que
     se lea como "adentro de" y no como una fila hermana. */
  .riel-tabs{display:none}
  nav.sections .riel-tabs a{
    padding:7px var(--e2) 7px calc(var(--e2) + 20px + var(--e2));
    font-size:var(--t-sm);
  }
  /* NIVEL 2 -- la sección navegable. `--ink` y no `--mute`: es la tinta del
     cuerpo, y es lo que hace que la fila se lea como algo que se toca en vez de
     como un subtítulo. 13px (y no 13.5) para que no compita con los h2 de la
     página; el `display:block` con padding hace que el blanco de clic sea la
     FILA entera y no las letras. */
  nav.sections a{
    display:flex; align-items:center; gap:var(--e2); text-decoration:none;
    color:var(--ink); padding:7px var(--e2);
    border-radius:var(--r-sm); font-size:var(--t-md); font-weight:500;
    transition:background .15s ease, color .15s ease;
  }
  /* El ícono (⚖ fundador 25-ago). NO lleva color propio a propósito: hereda
     `currentColor`, así que los tres niveles de la columna --rótulo, ítem vivo,
     oferta del plan-- y los estados (hover, activo) lo arrastran solos. Una
     regla de color por estado para el glifo sería la cuarta jerarquía que la
     columna no necesita.
     `flex:none` porque la etiqueta más larga del nav aplastaba el SVG antes de
     partir el texto. (Sin nombrarla: el CSS viaja en TODAS las páginas, y un
     test que comprueba que cierta sección NO se le ofrece a un rol busca su
     etiqueta en el HTML entero -- un comentario que la escriba la "filtra".) `opacity` un pelo abajo de 1: a peso completo
     el trazo pesa más que las letras de 13px y la columna se lee como una fila
     de dibujos con texto al lado, que es al revés de lo que queremos. En el
     ítem activo vuelve a 1 -- ahí el fondo terracota se come el contraste. */
  nav.sections .ico{flex:none; opacity:.78}
  nav.sections a.on .ico{opacity:1}
  nav.sections .txt{min-width:0}
  /* El hover pasa a terracota -- el acento de la marca -- en vez del gris de
     antes: el color dice "esto es un enlace" sin subrayar nada.

     El FONDO es blanco y no un tinte terracota, y la razón es medida, no
     estética: sobre `--crema` el tinte naranja deja `--terra-text` en 4.28:1
     (4.48 incluso bajándolo a alpha .06), o sea DEBAJO de AA -- el tinte solo
     funcionaba mientras la columna era casi blanca. Con fondo `--bg` da 5.44:1
     y además se lee mejor: la fila se LEVANTA del crema, que es justo lo que
     tiene que hacer algo que responde al mouse. La progresión queda crema
     (quieto) -> blanco + terracota (hover) -> terracota lleno (activo). */
  nav.sections a:hover{background:var(--bg); color:var(--terra-text)}
  /* La píldora activa se traduce a una fila llena: mismo par de colores (fondo
     `--terra-text`, texto blanco = 5.44:1), que es el que pasa AA. */
  nav.sections a.on{background:var(--terra-text); color:#fff; font-weight:600}
  nav.sections a.on:hover{background:var(--terra-text); color:#fff}
  /* NIVEL 3 -- la OFERTA del plan (⚖ fundador 26-ago). Es la fila que reemplazó
     a las cuatro con candado del 25-ago, y **es un `<a>` que lleva al wizard**
     (`/plan`): la regla del nav prohíbe ofrecer un tab que termine en 403, no
     prohíbe ofrecer el camino para comprarlo. Lo que nunca se linkea es la
     sección bloqueada.

     Se queda en `--mute` mientras el ítem vivo subió a `--ink`, y ESA es la
     diferencia de tratamiento: no es una sección que la cuenta tenga.

     ⚠ Sin `opacity`, y no por gusto: el `.72` que esto tenía mezclaba `--mute`
     contra `--soft` y dejaba el texto en **3.61:1**, o sea DEBAJO del 4.5:1 de
     AA. `--mute` liso da 7.11:1. Un estado "no disponible" sigue siendo texto
     que hay que poder leer -- si no, no es una oferta, es un borrón.

     Cursor `pointer` y con hover: lo que se toca es un enlace de verdad, y el
     hover es el MISMO terracota del ítem vivo -- la fila se levanta del crema
     igual que cualquier otro enlace. `not-allowed` nunca fue una opción: no es
     algo prohibido, es algo que todavía no compró.

     El `gap` es el mismo 10px del ítem vivo: la primera cosa de cada fila es el
     ícono, y con dos gaps distintos las etiquetas arrancaban cuatro píxeles
     corridas de las demás -- una columna cuyo borde de texto tiembla se lee
     como un error de render. */
  nav.sections a.oferta{
    display:flex; align-items:center; gap:var(--e2);
    color:var(--mute); padding:7px var(--e2); border-radius:var(--r-sm);
    font-size:var(--t-md); font-weight:500; cursor:pointer;
  }
  /* Va DESPUÉS de la regla de arriba y no antes: `a.oferta` y `a.oferta:hover`
     no empatan en especificidad, pero el `a:hover` genérico de más arriba sí
     le ganaría a `a.oferta` -- sin esta línea, la oferta sería el único enlace
     de la columna que no responde al mouse. */
  nav.sections a.oferta:hover{background:var(--bg); color:var(--terra-text)}

  /* La lista del pop-up de la oferta (`_upgrade_pms.html`). Es una enumeración
     de verdad --`<ul>`-- y no filas fingidas con `<div>`: lo que se lee ahí es
     "estas cinco cosas", y un lector de pantalla tiene que poder decir cuántas
     son. El disco se cambia por un signo de suma: cada línea es algo que SE
     AGREGA a lo que la cuenta ya tiene, y el símbolo lo dice sin una palabra. */
  dialog ul.ventajas{list-style:none; margin:var(--e3) 0 0; padding:0}
  dialog ul.ventajas li{position:relative; padding:var(--e1) 0 var(--e1) 22px; font-size:var(--t-md);
    line-height:1.45; color:var(--ink)}
  dialog ul.ventajas li::before{content:"+"; position:absolute; left:2px;
    color:var(--terra-text); font-weight:700}
  /* El "ahora no" no compite con el botón de arriba: va en su propia línea y
     con el tratamiento secundario que ya existe. */
  dialog .upgrade-no{margin-top:4px}

  /* ---------- el selector de propietario, DUPLICADO en el menú móvil ---------- */
  /* PR 3 de la cáscara moderna: el selector de contribuyente vive en
     `.barra-sup` desde el PR 2, y esa barra lo esconde por debajo de 640px
     (revisión de diseño/seguridad sobre el PR 2, más arriba en esta hoja) --
     ahí el celular se queda sin forma de cambiar de contribuyente, porque el
     riel de abajo de 900px es el MENÚ deslizable y no un lugar donde el
     selector ya viviera. `_sections_nav.html` lo pinta una segunda vez, con
     esta clase, usando el mismo componente `.emisor-pick` de siempre (mismo
     CSS, cero reglas nuevas de layout) -- acá lo único propio es CUÁNDO se
     ve: oculto siempre que la barra de arriba también lo tenga, visible
     donde ella no lo tiene. */
  .emisor-pick-movil{display:none}
  @media (max-width:640px){ .emisor-pick-movil{display:block; margin:0 0 var(--e2)} }

  /* La columna oscura y su preferencia de usuario (`data-nav`, `.tema-nav`)
     se retiraron en el PR 3 de la cáscara moderna: el riel tiene un solo
     tema (`decision-cascara-moderna-21sep`). */

  /* La tira de contadores del funnel de onboarding. Eran píldoras prestadas de
     `nav.sections`, y con el nav en vertical dejaron de tener dueño: no son
     navegación (no llevan a ningún lado), así que se quedan con las píldoras y
     con clase propia. */
  .chips{display:flex; gap:var(--e1); flex-wrap:wrap; padding:var(--e3) 0}
  .chips a{
    text-decoration:none; color:var(--mute); padding:8px var(--e3); border-radius:var(--r-pill);
    border:1px solid var(--line); font-size:var(--t-md); font-weight:500;
  }
  .chips a.on{background:var(--terra-text); color:#fff; border-color:var(--terra-text); font-weight:600}
  /* El chip que CUENTA y no navega (el embudo del tablero): se ve igual, pero
     el cursor no promete un destino que no hay. */
  .chips a.contador{cursor:default}

  /* Los TABS (⚖ fundador 2-sep-2026, `_tabs.html`): la misma píldora, porque es
     la misma clase de cosa mirada de cerca -- una tira horizontal de opciones
     donde una está prendida. Lo que cambia es que ÉSTAS sí navegan, así que se
     agrega lo único que la navegación necesita y los contadores no: el hover.
     Una clase propia con otro dibujo habría dado dos vocabularios de píldora en
     la misma app. */
  /* Los tabs se despegan de la píldora (cáscara moderna 21-sep-2026, maqueta
     A §2): una tira horizontal de opciones que NAVEGA se subraya, no se
     rellena -- el relleno queda para el botón primario y el filtro/contador
     de `.chips` sin `.tabs`, que sigue siendo píldora. */
  .chips.tabs{gap:0; border-bottom:1px solid var(--line); padding:0; margin-bottom:var(--e3)}
  .chips.tabs a{border:0; border-radius:0; background:none; padding:10px 14px 12px; color:var(--mute);
    border-bottom:3px solid transparent; margin-bottom:-1px; font-weight:500}
  .chips.tabs a:hover{color:var(--ink); border-bottom-color:var(--line-strong)}
  .chips.tabs a.on, .chips.tabs a.on:hover{background:none; color:var(--ink);
    border-bottom-color:var(--accent); font-weight:600}
  /* La salida hacia otra pantalla (hoy, el resumen fiscal del mes desde el
     cierre). Separada a la derecha, pero PÍLDORA como las demás (⚖ fundador
     2-sep-2026): era un link subrayado al lado de una píldora rellena, o sea
     dos vocabularios para dos cosas que se eligen igual. Lo que dice que no es
     un tab hermano es el hueco, no un dibujo aparte. */
  .chips.tabs a.aparte{margin-left:auto}
  /* A 640px la fila ya no entra: los tabs envuelven (`flex-wrap` de `.chips`) y
     el `margin-left:auto` de la salida dejaría de significar nada en su propia
     línea. */
  @media (max-width:640px){ .chips.tabs a.aparte{margin-left:0} }

  /* El toggle de móvil, sin una línea de JavaScript: un checkbox escondido que
     el `<label>` prende y que el selector hermano lee. Se esconde con `clip` y
     no con `hidden` para que siga siendo enfocable con el teclado. */
  .menu-check{position:absolute; width:1px; height:1px; opacity:0; margin:0}
  .menu-btn{display:none}

  main{padding:var(--e5) 0 60px}
  /* La escala de títulos. `h1` estaba en 23px/400 contra un cuerpo de 14px: a
     un renglón de distancia no se leía como título sino como texto grande.
     Sube a 27px y aprieta un punto más el tracking -- el peso 400 con tracking
     negativo ES la firma Anrok y no se toca; la jerarquía la hace el tamaño.
     `h2`/`h3` existían sin regla propia, así que el `<h2>` de la ficha de una
     reserva salía con el default del navegador (24px en negrita), o sea MÁS
     grande que el h1 de su propia pantalla. */
  /* Semibold y 30px (cáscara moderna 21-sep-2026): el título de pantalla pasa
     de 400/27 a 600/30, letter-spacing más cerrado -- la jerarquía la da el
     PESO, no solo el tamaño. */
  h1{font-weight:600; font-size:30px; letter-spacing:-.02em; margin-bottom:var(--e1)}
  /* El tipo de cambio del día (`_dolar_hoy.html`, ⚖ fundador 24-sep-2026,
     revisión UX): dato, no botón ni chip -- mismo criterio que tenía `.tc-hoy`
     en la barra superior, retirado. Vive debajo del `<h1>` de las dos
     pantallas que lo usan (Comprobantes, Cierre del mes). */
  .tc-linea{
    margin:0 0 var(--e3); color:var(--mute); font-size:var(--t-sm);
    font-variant-numeric:tabular-nums;
  }
  h2{font-weight:600; font-size:var(--t-h2); letter-spacing:-.02em; margin:var(--e4) 0 var(--e1)}
  /* El `h2` que abre OTRO bloque de la pantalla y no un subtema del anterior
     --la segunda lista de la bandeja de solicitudes, debajo de las
     pendientes--: arranca más abajo para que se lea como el corte que es.
     ⚠ Sin el texto del rótulo acá adentro: hay tests que afirman que esa frase
     NO está en el HTML cuando no hay filas, y la hoja viaja en cada página. */
  h2.aparte{margin-top:32px}
  h3{font-weight:600; font-size:var(--t-lg); letter-spacing:-.01em; margin:var(--e3) 0 var(--e1)}
  /* El TÍTULO de una cabecera de mes. Las cuatro pantallas que tienen una
     --resumen fiscal, ocupación, primeros pasos y la hoja del dueño-- ponían el
     mismo par de declaraciones con su propio nombre; el `flex:1` es lo que hace
     que el botón de la derecha no se despegue del borde, y el `min-width:0` lo
     que deja que un nombre largo se corte en vez de estirar la fila. */
  .rf-cab-t, .oc-cab-t, .pp-cab-t, .ol-cab-t{flex:1; min-width:0}

  /* ---------- tablas ---------- */
  /* Las tablas anchas (6-7 columnas: comprobantes, retenidos de portada, el
     board de onboarding) scrollean DENTRO de esta caja, así el `body` nunca
     scrollea horizontal en móvil -- mismo patrón que `.cal-scroll`. Sin borde
     ni radio propios: la tabla ya trae marco y esquinas, duplicarlos pinta dos
     líneas. En desktop, cuando la tabla entra, no aparece barra y el bloque se
     comporta como flujo normal, así que la vista ancha no cambia. */
  .tabla-scroll{overflow-x:auto}
  .paginacion{display:flex; gap:18px; align-items:baseline; margin-top:var(--e3); font-size:var(--t-md)}
  /* LA TABLA ESTÁNDAR (rediseño 30-sep-2026, docs/96 etapa 1b): tarjeta flotante
     de esquinas de 16, encabezado de eyebrow sobre el mismo blanco, filas
     separadas por `--line` y hover en `--crema`. Los montos van en `.num` (a la
     derecha, cifra tabular) y la fila que abre algo es `.fila-enlace`, con su
     chevrón. Una tabla ancha va dentro de `.tabla-scroll` para que en móvil
     scrollee ella y no el `body`. */
  table, .tabla{width:100%; border-collapse:collapse; margin-top:var(--e4); background:var(--bg);
    border:0; border-radius:var(--r-lg); box-shadow:var(--sh-float); overflow:hidden}
  th, td{text-align:left; padding:var(--e2) var(--e3); border-bottom:1px solid var(--line); font-size:var(--t-md)}
  /* El enlace de acción de una fila ("Ver →", "Editar ficha →") era texto de
     13.5px sin caja: ~18px de alto para tocar con el pulgar. El selector es de
     hijo DIRECTO del `<td>` a propósito -- así agarra la acción de la fila y no
     los enlaces que viven dentro de un `.hint` o de un párrafo de la celda. */
  td > a{display:inline-block; padding:7px 0; line-height:22px}
  th{color:var(--mute); font-weight:600; text-transform:uppercase; font-size:var(--t-xs); letter-spacing:.05em;
    background:var(--bg)}
  /* La celda de plata, y el rótulo de la columna que la encabeza: a la derecha,
     que es como se lee una columna de montos. Estaba escrito
     `style="text-align:right"` nueve veces, en tablas y en filas-grilla. La
     cifra tabular la sigue poniendo `.num`, que es otra decisión. */
  .monto{text-align:right}
  tbody tr:last-child td{border-bottom:0}
  tbody tr:hover{background:var(--crema)}
  /* El pie de una tabla es un TOTAL, no una fila más: se despega de las filas
     con una línea arriba y no dibuja una abajo -- el borde de la caja ya cierra
     ahí, y una segunda línea sobre la esquina redondeada se ve como un error. */
  tfoot td{border-bottom:0; border-top:1px solid var(--line); background:var(--bg)}

  .empty{color:var(--mute); padding:var(--e4) 0; font-size:var(--t-md)}
  /* `.empty` como RENGLÓN y no como bloque: el tablero de propietarios la usa
     para el teléfono, el magic link y los enlaces que cuelgan de una celda o de
     un card. Con el padding de bloque cada renglón abría veinte píxeles adentro
     de la celda, así que estaba anulado a mano siete veces. `.dato` cuelga de un
     bloque y `.dato-celda` de una fila de tabla, que respira menos; `.pegado`
     cuelga de OTRO dato de la misma celda --el teléfono debajo del correo-- y
     respira menos todavía, porque los dos son la misma respuesta. */
  .empty.dato{padding:var(--e1) 0 0}
  .empty.dato-celda{padding:4px 0 0}
  .empty.pegado{padding:2px 0 0}
  /* El renglón que EXPLICA la tarjeta, arriba de sus campos: no es un "no hay
     nada", es la entrada. Sin aire arriba (el borde de la card ya lo puso) y
     con aire abajo, contra lo que viene. */
  .empty.intro{padding:0 0 var(--e2)}
  /* El estado vacío ilustrado (`_vacio.html`). El `.empty` pelado -- una línea
     gris y nada más -- se lee como una pantalla rota y no como una pantalla
     diseñada. Sigue existiendo para el vacío accesorio (una celda, un renglón
     de detalle); las listas de más tráfico usan el bloque de abajo.
     Ancho acotado y centrado: la explicación es UNA línea, y a lo ancho de un
     monitor de 27" quedaría flotando sola en el medio de la nada. */
  .vacio{text-align:center; max-width:420px; margin:0 auto; padding:var(--e5) var(--e3)}
  /* El ícono en su círculo crema de 56px (rediseño 30-sep-2026): un solo
     dibujo del set propio, sin ilustración. La acción es un secundario en
     píldora -- el mismo molde que `.btn-sec`. */
  .vacio-ic{width:56px; height:56px; margin:0 auto var(--e2); border-radius:50%; background:var(--crema);
    color:var(--accent); display:flex; align-items:center; justify-content:center}
  .vacio-ic svg{width:24px; height:24px}
  .vacio-t{font-size:var(--t-lg); font-weight:600; letter-spacing:-.01em; margin-bottom:var(--e1)}
  .vacio-d{color:var(--mute); font-size:var(--t-md); line-height:1.55}
  .vacio-a{display:inline-flex; align-items:center; justify-content:center; margin-top:var(--e3);
    min-height:36px; padding:0 14px; border:1px solid var(--line-input); border-radius:var(--r-pill);
    background:var(--bg); color:var(--ink); font-size:var(--t-sm); font-weight:600; text-decoration:none}
  .vacio-a:hover{background:var(--crema)}
  /* El renglón que va DEBAJO del h1 y dice de qué es esta pantalla. Estaba
     escrito 42 veces como `class="empty" style="padding-top:0"` -- o sea el
     estilo del "no hay nada acá" con su padding anulado a mano --, así que
     ninguna pantalla tenía por qué respirar igual que la de al lado. Es lo
     mismo que se venía viendo, pero con nombre y una sola definición. El
     nombre no es nuevo: la página pública de reserva ya llamaba `.sub` a este
     mismo renglón en su hoja propia. */
  .sub{color:var(--mute); font-size:var(--t-md); margin-bottom:var(--e2)}
  /* Lo que el renglón destaca (la puerta y el emisor en el calendario) va en
     tinta y a 600: el resto del renglón --el canal-- sigue en --mute. */
  .sub strong{color:var(--ink); font-weight:600}
  /* El dato en letra chica que cuelga de una celda: cada anuncio de una puerta,
     con su canal y su id. No es `.hint` --no es una ayuda, es el dato-- y por
     eso no lleva el gris ni el margen de arriba. */
  .dato-chico{font-size:var(--t-sm)}
  /* El enlace que CIERRA un bloque y lleva a donde se sigue ("Ver los
     comprobantes del mes →"). Mismo vestido que el de un paso: sin subrayado,
     peso 600 y el tamaño de una fila. */
  .enlace-salida{text-decoration:none; font-weight:600; font-size:var(--t-md)}
  /* El renglón de ayuda. `word-break:normal` no es adorno: el `.hint` cae
     seguido adentro de un `dl.kv dd`, que corta a mitad de palabra para las
     claves de Hacienda, y ahí la prosa salía partida. Estaba escrito
     `style="word-break:normal"` en 48 lugares -- la misma corrección, a mano,
     una vez por párrafo. */
  .hint{font-size:var(--t-sm); color:var(--mute); margin-top:5px; word-break:normal}
  /* Lo contrario, para lo que NO es prosa: la clave de 50 dígitos de Hacienda,
     el magic link. Sin esto la cadena sin espacios es UNA palabra y ensancha su
     columna hasta sacar la tabla del marco. Va con el nombre repetido porque
     tiene que ganarle al `word-break:normal` del renglón de ayuda. */
  .cortar.cortar{word-break:break-all}
  /* Un punto más chica: el identificador técnico que cuelga de un nombre --el
     uuid del anuncio, el código del canal--, que se lee solo cuando se lo va a
     copiar. */
  .hint.chica{font-size:var(--t-xs)}
  /* Y un punto más grande y en tinta: la ayuda que es la FRASE del modal, no su
     letra chica -- es lo que la persona vino a leer. Dos modales, el mismo
     `style=` escrito dos veces. */
  .hint.frase{font-size:var(--t-md); color:var(--ink); margin-top:var(--e2)}
  /* La ayuda escrita como LISTA: el `*{padding:0}` del arranque le saca a `ul`
     la sangría con la que el navegador deja lugar para los bullets. */
  .hint.lista{margin:8px 0 0 18px}
  /* La ayuda que va ANTES de los campos y no después de uno: el aire va abajo,
     contra lo que viene, y no arriba. */
  .hint.antes{margin-bottom:var(--e3)}
  /* La glosa de una opción marcable: debajo de la frase y alineada con ella,
     no con el control (24px = la casilla más su aire). */
  .hint.glosa{display:block; margin-left:24px}
  /* El renglón que introduce el link del huésped: se despega del bloque de
     arriba y se pega al campo que va abajo. */
  .hint.pie-link{margin:18px 0 8px}
  /* El renglón de navegación de PIE de una pantalla: el par de enlaces del
     login, el «volver» del resumen de la cuenta, el pedir otro código. Venía
     escrito como `.empty` en siete pantallas -- o sea el estilo del "no hay
     nada acá" puesto sobre el único enlace que la pantalla SÍ quiere que
     toques. Mismo caso que `.sub`: las declaraciones son idénticas a las de
     `.empty`, así que el cambio no movió un pixel. Lo que cambia es que el día
     que el pie quiera respirar distinto -- más aire arriba, una línea que lo
     separe del formulario, 13,5px -- no arrastra con él los cuarenta vacíos
     accesorios de la app.
     ⚠ Va DESPUÉS de `.hint` y no antes: la hoja del dueño escribe
     `class="pie hint"`, las dos pesan una clase y el desempate es el orden --
     al revés, ese renglón bajaría de 14px a 12px. */
  .pie{color:var(--mute); padding:var(--e4) 0; font-size:var(--t-md)}
  /* El buscador de una lista (docs/48 fila 15). En una fila con el botón al
     lado, y el input NO a todo el ancho: un campo de 1000px para un apellido
     se lee como un formulario, no como una búsqueda. */
  .buscador{display:flex; gap:8px; align-items:center; margin:var(--e3) 0 var(--e1); flex-wrap:wrap}
  .buscador input[type="search"]{width:auto; flex:1; min-width:200px; max-width:340px; padding:8px 12px}
  .buscador button{margin-top:0; padding:8px 18px; font-size:var(--t-md); min-height:36px}
  /* `#buscador-estado` va FUERA del form (la búsqueda en vivo lo trae por
     `hx-select-oob`): "Limpiar" y el "Mostrando…" en un renglón. */
  #buscador-estado:empty{display:none}
  #buscador-estado{display:flex; gap:var(--e2); align-items:baseline; flex-wrap:wrap}
  .barra #buscador-estado{order:1; flex:0 0 100%}
  #buscador-estado .hint{margin:0}
  #buscador-estado .limpiar{font-size:var(--t-md); text-decoration:none}
  #buscador-estado .limpiar:hover{text-decoration:underline}
  /* La lista de enlaces bajo un buscador (elegir un dueño en la pantalla de comisión). Mismas
     filas que `.pick-menu a`, pero siempre abiertas y con el objetivo táctil de
     44px a 375px: cada fila es un enlace de verdad. */
  .elegir-lista{list-style:none; margin:var(--e2) 0; padding:0}
  .elegir-lista a{display:flex; justify-content:space-between; gap:var(--e2); align-items:center;
    min-height:44px; padding:8px 12px; border-radius:var(--r-sm); text-decoration:none;
    color:var(--ink); font-size:var(--t-md); overflow-wrap:anywhere}
  .elegir-lista a:hover{background:var(--soft)}
  .elegir-lista a[aria-current]{color:var(--terra-text); font-weight:600; background:var(--soft)}
  .elegir-lista .dato{color:var(--mute); white-space:nowrap; font-weight:400}
  /* El otro buscador: el de códigos CAByS del formulario de la FEC manual
     (`gastos.html`). No es la fila de arriba -- vive DENTRO de un formulario y
     su resultado es una pila de opciones que llenan un campo, no enlaces a otra
     pantalla. Cada renglón es un `<button>` y hay que devolverle la silueta de
     texto: el botón de la app es una píldora terracota, que acá serían diez
     píldoras apiladas gritando encima del formulario. */
  .cabys-lista{list-style:none; margin:8px 0 0; border:1px solid var(--line);
    border-radius:var(--r-md); overflow:hidden; max-height:260px; overflow-y:auto}
  .cabys-lista li + li{border-top:1px solid var(--line)}
  .cabys-lista button{display:block; width:100%; margin:0; padding:var(--e2) var(--e3);
    text-align:left; background:var(--bg); color:var(--ink); border:0;
    border-radius:0; font-size:var(--t-md); font-weight:400; line-height:1.45}
  .cabys-lista button:hover{background:var(--soft)}
  .cabys-lista .cabys-cod{font-weight:600; font-variant-numeric:tabular-nums;
    margin-right:8px}
  .cabys-lista .badge{margin-left:8px}
  /* Formularios de una sola pregunta (login, código, recuperación): a todo el
     ancho del wrap el card queda desproporcionado -- un input de 1000px para un
     correo. La columna angosta se lee como formulario, no como página vacía. */
  .auth{max-width:460px; margin:0 auto; padding-top:var(--e5)}
  /* La otra columna angosta: formularios de varios campos y pantallas de puro
     texto (crear cuenta, el recorrido del dueño). No entran en los 460px de
     `.auth` -- ahí la rejilla de dos columnas se aprieta -- pero a todo el ancho
     de un monitor de 27" un renglón de prosa se vuelve ilegible. */
  .angosto{max-width:820px; margin:0 auto}
  /* Rótulo de grupo dentro de una lista de cards (portada de la cuenta): mismo
     tratamiento que los rótulos del nav agrupado y los `label`. */
  .bloque-h{font-size:var(--t-xs); font-weight:600; color:var(--mute); text-transform:uppercase;
    letter-spacing:.06em; margin:var(--e4) 0 8px}
  .bloque-h:first-of-type{margin-top:0}
  /* EL RÓTULO DE COLUMNA. Trece bloques declaraban las mismas seis
     propiedades: la tarjeta del triage, la del recuperado, el campo de la
     retención, la cifra, las columnas de casillas, la cola de dueños, las tres
     filas de rótulos (liquidación, estadías, ocupación), los KPI y el pie de la
     portada. Todos son la misma cosa --el nombre de lo que hay abajo, en
     versalitas apagadas-- y cuando estaban repetidos alcanzaba con que a uno se
     le olvidara el `font-weight` para que la pantalla se leyera distinta.
     Cada selector conserva su peso propio, así que los que lo pisan después
     --`.oc-kpi.mia .rot` en terracota, `.ol-neto-cifra .cifra-rot`-- siguen
     ganando. Los dos que NO entran son `.oc-grupo-cifra .rot` y
     `.rf-emisor-cifra .rot`: van sin `font-weight` a propósito, para que el
     rótulo no compita con el número que tienen debajo. */
  .cifra-rot, .casillas-rot,
  .rf-triage .rot, .rf-recuperado .rot, .rf-retencion .rot,
  .ol-detalle-rci .rot, .ol-recuperado .rot, .oc-kpi .rot,
  .liq-estado > .rot, .pt-mirado > .rot,
  .ol-fila.rotulos > *, .oc-fila.rotulos > *, .pp-cola-rot > *{
    display:block; font-size:var(--t-xs); color:var(--mute); font-weight:600;
    text-transform:uppercase; letter-spacing:.05em;
  }
  /* La píldora de estado. El `.badge` pelado es el rótulo NEUTRO de marca (el
     régimen del emisor, la ruta de una invitación): no dice bien ni mal.
     Los cuatro de abajo son los que sí dicen algo, y son los MISMOS en toda la
     app -- hasta acá cada pantalla elegía su tinte y "aceptado" en una era del
     mismo naranja que "venció" en otra. */
  /* Neutro y no terracota (cáscara moderna 21-sep-2026, maqueta A §1): un
     régimen o un origen no es ni bien ni mal, así que no gasta el color que el
     botón primario necesita para destacar. El mismo gris que ya usaba
     `.badge-neutro`. */
  .badge{display:inline-block; padding:4px var(--e2); border-radius:var(--r-pill); font-size:var(--t-xs); font-weight:600;
    background:rgba(27,38,36,.06); color:var(--mute)}
  /* Bug visto en la maqueta del 21-sep: `.badge{display:inline-block}` (arriba)
     le gana en cascada al `[hidden]{display:none}` del navegador -- misma
     especificidad, esta regla viene después. El punto de la campana se veía
     "vacío" (sin número) en vez de desaparecer cuando no había nada nuevo. */
  .badge[hidden]{display:none}
  .badge-ok{background:var(--ok-bg); color:var(--ok)}
  .badge-warn{background:var(--warn-bg); color:var(--warn)}
  .badge-err{background:var(--err-bg); color:var(--err)}
  /* Lo informativo -- "en curso", "esperando", "quedan 5 días" -- no es un
     estado bueno ni malo, así que no gasta ninguno de los tres colores. */
  .badge-neutro{background:rgba(27,38,36,.06); color:var(--mute)}
  /* El cuarto tinte de `docs/68`. Es distinto de `.badge-neutro`: éste sigue
     siendo gris porque "en curso" no es ni bien ni mal, pero un dato como el
     AMBIENTE en el que corre el proceso ("Estado del sistema", 23-sep-2026) no
     es un juicio de valor -- ni ok ni warn -- y sí conviene que se note que es
     informativo y no un estado de un freno. Mismo par que ya usa
     `.estado-en_curso` (`var(--info)`/`var(--info-bg)`), con nombre propio
     para que una pantalla nueva no tenga que inventar la clase de estado de un
     enum que no tiene. */
  .badge-info{background:var(--info-bg); color:var(--info)}
  /* LA PÍLDORA DE ESTADO del sistema (rediseño 30-sep-2026): punto + palabra,
     24px de alto, y el tinte sale de los mismos cuatro tokens de estado. Es la
     misma silueta que `.badge-*` de arriba -- que las pantallas migran en la
     Etapa 2 -- pero con el punto incluido y sin depender de qué enum la pinta.
     `.neutra` es el quinto: ni bien ni mal, gris sobre crema. */
  .pill{display:inline-flex; align-items:center; gap:6px; height:24px; padding:0 10px;
    border-radius:var(--r-pill); font-size:var(--t-sm); font-weight:600; white-space:nowrap;
    background:var(--crema); color:var(--mute)}
  /* El punto es el de la regla compartida de `.badge-*`/`.estado-*` (más abajo); acá solo se
     le quita el margen, porque el `gap` del `.pill` ya separa. */
  .pill::before{margin:0; flex:none}
  .pill.ok{background:var(--ok-bg); color:var(--ok)}
  .pill.warn{background:var(--warn-bg); color:var(--warn)}
  .pill.err{background:var(--err-bg); color:var(--err)}
  .pill.info{background:var(--info-bg); color:var(--info)}
  .pill.neutra{background:var(--crema); color:var(--mute)}
  /* `.ok`/`.warn`/`.bad`/`.info` globales pintan el filo izquierdo de 3px de las
     tarjetas; sobre una `.pill` le deformaban la silueta (visto en la Etapa 2). */
  .pill.ok, .pill.warn, .pill.err, .pill.info{border-left:0}
  /* El ícono dentro de una píldora (el candado de "Plan completo"): del tamaño del texto. */
  .badge .ico, .pill .ico{width:12px; height:12px; vertical-align:-1px; margin-right:2px}
  /* Los `estado-*` son la misma píldora con el tinte que le toca, y NO se
     renombran: la clase sale del `.value` del enum, o sea que es la identidad
     del estado y no su vestido (ver `app/deps.py`, `operacion_tareas.html` y el
     test de i18n que lo fija). Estaban repartidos en cuatro puntos del archivo
     --tareas, primeros pasos, el recorrido del dueño-- con siete reglas para
     cuatro tintes, y así "hecha" (gris) y "hecho" (verde) llegaron a ser el
     mismo significado pintado de dos maneras.
     RECHAZADO no es "pendiente": no hay nada que esperar, hay que rehacer el
     documento con un `clave` nuevo, y por eso no comparte el ámbar del
     retenido, que sí se destraba solo. */
  .estado-hecho, .estado-hecha{background:var(--ok-bg); color:var(--ok)}
  .estado-pendiente{background:var(--warn-bg); color:var(--warn)}
  .estado-rechazado{background:var(--err-bg); color:var(--err)}
  /* "En camino" (PENDING/SUBMITTING/SUBMITTED/ANNULLED, `CSS_ESTADO_DEFAULT`)
     es el único de los cuatro que de verdad está EN CURSO -- entra al petróleo
     --info en vez del gris neutro (cáscara moderna 21-sep-2026). Esperando y
     bloqueado siguen grises: no son "en curso", están trabados en otra cosa. */
  .estado-en_curso{background:var(--info-bg); color:var(--info)}
  .estado-esperando,
  .estado-bloqueado{background:rgba(27,38,36,.06); color:var(--mute)}
  /* Cáscara moderna 21-sep-2026: la tarjeta flota sobre --bg-app con sombra en
     vez de competir por un hairline -- blanca, radio 16, sin borde. */
  .card{background:var(--bg); border:0; border-radius:var(--r-lg); box-shadow:var(--sh-float); padding:var(--e4); margin-bottom:var(--e3)}
  /* La card que abre un bloque nuevo de la pantalla: se despega de lo de
     arriba, que una card pegada a la anterior se lee como parte de ella. */
  .card.despegada{margin-top:18px}
  .card a{text-decoration:none; font-weight:600}
  /* El card con el filo de marca y nada más: un aviso de una línea. No es
     `.card-aviso` --ése además tiñe el fondo y engorda el padding, y es el que
     ABRE una pantalla--; éste es el que aparece después de una acción. */
  .card.destacada{box-shadow:var(--sh-float), inset 3px 0 0 var(--terra)}
  /* Los PESOS de la card. `.card` es el único recipiente de la app, así que sin
     modificadores el aviso que abre la portada y el enlace al estado de la
     suscripción son el mismo rectángulo del mismo tamaño. `.ok` y `.bad` ya
     ponen un filo de 3px con su token, pero un filo no es jerarquía: es color
     sin peso. */
  /* El que ABRE la pantalla: una por pantalla, arriba de todo, y solo si hay
     algo que hacer. El fondo es el tinte de estado en vez de `--soft`, así que
     el estado se lee antes que el texto. El color no lo elige esta regla: lo
     trae la `.ok`/`.bad`/`.warn` que la plantilla ya escribe, y por eso las
     tres variantes son la misma regla con otro token. Sin ninguna de las tres
     queda terracota, que es el aviso de marca (el criterio de `.badge` pelado). */
  /* Sin `.card`'s hairline (que ya no existe, cáscara moderna 21-sep-2026): la
     tinta de estado se dice con un filo IZQUIERDO de 3px, no un borde de las
     cuatro caras -- el resto de la silueta la pone `.card` (blanca, radio 16,
     sombra). */
  .card-aviso{background:var(--brand-tint); box-shadow:var(--sh-float), inset 3px 0 0 var(--terra);
    padding:var(--e4)}
  .card-aviso.bad{background:var(--err-bg); box-shadow:var(--sh-float), inset 3px 0 0 var(--err)}
  .card-aviso.warn{background:var(--warn-bg); box-shadow:var(--sh-float), inset 3px 0 0 var(--warn)}
  /* La excepción es `.ok`: en Hoy, "nada pide tu atención" no es un banner que
     abre la pantalla -- es un renglón tranquilo con una barra izquierda, sin
     tinte de fondo ni sombra (maqueta A §7, ⚖ fundador 21-sep-2026: "todo
     bien" no compite visualmente con lo que sí hay que atender). */
  .card-aviso.ok{background:none; box-shadow:none; border:1px solid var(--line);
    border-left:3px solid var(--ok); padding:14px 18px}
  /* La primera línea del aviso es el titular de la pantalla, no un renglón
     más: 17px contra los 14 del cuerpo. Va por `strong` porque es lo que las
     plantillas ya ponen ahí. */
  .card-aviso > strong{display:block; font-size:var(--t-lg); font-weight:600;
    letter-spacing:-.015em; margin-bottom:5px}
  /* El renglón de "todo bien" no necesita el mismo peso que un banner que abre
     la pantalla: el título baja al tamaño del cuerpo. */
  .card-aviso.ok > strong{font-size:var(--t-md)}
  .card-aviso.ok .hint{margin-top:2px}
  /* El `.hint` de adentro no se apaga tanto como en una card normal: sobre un
     fondo tintado, `--mute` pierde contraste. El renglón `.ok` NO está tintado,
     así que se queda con el `--mute` de siempre. */
  .card-aviso:not(.ok) .hint{color:var(--ink); opacity:.8}
  /* En papel el aviso no se imprime tintado: el color de fondo o no sale o sale
     gris. Conserva el filo, que es la señal que sí sobrevive. */
  @media print{
    .card-aviso{background:none}
  }
  /* "Todo bien" en Hoy (⚖ fundador 24-sep-2026): reemplaza a `.card-aviso.ok`,
     que seguía siendo una caja con filo aunque ya no tuviera tinte de fondo.
     Sin nada urgente no hace falta un marco -- un renglón con su tilde en
     --ok (el color no va solo) y el texto en --ink, que es la misma tinta del
     cuerpo, alcanza. */
  .hoy-al-dia{
    display:flex; align-items:baseline; gap:8px; margin-bottom:var(--e4);
    font-size:var(--t-md); color:var(--ink);
  }
  .hoy-al-dia-tick{color:var(--ok); font-weight:600}
  .grid{display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:var(--e3)}
  /* El campo que ocupa las dos columnas de la rejilla. El nombre no es nuevo:
     `factura_publica.html` ya llamaba `.ancho` a esto en su hoja propia. */
  .grid > .ancho{grid-column:1 / -1}

  /* ---------- forms ---------- */
  label{display:block; font-size:var(--t-xs); color:var(--mute); text-transform:uppercase; letter-spacing:.05em; margin-bottom:5px; font-weight:600}
  /* El asterisco de "obligatorio" (⚖ fundador 27-sep-2026): un solo patrón,
     junto al rótulo del campo. Nunca lleva su propio texto -- lo explica la
     leyenda de arriba del formulario ("Los campos con * son obligatorios"). */
  .req{color:var(--err); margin-left:2px}
  /* El `textarea` va en la MISMA regla y no en una copia: cuando tenía la suya
     --idéntica, escrita aparte-- el día que el input cambiara de padding los dos
     controles dejaban de coincidir. Es exactamente lo que ya había pasado con
     los `style=` que cada plantilla le copiaba al textarea. */
  input, select, textarea{width:100%; padding:11px 12px; border-radius:var(--r-sm); font-size:var(--t-md); font-family:inherit;
    background:var(--bg); color:var(--ink); border:1px solid var(--line-input); transition:border-color .15s ease}
  input:focus, select:focus, textarea:focus{outline:none; border-color:var(--accent); box-shadow:var(--focus-ring)}
  /* Error de campo (rediseño 30-sep-2026): borde `--err` y, debajo, el mensaje
     (`.campo-error`) -- el color nunca va solo, el texto dice qué pasó. La
     plantilla marca el control con `aria-invalid="true"` (o `.invalido`) y
     escribe el mensaje en el elemento hermano. */
  input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"],
  input.invalido, select.invalido, textarea.invalido{border-color:var(--err)}
  input[aria-invalid="true"]:focus, select[aria-invalid="true"]:focus, textarea[aria-invalid="true"]:focus,
  input.invalido:focus, select.invalido:focus, textarea.invalido:focus{border-color:var(--err)}
  .campo-error{margin-top:var(--e1); color:var(--err); font-size:var(--t-sm); font-weight:500; line-height:1.4}
  /* El control segmentado: dos a cuatro opciones excluyentes en una píldora con
     borde (mensual / anual, semana / mes). Cada opción es un `label` que
     envuelve su radio; el seleccionado se rellena en océano. Sin JavaScript. */
  .segmentado{display:inline-flex; gap:2px; padding:3px; border:1px solid var(--line-input);
    border-radius:var(--r-pill); background:var(--bg); vertical-align:middle}
  .segmentado > label, .segmentado > a, .segmentado > button{
    display:inline-flex; align-items:center; margin:0; min-height:36px; padding:0 16px;
    border:0; border-radius:var(--r-pill); background:none; color:var(--mute);
    font-size:var(--t-sm); font-weight:600; text-transform:none; letter-spacing:0;
    text-decoration:none; cursor:pointer}
  .segmentado > label input{position:absolute; opacity:0; width:1px; height:1px}
  .segmentado > .on, .segmentado > label:has(input:checked){background:var(--accent); color:#fff}
  .segmentado > label:has(input:focus-visible){outline:3px solid var(--accent-hover); outline-offset:2px}
  /* El teléfono del registro: selector de país + número, en una sola fila. El
     select se queda con su ancho de contenido (el nombre del país más largo) y
     el número se lleva el resto -- al revés, el campo donde se escribe queda
     apretado en un teléfono. */
  .tel{display:flex; gap:8px}
  .tel select{width:auto; flex:0 1 auto; min-width:0}
  .tel input{flex:1 1 auto; min-width:0}
  /* El `label` que ENVUELVE su casilla o su radio. El `label` de la app es el
     rótulo de un campo --versalitas, gris, 11,5px-- y acá la etiqueta ES la
     frase que se marca, así que se le devuelve la caja de texto. Estaba escrito
     doce veces con el mismo `style=`, y el control adentro otras doce con el
     mismo `width:auto`.
     Dos tamaños porque son dos cosas: `.marcable` es una opción de una lista y
     `.chica` la frase de una confirmación, que suele ser un párrafo entero. */
  label.marcable{text-transform:none; font-size:var(--t-md); color:var(--ink); font-weight:400}
  label.marcable.chica{font-size:var(--t-md)}
  /* El control no se estira: al lado de su frase, no arriba de ella. */
  label.marcable input{width:auto; margin-right:8px}
  /* Cada opción de la lista de marcables, con su glosa adentro: respira entre
     una y la siguiente para que la glosa se lea pegada a SU frase. */
  .opcion{margin-bottom:var(--e2)}
  /* El link de reserva directa adentro de la fila de su puerta: es para
     copiar, no para escribir, así que toma el ancho de lo que muestra y no el
     del renglón. */
  .link-directo{width:220px; font-size:var(--t-sm); padding:5px 8px; margin-right:var(--e1)}
  /* El `textarea` de la plantilla de mensajes: lo único suyo es el padding --el
     resto de su `style=` era una copia palabra por palabra de la regla de
     arriba, o sea siete declaraciones que ya estaban dichas. */
  textarea.compacto{padding:9px 11px}
  /* Dos acciones de una misma fila son dos `<form>`, y un `<form>` es un
     bloque: sin esto el segundo botón cae debajo del primero. */
  form.en-linea{display:inline}
  /* El renglón de controles: el `select` con el campo que lo acompaña, el
     campo con su botón de copiar, el filtro con su «aplicar». Alineados por
     ABAJO porque cada control trae su rótulo arriba y los rótulos no miden
     igual; centrados cuando no hay rótulos y lo que se alinea es el texto.
     Estaba escrito seis veces a mano --en cinco plantillas-- con la misma
     forma y el gap elegido de a uno. */
  .fila-campos{display:flex; gap:var(--e1); align-items:flex-end}
  .fila-campos.aire{gap:8px}
  .fila-campos.centrada{align-items:center}
  /* Opt-in, no el default: `.fila-campos` mide su ancho por el más largo de
     sus renglones de siempre (input + botón), y la mayoría entra bien en un
     teléfono. La de la contraoferta agrega un código de moneda en el medio
     (`solicitudes.html`) y ahí sí no entra -- envuelve solo donde se pide. */
  .fila-campos.envolvente{flex-wrap:wrap}
  /* Quién se lleva el ancho sobrante del renglón y quién se queda con el suyo. */
  .fila-campos > .crece{flex:1}
  .fila-campos > .fijo{flex:0 0 auto}
  /* Dos anchos de campo corto, porque son dos cosas: `.corto` es el campo de
     TEXTO de una fila (el motivo de un rechazo) y `.numerico` el que solo lleva
     una cifra (el valor de un recargo, adentro de la celda de una tabla). Sin
     esto un `input` se lleva el renglón entero, que es su ancho por defecto. */
  .fila-campos > .corto{width:180px}
  .fila-campos > .numerico{max-width:110px}
  /* La fila de DECISIONES de una tarjeta (aceptar / rechazar / pre-aprobar).
     Los botones que van adentro pierden el `margin-top` del botón suelto: en
     una fila el aire lo pone la fila, no cada botón -- estaba anulado a mano
     una vez por botón. */
  /* `wrap`: con un botón MÁS un formulario chico adentro (la contraoferta de
     `solicitudes.html`, o el rechazo del widget) la fila no entra en un
     teléfono -- sin esto el botón de más a la derecha queda clipeado por el
     `overflow` de la tarjeta en vez de bajar de línea. */
  .fila-acciones{display:flex; flex-wrap:wrap; gap:var(--e2); margin-top:var(--e3)}
  /* El `fieldset` sin vestido: se usa para AGRUPAR --un solo `disabled` que
     apaga el formulario entero, un `legend` que hace de rótulo-- y no para
     dibujar el marco que el navegador le pone solo. Tres pantallas pedían lo
     mismo con tres declaraciones idénticas. */
  fieldset.opciones, .ari-fieldset, .fieldset-plano{border:0; padding:0; margin:0; min-width:0}
  /* El `summary` de un desplegable de segundo plano: se ve como una ayuda --gris
     y chico-- porque lo que esconde es material de rescate, no el camino. */
  summary.tenue{cursor:pointer; color:var(--mute); font-size:var(--t-md)}

  /* ---------- panel de la reserva en el hilo de mensajes (⚖ fundador
     27-sep-2026) ---------- */
  /* El reparto: el hilo a la izquierda con su propio tope de ancho (`.chat`,
     más abajo) y el panel a la derecha, angosto y fijo. En una pantalla
     angosta se apilan -- el panel pasa a un `details` como único mecanismo de
     colapso, sin depender de JS. */
  .hilo-layout{display:flex; align-items:flex-start; gap:var(--e5)}
  .hilo-principal{flex:1; min-width:0}
  .hilo-panel{flex:0 0 260px; width:260px; border:1px solid var(--line);
    border-radius:var(--r-md); padding:var(--e3); font-size:var(--t-sm)}
  /* Mismo mecanismo que `.ayuda`: el summary es el gatillo del colapso en
     móvil, y el `<details>` NO lleva `open` en el HTML (`operacion_mensaje_
     hilo.html`) -- así que en el teléfono arranca colapsado. En escritorio no
     hace falta gatillo -- el panel va siempre abierto y visible --, así que
     ahí se esconde el summary y se fuerza el contenido a mostrarse aunque el
     atributo `open` no esté puesto (regla de abajo, sin JS). */
  .hilo-panel > summary{font-weight:600; color:var(--ink); cursor:pointer;
    list-style:none}
  .hilo-panel > summary::-webkit-details-marker{display:none}
  .hilo-panel dl{margin:var(--e2) 0 0; display:grid; grid-template-columns:auto 1fr;
    gap:4px var(--e2)}
  .hilo-panel dt{color:var(--mute)}
  .hilo-panel dd{margin:0}
  .hilo-panel p{margin:var(--e2) 0 0}
  @media (min-width:861px){
    .hilo-panel > summary{display:none}
    .hilo-panel:not([open]) > :not(summary){
      display:block !important;
      content-visibility:visible !important;
    }
  }
  @media (max-width:860px){
    .hilo-layout{flex-direction:column}
    .hilo-panel{width:100%; flex:none}
  }

  /* ---------- chat de mensajes con huéspedes ---------- */
  /* Una conversación, no un log: la burbuja del huésped a la izquierda sobre
     --soft, la del anfitrión a la derecha con el tinte de marca. El ancho se
     acota porque una línea de chat que cruza la pantalla entera no se lee. Los
     tintes salen de --terra vía color-mix para no acuñar hex nuevos. */
  .chat{max-width:680px}
  .chat .msg{margin-bottom:var(--e3); display:flex; flex-direction:column; align-items:flex-start}
  .chat .msg.mia{align-items:flex-end}
  .chat .quien{font-size:var(--t-sm); color:var(--mute); margin:0 4px 4px}
  .chat .burbuja{background:var(--soft); border:1px solid var(--line);
    border-radius:var(--r-md); border-top-left-radius:var(--r-xs); padding:9px var(--e3);
    max-width:85%; white-space:pre-wrap; overflow-wrap:anywhere}
  .chat .burbuja p{margin:0}
  .chat .msg.mia .burbuja{
    background:color-mix(in srgb, var(--terra) 7%, var(--bg));
    border-color:color-mix(in srgb, var(--terra) 22%, var(--line));
    border-top-left-radius:var(--r-md); border-top-right-radius:var(--r-xs)}
  .chat .adjunto{font-size:var(--t-sm); color:var(--mute); margin:4px 4px 0}
  .responder{max-width:680px}
  .responder textarea{resize:vertical}
  /* El preview de la bandeja no manda sobre el ancho de la tabla. */
  td.msg-preview{max-width:300px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:var(--mute)}
  /* El filtro por puerta de la bandeja: un renglón, no un formulario. El
     `label` va al lado del control y con el tamaño de una etiqueta de fila. */
  .msg-filtro{display:flex; align-items:center; gap:8px; flex-wrap:wrap;
    margin:0 0 var(--e3)}
  .msg-filtro label{margin:0; font-size:var(--t-sm); color:var(--mute)}
  .msg-filtro select{width:auto; margin:0}
  /* Cada reseña dentro de la tarjeta de su puerta: separadas por la hairline de
     siempre, sin caja propia -- la caja ya es la puerta. El texto del huésped
     respeta los saltos que él escribió y corta las palabras larguísimas, misma
     regla que la burbuja del chat. */
  .resena{border-top:1px solid var(--line); padding:var(--e3) 0}
  .resena:first-of-type{border-top:0}
  .resena p{white-space:pre-wrap; overflow-wrap:anywhere}
  .resena form{max-width:680px; margin-top:var(--e2)}
  .resena textarea{resize:vertical}

  /* Caja de dinero: el símbolo va DENTRO del campo, pegado al borde izquierdo,
     y el número se alinea a la derecha con cifras tabulares -- que es como se
     lee una columna de plata y por qué existe el "formato contabilidad". El
     símbolo lo pinta la plantilla con el de la moneda REAL del campo (₡, $, €):
     un ₡ fijo sobre un monto en dólares es el error de factor ~500x que
     `docs/34` §1.5 llama bloqueante. Sin JS: es un span posicionado, así que
     no hay nada que se pueda desincronizar del valor que se envía. */
  .money{position:relative; display:block}
  .money > .money-sym{
    position:absolute; left:12px; top:50%; transform:translateY(-50%);
    font-size:var(--t-md); color:var(--mute); pointer-events:none; font-variant-numeric:tabular-nums}
  .money > input{
    padding-left:32px; text-align:right;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  /* Moneda de 3 letras (una que no tenga símbolo): necesita más aire. */
  .money.money-cod > .money-sym{font-size:var(--t-sm); font-weight:600}
  .money.money-cod > input{padding-left:46px}
  /* El botón nativo del input de archivo (importar) lo pinta el navegador con
     su gris de sistema y en su idioma; el texto no es nuestro, pero el vestido
     sí puede ser el de los demás controles. El selector va sin el atributo a
     propósito: el pseudo-elemento solo existe en inputs de archivo, y escribir
     el atributo literal en el CSS de base dispararía el test que prueba que el
     recorrido del dueño no ofrece dónde subir un archivo. */
  input::file-selector-button{
    padding:var(--e1) var(--e3); margin-right:var(--e2); border:1px solid var(--line); border-radius:var(--r-pill);
    background:var(--bg); color:var(--ink); font-family:inherit; font-size:var(--t-md); font-weight:500;
    cursor:pointer; transition:border-color .15s ease}
  input::file-selector-button:hover{border-color:var(--accent)}
  /* ---------- botones (rediseño 30-sep-2026, docs/96 etapa 1b) ----------
     Cinco niveles y un solo molde: píldora de 44px (`.btn-chico`, 36px), con el
     ícono opcional al lado del rótulo. Cada nivel es una clase y NADA más:
       .btn          primario -- relleno océano, uno por bloque
       .btn-sec      secundario -- blanco con borde de 3:1 (`--line-input`)
       .btn-marca    destacado ámbar, solo adquisición y onboarding
       .btn-enlace   terciario -- navegar, no ejecutar
       .btn-peligro  baja o borrado: contorno rojo, nunca relleno
       .btn-icono    círculo de 36px con borde (menú de fila, cerrar)
     Un `<button>` SIN clase es un secundario neutro: ya no hereda el primario
     gigante de antes ni el `margin-top` que obligaba a anularlo en cada fila.
     La excepción es el `submit` sin clase, que sigue siendo primario (es la
     acción del formulario; hay un centenar así hasta que la Etapa 2 les ponga
     su nivel). Las bases van en `:where()` para que cualquier regla de
     contexto (`.buscador button`, `.menu-usuario .pick-menu button`) les gane
     sin pelear especificidad. */
  :where(button, .btn, .btn-sec, .btn-marca, .btn-enlace, .btn-peligro, .btn-icono, .btn-chico){
    display:inline-flex; align-items:center; justify-content:center; gap:8px; text-align:center;
    min-height:44px; padding:0 20px; border:1px solid var(--line-input); border-radius:var(--r-pill);
    background:var(--bg); color:var(--ink); font-family:inherit; font-size:var(--t-md); font-weight:600;
    line-height:1.2; text-decoration:none; cursor:pointer;
    transition:background .15s ease, border-color .15s ease, color .15s ease}
  :where(button, .btn, .btn-sec, .btn-marca, .btn-enlace, .btn-peligro, .btn-icono, .btn-chico):hover{background:var(--crema)}
  /* El primario. `.btn` va con su especificidad de clase y no en `:where()`:
     el `a{color}` global (más abajo en la lectura, mismo peso que un
     elemento) le ganaría el blanco a un `<a class="btn">`. */
  :where(button[type="submit"]:not([class]), button.btn-chico){
    background:var(--accent); color:#fff; border-color:transparent}
  :where(button[type="submit"]:not([class]), button.btn-chico):hover{background:var(--accent-hover)}
  :where(button[type="submit"]:not([class]), button.btn-chico):active{background:var(--accent-active)}
  .btn{background:var(--accent); color:#fff; border-color:transparent}
  .btn:hover{background:var(--accent-hover)}
  .btn:active{background:var(--accent-active)}
  :where(button, .btn, .btn-sec, .btn-marca, .btn-enlace, .btn-peligro, .btn-icono, .btn-chico):focus-visible{
    outline:3px solid var(--accent-hover); outline-offset:2px}   /* el color de `--focus-ring` */
  .btn-sec{background:var(--bg); color:var(--ink); border-color:var(--line-input)}
  .btn-sec:hover{background:var(--crema)}
  .btn-marca{background:var(--brand); color:var(--brand-ink); border-color:transparent}
  .btn-marca:hover{background:var(--brand); filter:brightness(.94)}
  .btn-peligro{background:var(--bg); color:var(--err);
    border-color:color-mix(in srgb, var(--err) 45%, transparent)}
  .btn-peligro:hover{background:var(--err-bg); border-color:var(--err)}
  /* Terciario: se lee como enlace (texto océano, subrayado en hover) pero es
     un `button` o un `a` con el blanco de toque de un botón. Un `button` que
     hace un POST por fila pesa como los demás enlaces de la fila, no como el
     botón de la pantalla. */
  .btn-enlace{background:none; color:var(--accent); border-color:transparent; min-height:0;
    padding:0 6px; border-radius:var(--r-sm)}
  .btn-enlace:hover{background:var(--brand-tint); text-decoration:underline; text-underline-offset:3px}
  .btn-icono{width:36px; min-height:36px; padding:0; border-radius:50%; border-color:var(--line-strong)}
  .btn-icono:hover{background:var(--crema); border-color:var(--line-input)}
  .btn svg, .btn-sec svg, .btn-marca svg, .btn-peligro svg, .btn-icono svg{width:20px; height:20px; flex:none}
  /* Chico: el que va DENTRO de una fila de tabla o al lado de un input. Nunca
     menos de 36px de alto -- el piso táctil de lo que se toca con el pulgar. */
  .btn-chico{min-height:36px; padding:0 14px; font-size:var(--t-sm); gap:6px}
  .btn-chico svg{width:18px; height:18px}
  .btn-enlace.btn-chico{min-height:0; padding:0 6px}
  /* Deshabilitado: el mismo molde a 45%, sin hover. */
  button:disabled, .btn[disabled], .btn[aria-disabled="true"], .btn-sec[disabled], .btn-marca[disabled],
  .btn-peligro[disabled], .btn-enlace[disabled], .btn-icono[disabled]{opacity:.45; cursor:not-allowed}
  /* El `submit` o el botón que sigue a un campo dentro de un formulario se
     despega del campo. Era el `margin-top` global de `button`; acá solo vale
     para el hermano que SIGUE a un control o a un texto (`+`), y con
     especificidad cero: una fila (`.inline-form`, `.buscador`) lo anula con
     lo que ya traía. */
  :where(form > :is(input:not([type="hidden"]), select, textarea, label, .money, p, .hint, .ayuda, fieldset, div)
    + :is(button, .btn)){margin-top:var(--e3)}
  /* Opciones en tarjeta (signup): una pregunta que segmenta al cliente no se
     esconde en un `select`. El `legend` lleva el mismo tratamiento que un
     `label` para que el formulario se lea parejo. */
  fieldset.opciones legend{font-size:var(--t-xs); color:var(--mute); text-transform:uppercase;
    letter-spacing:.05em; margin-bottom:var(--e1); font-weight:600}
  fieldset.opciones .lista{display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:var(--e2)}
  /* Dos opciones (el plan y el ciclo de cobro del wizard) no llenan la grilla de
     tres: quedaban dos tarjetas y un hueco a la derecha. Va ANTES de la media
     query de mobile a propósito -- misma especificidad, así que ahí abajo la
     regla de `.lista` gana igual y las dos se apilan. */
  fieldset.opciones .lista-2{grid-template-columns:repeat(2, minmax(0,1fr))}
  fieldset.opciones .opcion{
    display:block; margin:0; padding:12px var(--e3); cursor:pointer;
    background:var(--bg); border:1px solid var(--line); border-radius:var(--r-md);
    font-size:var(--t-md); font-weight:400; color:var(--ink); text-transform:none; letter-spacing:0;
    transition:border-color .15s ease;
  }
  fieldset.opciones .opcion:hover{border-color:var(--accent)}
  fieldset.opciones .opcion:focus-within{border-color:var(--accent)}
  /* La elegida se marca con el borde ADEMÁS del radio: el color no es el único
     portador, el punto del radio sigue ahí. */
  fieldset.opciones .opcion:has(input:checked){border-color:var(--accent);
    box-shadow:inset 0 0 0 1px var(--accent)}
  fieldset.opciones .opcion input{width:auto; margin-right:8px; accent-color:var(--accent)}
  fieldset.opciones .op-t{font-weight:600; letter-spacing:-.01em}
  fieldset.opciones .op-d{display:block; color:var(--mute); font-size:var(--t-sm);
    margin-top:var(--e1); line-height:1.45}
  /* El rol que el plan no incluye (Equipo, ⚖ fundador 22-sep-2026): sigue
     mostrándose -- no desaparece del selector -- pero apagado y sin poder
     elegirse. Misma opacidad que `.card.bloqueada`; el candado real es el
     `disabled` del input y la guarda del POST, esto es solo el vestido. */
  fieldset.opciones .opcion-bloqueada{opacity:.6; cursor:not-allowed}
  fieldset.opciones .opcion-bloqueada:hover{border-color:var(--line)}
  /* La regla de la card de resultado. `.ok` era terracota, o sea el MISMO color
     con el que la app marca lo que es marca y lo que pide atención: la card que
     dice "puerta conectada" y la que dice "falló" se distinguían solo por el
     texto. Ahora cada una toma su token. */
  .ok{border-left:3px solid var(--ok)}
  .bad{border-left:3px solid var(--err)}
  /* Ni éxito ni error: el empujón completo salió, pero algo quedó a medias
     (`MSG_PUSH_TARIFAS_INCOMPLETAS`, 27-sep-2026) y el host tiene que
     actuar. Mismo criterio de token que `.pt-bloque-cab.warn`. */
  .warn{border-left:3px solid var(--warn)}
  /* El modal de confirmación (elemento `dialog` nativo, ver _canal_ota_modal.html).
     El `::backdrop` solo lo pinta el navegador cuando el diálogo se abrió con
     `showModal()`; con JS apagado el diálogo sale igual, en flujo, y el
     formulario funciona lo mismo. */
  /* Los dos `auto` del margen centran el diálogo, y **ninguno de los dos lo
     hace el navegador solo**: el reset de arriba pone `margin:0` en todo, así
     que la regla de agente de usuario que centra un `dialog:modal` (`margin:
     auto` con `inset:0`) queda pisada. Sin `margin-inline` el diálogo se pega
     al borde izquierdo; sin `margin-block` se pega al de ARRIBA -- visto a ojo
     el 26-ago, con el pop-up de la oferta arrancando en `y=0` sobre una
     ventana de 720px. En el camino SIN JS el diálogo cae en flujo y ahí
     `margin-block:auto` computa 0, así que no lo mueve. */
  dialog{max-width:560px; width:calc(100% - 32px); margin:auto;
    border:1px solid var(--line); border-radius:var(--r-md);
    padding:var(--e4) 24px; background:var(--bg); color:var(--ink); font-family:inherit}
  /* El estado y su glifo no se parten en dos líneas: leerlo de un vistazo es
     justamente el punto (`docs/40` punto 5). */
  .estado-canal{white-space:nowrap}
  dialog::backdrop{background:rgba(11,52,61,.45)}
  /* El título del modal arranca pegado al borde de arriba: el margen superior
     del `h2` global acá sería aire doble contra el padding del diálogo. */
  dialog h2{font-size:var(--t-h2); font-weight:600; letter-spacing:-.02em; line-height:1.25; margin-top:0}

  dl.kv{display:grid; grid-template-columns:auto 1fr; gap:var(--e1) 16px; font-size:var(--t-md); margin-top:var(--e2)}
  dl.kv dt{color:var(--mute)}

  /* ========== el bloque por cuenta de OTA (canales.html, 24/25-sep-2026) ==== */
  /* Cada fila es un anuncio ya conectado, y el botón de "Desconectar" -- la
     única acción que ofrece -- tiene que verse en cualquier ancho, sin scroll
     horizontal. Por eso NO es una tabla HTML: mismo motivo que `.liq-fila` y
     `.cmp-linea` (24-sep-2026, revisión del fundador sobre la primera
     versión con tabla + `.tabla-scroll`, que desbordaba la pantalla
     ENTERA en 390px). El nombre de la puerta y el título del anuncio los
     escribió el host y pueden ser largos: un ancho de columna fijo obliga a
     elegir entre cortarlos o desbordar, y acá la fila envuelve en flujo. */
  .ota-cuenta-lista{border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden}
  .ota-cuenta-fila{display:flex; flex-wrap:wrap; align-items:center; gap:var(--e2) var(--e4);
    padding:var(--e3) var(--e4); border-bottom:1px solid var(--line)}
  .ota-cuenta-fila:last-child{border-bottom:0}
  .ota-cuenta-fila:hover{background:var(--soft)}
  /* La puerta y el anuncio se quedan con el aire que sobra -- son el dato más
     largo de la fila -- y el resto se acomoda a la derecha mientras entre. */
  .ota-cuenta-anuncio{flex:1 1 220px; min-width:0}
  .ota-cuenta-datos{display:flex; flex-wrap:wrap; align-items:center; gap:var(--e2) var(--e3)}
  .ota-cuenta-sync{font-size:var(--t-sm); color:var(--mute); white-space:nowrap}
  /* La nota del estado "apagado" (por qué Sincroniza sigue diciendo lo que
     dice aunque el canal no esté activo): ancho completo, debajo de la fila,
     nunca compitiendo por el mismo renglón que el badge. */
  .ota-cuenta-nota{flex:0 0 100%; margin:0}
  .ota-cuenta-accion{margin-left:auto}
  @media (max-width:640px){
    /* La fila se apila: puerta y anuncio arriba, Sincroniza + Estado debajo,
       el botón al final -- siempre en flujo, nunca atrás de un scroll. */
    .ota-cuenta-fila{flex-direction:column; align-items:stretch}
    /* Sin este reset, el `flex:1 1 220px` de arriba -- pensado para repartir
       ANCHO en una fila horizontal -- pasa a repartir ALTO en la columna: la
       puerta quedaba con 220px de alto y un hueco vacío antes de Sincroniza.
       Apilada, cada bloque mide lo que su contenido pide y nada más. */
    .ota-cuenta-anuncio{flex:0 0 auto}
    .ota-cuenta-accion{margin-left:0}
    /* Piso táctil de móvil en el único control de la fila. */
    .ota-cuenta-accion .btn-chico{display:inline-block; min-height:36px; line-height:36px}
  }
  dl.kv dd{word-break:break-all}

  /* ---------- gráficos ---------- */
  /* Los tres gráficos de `_graficos.html`. En el `style=` de cada elemento
     queda lo que CAMBIA por dato --la posición y el ancho de una barra, el
     color de una porción, el resaltado del mes en pantalla--; lo que baja acá
     es todo lo que era igual en cada corrida: tamaños, hairlines y tipografías.
     El macro decía "sin CSS global a propósito", y esa era la razón por la que
     veintitrés declaraciones se copiaban a mano en un archivo de 165 líneas. */
  .gr-serie{width:100%; height:auto; display:block; overflow:visible}
  /* El tope de la escala y la línea del cero: sin las dos, una columna alta no
     dice contra qué es alta. */
  .gr-guia{stroke:var(--line)}
  /* Sin dato NO es cero: la marca punteada sobre la línea del cero. */
  .gr-sindato{stroke:var(--mute)}
  .gr-cifra{font-size:var(--t-xs); font-weight:600; fill:var(--ink)}
  .gr-eje{font-size:var(--t-xs)}
  /* El anillo y su lista de porciones, uno al lado del otro. */
  .gr-torta{display:flex; flex-wrap:wrap; gap:24px; align-items:center; margin:12px 0 2px}
  .gr-anillo{width:172px; height:172px; flex:none; display:block}
  /* El filo que separa una porción de la siguiente es el color del fondo. */
  .gr-porcion{stroke:var(--bg); stroke-width:1.5}
  .gr-hueco{fill:var(--bg)}
  .gr-centro{font-size:var(--t-md); font-weight:600; fill:var(--ink)}
  .gr-lista{flex:1; min-width:220px}
  .gr-parte{display:flex; align-items:baseline; gap:8px; padding:5px 0; border-top:1px solid var(--line)}
  /* El cuadradito de color de una porción y el de una leyenda son DOS clases y
     no una: uno es hijo de un flex y el otro va en medio de un renglón de
     texto, así que no pueden compartir el `display`. */
  .gr-punto{width:9px; height:9px; border-radius:var(--r-xs); flex:none}
  .gr-punto-txt{width:9px; height:9px; border-radius:var(--r-xs); display:inline-block}
  .gr-parte-t{font-size:var(--t-md); flex:1}
  .gr-parte-v{font-size:var(--t-md); white-space:nowrap}
  .gr-parte-pct{font-size:var(--t-xs); color:var(--mute); white-space:nowrap; width:52px; text-align:right}
  /* Las barras horizontales de comparación: HTML y no SVG, porque la etiqueta
     es un nombre que puso el host y tiene que cortarse como texto. */
  .gr-comp{margin:12px 0 2px}
  .gr-leyenda{display:flex; gap:18px; font-size:var(--t-xs); color:var(--mute); margin-bottom:var(--e2)}
  /* Hijo DIRECTO: adentro de cada renglón de la leyenda va su cuadradito, y un
     descendiente suelto le ganaría el `display` a `.gr-punto-txt`. */
  .gr-leyenda > span{display:inline-flex; align-items:center; gap:var(--e1)}
  .gr-fila{display:grid; grid-template-columns:minmax(80px, 20%) 1fr; gap:var(--e3);
    align-items:center; padding:5px 0; border-top:1px solid var(--line)}
  .gr-fila-t{font-size:var(--t-md); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
  /* La barra en una columna y su número en otra de ancho fijo: con los dos en
     la misma, la barra al 100% de la escala empujaba el número fuera de la
     pantalla. */
  .gr-barra{display:grid; grid-template-columns:1fr 132px; gap:var(--e2); align-items:center; margin:3px 0}
  .gr-pista{height:9px}
  .gr-relleno{display:block; height:9px; border-radius:var(--r-xs); min-width:1px}
  .gr-barra-v{font-size:var(--t-xs); color:var(--mute); white-space:nowrap}
  .gr-nota{font-size:var(--t-xs); color:var(--mute)}
  /* El rótulo que va ARRIBA de un gráfico y dice qué se está mirando. Es la
     misma letra de ayuda, pero ABRE el bloque en vez de cerrarlo: no respira
     arriba, respira abajo. */
  .hint.gr-rot{margin:0 0 4px}

  /* ---------- calendario ---------- */
  .cal-nav{display:flex; align-items:center; gap:12px; margin:var(--e3) 0}
  /* Mes anterior / mes siguiente: eran cajitas de 26px de alto y son el control
     que más se toca del calendario. Suben al mismo piso de 36px que `.btn-chico`. */
  /* Desde el 28-sep visten como `.btn-sec` (fondo --bg, tinta --ink) y no como
     texto apagado con hairline: sobre la arena de `body` un enlace en --mute con
     borde --line se leía como texto suelto, no como botón. El borde es
     --line-input, el único token que pasa 3:1 sobre arena (WCAG 1.4.11): la
     queja era que no se distinguían. El radio de 8px y el piso de 36px se
     quedan, porque `.oc-mes-paso` y `.rf-cab-mes .paso` copian esta silueta. */
  .cal-nav a{text-decoration:none; color:var(--ink); background:var(--bg); font-weight:600;
    padding:8px var(--e3); border:1px solid var(--line-input);
    border-radius:var(--r-sm); font-size:var(--t-md); min-height:36px; display:inline-flex; align-items:center}
  .cal-nav a:hover{border-color:var(--accent); color:var(--accent)}
  .cal-nav strong{font-weight:600; font-size:var(--t-lg); text-transform:capitalize}
  /* El salto directo de mes (28-sep): dos `<select>` y un "Ir" con la MISMA
     silueta que las flechas de arriba -- piso de 36px, borde --line-input,
     radio 8px, fondo --bg -- para que el renglón se lea como una sola barra y
     no como una barra más un formulario pegado. Se va a la derecha con el
     `margin-left:auto` y envuelve en angosto en vez de empujar el ancho. */
  .cal-salto{display:flex; align-items:center; gap:var(--e1); margin-left:auto; flex-wrap:wrap}
  .cal-salto select{width:auto; margin:0; padding:7px var(--e2); min-height:36px; font-size:var(--t-md);
    font-weight:600; color:var(--ink); background:var(--bg); border:1px solid var(--line-input); border-radius:var(--r-sm)}
  .alta-acciones{display:flex; flex-wrap:wrap; align-items:center; gap:var(--e2)}
  .vacio .alta-acciones{justify-content:center; margin-top:var(--e3)}
  .vacio .alta-acciones .vacio-a{margin-top:0}
  /* La marca de frescura de la ingesta y de la escritura (docs/40 punto 10).
     Es ESTADO, no título: letra chica y apagada, y el único pedazo en tinta es
     el estado que importa ("nunca"). Lo que cambió el 11-sep es el aire de
     abajo: con la card del precio fuera de en medio, esta línea quedó pegada a
     la grilla y con 4px se leía como su encabezado. */
  .cal-frescura{font-size:var(--t-sm); color:var(--mute); margin:-4px 0 var(--e2)}
  .cal-frescura .nunca{color:var(--ink)}
  /* "Publicar ahora", pegado a la marca de frescura: la pregunta y su respuesta
     en el mismo renglón. El `.hint` va al lado y no debajo -- es una aclaración
     del botón, no un párrafo -- y por eso se le saca el `margin-top` que trae. */
  .cal-publicar{display:flex; align-items:center; gap:var(--e2); flex-wrap:wrap;
    margin:0 0 var(--e3)}
  .cal-publicar .hint{margin-top:0}

  .cal-scroll{overflow-x:auto; border:1px solid var(--line); border-radius:var(--r-md)}
  /* Anrok: hairlines, no rejilla. La celda lleva UNA línea (la de su derecha) y
     la fila lleva la suya abajo; el marco lo pone `.cal-scroll`. Un borde por
     los cuatro lados de cada celda pinta la misma línea dos veces y la grilla
     pesa el doble de lo que debería. */
  table.cal{border-collapse:collapse; margin:0; width:auto; border:0}
  table.cal th, table.cal td{border:0; border-right:1px solid var(--line-strong); padding:0;
    text-align:center; font-size:var(--t-xs)}
  table.cal th:last-child, table.cal td:last-child{border-right:0}
  table.cal tbody tr{border-top:1px solid var(--line-strong)}
  table.cal tbody tr:hover{background:transparent}
  table.cal thead th{color:var(--mute); font-weight:500; padding:5px 3px var(--e1); background:var(--soft);
    letter-spacing:0; text-transform:none}
  /* Dos renglones por columna: el día de la semana arriba, el número abajo.
     Una tira de 31 números no deja ver dónde cae un fin de semana. */
  table.cal thead th .dow{display:block; font-size:var(--t-celda-min); letter-spacing:.04em;
    text-transform:uppercase; opacity:.75}
  table.cal thead th .dnum{display:block; font-size:var(--t-xs); color:var(--ink);
    font-variant-numeric:tabular-nums}
  /* Fin de semana y hoy: el fondo NO es el único portador. El fin de semana se
     lee además en su rótulo ("Sá"/"Do") y hoy lleva regla de terracota, número
     en negrita y `aria-current="date"` en el `<th>`. */
  /* `--soft` es demasiado sutil sobre blanco para marcar una columna entera;
     este tinte es el mismo gris de la tinta al 4%, que se ve sin pesar. */
  table.cal th.finde, table.cal td.finde{background:rgba(27,38,36,.04)}
  table.cal th.hoy{box-shadow:inset 0 -2px 0 var(--accent)}
  table.cal th.hoy .dnum{color:var(--terra-text); font-weight:600}
  table.cal td.hoy{box-shadow:inset 1px 0 0 var(--accent), inset -1px 0 0 var(--accent)}
  /* La columna de la propiedad queda fija al hacer scroll horizontal: con 31
     columnas, saber de qué puerta es la fila que se está mirando es la mitad de
     la lectura. Ancho acotado y texto que envuelve: el motivo de la solo
     lectura es una frase, y sin tope empujaba la grilla fuera de la pantalla. */
  table.cal th.prop-col, table.cal td.prop-col{text-align:left; padding:8px var(--e3); font-size:var(--t-md);
    position:sticky; left:0; z-index:2; background:var(--bg);
    min-width:200px; max-width:240px; white-space:normal; border-right:1px solid var(--line)}
  table.cal thead th.prop-col{background:var(--soft)}
  table.cal td.day-cell{width:26px; height:26px; position:relative}
  .ari-rate{display:block; font-size:var(--t-celda); line-height:1.2; color:var(--ink);
    letter-spacing:-.02em; font-variant-numeric:tabular-nums}
  /* El "—" de la noche que todavía no tiene tarifa: mismo renglón y mismo
     tamaño que una tarifa, en gris, porque es una ausencia y no un monto.
     ⚠ Esta hoja va INLINE en toda pantalla, así que sus comentarios son texto
     de la página: una frase de acá que algún test busque en el HTML lo hace
     pasar (o fallar) por el motivo equivocado. */
  .ari-rate.nada{color:var(--mute)}
  .ari-min{display:block; font-size:var(--t-celda-min); line-height:1.2; color:var(--mute)}

  /* La leyenda: la misma forma que la celda, con su nombre al lado. */
  .cal-leyenda{list-style:none; display:flex; flex-wrap:wrap; gap:var(--e1) 18px; margin:var(--e2) 0 0;
    font-size:var(--t-sm); color:var(--mute)}
  .cal-leyenda li{display:flex; align-items:center; gap:var(--e1)}
  /* El `hidden` del renglón que el poller todavía no encendió. Va explícito
     porque `.cal-leyenda li{display:flex}` le gana por especificidad al
     `[hidden]{display:none}` del navegador: sin esta línea la leyenda mostraba
     los cinco estados otra vez, que es exactamente el bug que se vino a
     arreglar. Se vio en el render, no en un test. */
  .cal-leyenda li[hidden]{display:none}
  /* El pie de la grilla: la leyenda a la izquierda y el camino a la pantalla del
     precio por canal a la derecha, en el mismo renglón (⚖ fundador 11-sep). El
     `margin-left:auto` y no `justify-content:space-between` porque la leyenda
     puede no existir --el mes sin ninguna marca no pinta `<ul>`-- y con
     space-between el enlace solo se iría al borde izquierdo. */
  .cal-pie{display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--e1) var(--e4);
    margin-top:var(--e2)}
  /* El margen de la leyenda pasa a ser del pie: adentro de un flex los dos
     márgenes se sumarían. */
  .cal-pie .cal-leyenda{margin:0}
  .cal-pie-enlace{margin-left:auto; font-size:var(--t-sm); font-weight:500;
    color:var(--terra-text); text-decoration:none}
  .cal-pie-enlace:hover{text-decoration:underline}
  /* Con dos caminos en el pie, el `margin-left:auto` es del PRIMERO: repetido
     en el segundo, el espacio libre se repartiría entre los dos y quedarían
     separados a mitad del renglón. El `gap` del pie ya los despega. */
  .cal-pie-enlace ~ .cal-pie-enlace{margin-left:0}
  .cal-leyenda .ari-sync{position:static}
  .cal-leyenda .muestra{width:13px; height:13px; border-radius:var(--r-xs); border:1px solid var(--line)}
  /* La marca fiscal en la leyenda se pinta como el pedacito de barra que es --
     mismo fondo, mismo contorno, mismo glifo, mismas puntas redondeadas --, así
     la equivalencia con la grilla es literal y no hay que recordar a qué color
     corresponde qué. */
  .cal-leyenda .fmuestra{display:inline-flex; align-items:center; justify-content:center;
    width:19px; height:15px; border-radius:var(--r-sm); background:var(--occ-bg);
    box-shadow:inset 0 0 0 1px var(--occ-line)}
  .cal-leyenda .fmuestra .fglifo{font-style:normal; font-size:var(--t-celda-min); color:var(--ink)}
  .cal-leyenda .f-facturada{--occ-bg:var(--ok-bg); --occ-line:var(--ok)}
  .cal-leyenda .f-emitiendo{--occ-bg:rgba(242,181,68,.18); --occ-line:var(--accent)}
  .cal-leyenda .f-confirmada{--occ-bg:rgba(27,38,36,.035); --occ-line:rgba(27,38,36,.3)}
  .cal-leyenda .f-detenida{--occ-bg:var(--warn-bg); --occ-line:var(--warn)}
  .cal-leyenda .m-stop{background:repeating-linear-gradient(45deg,transparent,transparent 3px,rgba(11,52,61,.22) 3px,rgba(11,52,61,.22) 6px)}
  /* La leyenda chica del estado de RESERVA (⚖ fundador 27-sep-2026): mismo
     `<ul>`/`<li>` que `.cal-leyenda`, en una lista propia --es un eje distinto
     del fiscal-- y con un margen chico arriba para separarla de la de arriba
     sin duplicar toda la regla. */
  .cal-leyenda-reserva{margin-top:6px}
  .rmuestra{width:13px; height:13px; border-radius:var(--r-xs); display:inline-block;
    background:rgba(242,181,68,.28); box-shadow:inset 0 0 0 1px var(--accent)}
  .rmuestra.r-solicitud{background:rgba(27,38,36,.03); box-shadow:inset 0 0 0 1px var(--mute);
    background-image:repeating-linear-gradient(45deg,transparent,transparent 3px,rgba(27,38,36,.08) 3px,rgba(27,38,36,.08) 4px)}
  .rmuestra.r-cancelada{background:var(--err-bg); box-shadow:inset 0 0 0 1px var(--err)}
  /* El aviso único de las puertas sin conectar. Es un camino, no una alarma:
     tono neutro y el enlace como única acción. */
  .cal-aviso{margin:var(--e3) 0 4px; padding:12px 16px; font-size:var(--t-md)}
  .cal-aviso p{margin:0}
  .cal-aviso p + p{margin-top:5px}
  .cal-aviso a{color:var(--terra-text); font-weight:500}

  /* ---------- calendario editable (docs/34 Fase 4) ---------- */
  /* El interior de la celda es un div para que htmx pueda repintarlo por OOB:
     un <td> suelto en un fragmento lo descarta el parser antes de que htmx lo
     vea. Sin edición habilitada el div no lleva ningún atributo hx-. */
  .cell-in{position:relative; width:100%; height:100%; padding:4px var(--e1) 4px 5px;
    display:flex; flex-direction:column; justify-content:center; min-height:38px}
  .cell-in[hx-get]{cursor:pointer}
  /* El hover deja de ser terracota (cáscara moderna 21-sep-2026, maqueta A
     §1): un solo acento por pantalla, y acá el acento lo tiene el botón
     primario de la cabecera, no cada celda que se puede tocar. */
  .cell-in[hx-get]:hover{background:rgba(27,38,36,.03); box-shadow:inset 0 0 0 1px var(--line-strong)}
  .cell-in[hx-get]:focus-visible{outline:2px solid var(--accent); outline-offset:-2px}
  .cell-in.stop{background:repeating-linear-gradient(45deg,transparent,transparent 3px,rgba(11,52,61,.14) 3px,rgba(11,52,61,.14) 6px)}
  /* La celda en conflicto (Fase 5): lo nuestro salió y Channex muestra otra
     cosa. Se marca, no se corrige — el host decide (docs/34 §3.4). */
  .cell-in.conflicto{box-shadow:inset 0 0 0 2px var(--info)}
  /* Cinco estados de sincronización, no dos: la escritura es asíncrona y una UI
     que finge inmediatez miente durante el ciclo del batch.
     El glifo va de CONTENIDO, no de `background`: el color solo no distingue
     "error" de "sincronizado" para un daltonismo rojo-verde, y son justo los
     dos que no se pueden confundir. */
  .ari-sync{position:absolute; top:2px; right:3px; font-size:var(--t-celda-min); line-height:1;
    font-family:var(--sans); letter-spacing:0}
  .ari-sync.s-pendiente{color:var(--mute)}
  .ari-sync.s-sincronizado{color:var(--ok)}
  .ari-sync.s-aviso{color:var(--warn)}
  .ari-sync.s-error{color:var(--err)}
  .ari-sync.s-conflicto{color:var(--info)}
  .ari-conflictos{margin-top:var(--e3)}
  .ari-conflictos ul{list-style:none; font-size:var(--t-md); line-height:1.8}
  #ari-editor:empty{display:none}
  #ari-editor{background:var(--soft); border:1px solid var(--line); border-radius:var(--r-md); padding:var(--e4); margin-bottom:var(--e3)}
  .ari-h{font-weight:600; font-size:var(--t-md); letter-spacing:-.01em; margin-bottom:12px}
  .ari-campos{display:grid; grid-template-columns:repeat(auto-fit, minmax(150px,1fr)); gap:12px}
  .ari-campos input, .ari-campos select{padding:7px 9px; font-size:var(--t-md)}
  .ari-form button{margin-top:var(--e3)}
  .ari-bulk{margin-top:16px}
  .ari-msg{font-size:var(--t-md); padding:8px 12px; border-radius:var(--r-sm); margin-bottom:var(--e2)}
  .ari-msg.ok{background:var(--ok-bg); border-left:3px solid var(--ok); color:var(--dark)}
  .ari-msg.bad{background:var(--err-bg); border-left:3px solid var(--err); color:var(--dark)}
  /* El recap de lo declarado y el bloque de lo publicado (docs/40 puntos 4 y 6). */
  .ari-recap{font-size:var(--t-md); color:var(--mute); margin-bottom:var(--e2)}
  .ari-recap strong{color:var(--ink); font-weight:600}
  .ari-publicado{margin-top:16px; padding-top:var(--e3); border-top:1px solid var(--line); font-size:var(--t-md)}
  .ari-h3{font-size:var(--t-xs); font-weight:600; color:var(--mute); text-transform:uppercase;
    letter-spacing:.05em; margin-bottom:var(--e1)}
  .ari-publicado .nada{color:var(--mute)}
  /* La proyección PLEGADA (⚖ fundador 11-sep): sin tarifa escrita es el
     `details.ayuda` del sistema y nada más. El `margin-top` que trae `.ayuda`
     sobra acá -- el aire ya lo pone el `padding-top` del bloque, que es el que
     lo despega de su hairline. */
  .ari-publicado > .ayuda{margin-top:0}
  .ari-publicado .canal{display:inline-block; border:1px solid var(--line); border-radius:var(--r-pill);
    padding:1px 9px; margin-right:5px; font-size:var(--t-xs); color:var(--ink)}

  /* ---------- calendario mensual de UNA puerta ---------- */
  /* Sin una sola línea de JavaScript: la barra sale del servidor con `left` y
     `width` en porcentaje de su semana, y la punta diagonal es un clip-path. */
  /* --celda-borde: entre --line-strong (1.46:1, casi invisible sobre arena) y
     --line-input (3.27:1, pesado en 42 celdas): se distingue sin competir con
     hoy, la barra ni la selección. */
  .mes{--s:11px; --celda-borde:color-mix(in srgb, var(--line-strong) 50%, var(--line-input));
    border:1px solid var(--celda-borde); border-radius:var(--r-md); overflow:hidden; margin-top:8px}
  .mes-cab{display:grid; grid-template-columns:repeat(7,1fr); background:var(--soft);
    border-bottom:1px solid var(--line)}
  .mes-cab span{padding:7px 0; text-align:center; font-size:var(--t-xs); font-weight:600;
    text-transform:uppercase; letter-spacing:.05em; color:var(--mute)}
  .mes-sem{position:relative}
  .mes-sem + .mes-sem .mes-dia{border-top:1px solid var(--celda-borde)}
  .mes-dias{display:grid; grid-template-columns:repeat(7,1fr)}
  /* La diagonal de la celda NO es adorno: es la convención del rubro que separa
     la salida de la mañana de la entrada de la tarde, y es lo que deja que dos
     reservas compartan un día sin parecer choque.
     `to bottom right` y no `to top right`: la línea que se ve es la
     PERPENDICULAR al gradiente, y la del "to bottom right" pasa por las esquinas
     inferior-izquierda y superior-derecha -- o sea dibuja "/", que es la
     inclinación con la que calzan las puntas de las barras. Con "to top right"
     saldría "\" y las puntas quedarían al revés. */
  .mes-dia{height:64px; padding:5px 8px; font-size:var(--t-sm); color:var(--ink);
    border-left:1px solid var(--celda-borde);
    display:flex; justify-content:space-between; align-items:baseline;
    background-image:linear-gradient(to bottom right,
      transparent calc(50% - .5px), rgba(27,38,36,.05) calc(50% - .5px),
      rgba(27,38,36,.05) calc(50% + .5px), transparent calc(50% + .5px))}
  .mes-dia:first-child{border-left:0}
  .mes-dia.fuera{color:var(--mute); opacity:.45}
  /* La tarifa de la noche comparte el renglón del número, a la derecha, y no
     va debajo: la barra de la reserva ocupa la franja del medio de la celda
     (top:28px, 26 de alto), asi que abajo se pisarian. Se pinta apagada porque
     acá el precio es contexto -- lo que esta pantalla dice es quién entra y
     quién sale --, y no se puede tocar: se cambia en el calendario del
     contribuyente, que es a donde lleva el puente de arriba. */
  .mes-tarifa{font-size:var(--t-celda); color:var(--mute); white-space:nowrap;
    letter-spacing:-.01em}

  .mes-barra{position:absolute; top:28px; height:26px; box-sizing:content-box;
    background:rgba(242,181,68,.28); color:var(--brown); font-size:var(--t-sm); line-height:26px}
  .mes-barra span{display:block; padding:0 9px; overflow:hidden; text-overflow:ellipsis;
    white-space:nowrap}
  /* El extremo que ES el check-in/check-out real termina en punta; el que solo
     se cortó contra el fin de semana o el borde del mes queda recto, que es lo
     que dice la verdad: la reserva sigue del otro lado.
     Cada extremo diagonal se extiende hacia AFUERA media inclinación (menos
     medio pixel, que deja ver la línea de la celda entre las dos barras). Sin
     esa extensión, dos reservas que comparten un día dejan una muesca en V en
     vez de engranar sobre una sola línea. */
  .mes-barra.punta-ini{margin-left:calc(var(--s) / -2 + .5px); padding-left:calc(var(--s) / 2 - .5px);
    clip-path:polygon(var(--s) 0, 100% 0, 100% 100%, 0 100%)}
  .mes-barra.punta-fin{padding-right:calc(var(--s) / 2 - .5px);
    clip-path:polygon(0 0, 100% 0, calc(100% - var(--s)) 100%, 0 100%)}
  .mes-barra.punta-ini.punta-fin{
    clip-path:polygon(var(--s) 0, 100% 0, calc(100% - var(--s)) 100%, 0 100%)}

  /* ---------- el mismo mes, pero con las tarifas encima (⚖ fundador 26-ago) ---
     `.cal-mes` es la variante del mes de una puerta que se puede EDITAR: la
     celda deja de ser un cuadro de 64px con un número y pasa a alojar la tarifa,
     el mínimo de noches y el punto de sincronización. La geometría (semanas,
     diagonal, puntas de las barras) es la misma: es la misma cuadrícula, no una
     tercera. */
  /* En un teléfono siete columnas de 53px no alojan una tarifa, y una
     cuadrícula de mes no se puede plegar a menos columnas: la semana tiene
     siete días. Así que scrollea, igual que la grilla vieja (`.cal-scroll`).
     El ancho mínimo sale de mirar el render: 620/7 son 88px por columna, que es
     lo que pide "101 000" a 10,5px sin recortarse. */
  .cal-mes-scroll{overflow-x:auto}
  .cal-mes{min-width:620px}
  /* El envoltorio de la pantalla del mes. Existe para poder REORDENAR en
     angosto: `gap` reemplaza los márgenes que separaban estas piezas, así que
     el espaciado no cambia.

     El problema que resuelve no es la grilla --el scroll horizontal está bien
     razonado: siete columnas no entran en un teléfono sin aplastar la tarifa, y
     una semana no se puede plegar a menos de siete días-- sino que la única
     forma de elegir un tramo son dos clicks, y en un teléfono el segundo día
     puede estar del otro lado del scroll. En la superficie donde se declaran
     los precios. En ancho no cambia un pixel: sin `order` los hijos se pintan
     en el orden del DOM. */
  .cal-cuerpo{display:flex; flex-direction:column; gap:var(--e5)}
  @media (max-width:640px){
    /* Los tres que suben, en este orden: el panel de rango, su desglose y el
       teaser del plan (que ocupa el lugar del panel cuando no se puede
       editar -- si subiera el panel y el teaser se quedara abajo, la pantalla
       diría dos cosas distintas según el plan).
       El editor de UNA noche (`#ari-editor`) NO sube: nace vacío y se llena al
       hacer click en un día, o sea que su lugar natural es pegado a la grilla
       que lo disparó. */
    .cal-cuerpo > .ari-bulk{order:-3}
    /* `.ari-publicado` y no `.ari-desglose`: el segundo es el `id` del parcial
       (y en esta pantalla ni siquiera ese -- acá la caja es
       `ari-desglose-rango`). La clase de su root es `.ari-publicado`. Con el
       otro nombre la regla no matcheaba nada y el desglose caía debajo de la
       grilla, separado del panel que lo alimenta: justo lo que este bloque
       existe para evitar. */
    .cal-cuerpo > .ari-publicado{order:-2}
    /* Los DOS candados suben juntos: desde el 11-sep el del precio por canal es
       también `.card.bloqueada`, y son la misma mitad del producto que este
       plan no compró. Separados por un mes entero se leían como dos avisos sin
       relación. El empate de `order` lo desempata el DOM, así que el orden
       entre ellos es el de la plantilla. */
    .cal-cuerpo > .card.bloqueada{order:-1}
    /* La grilla, ya como confirmación. El borde superior es lo que dice que
       empieza otra cosa: sin él, en angosto, la cuadrícula se lee como si
       siguiera siendo el control.
       Desde el 11-sep el blanco es `.cal-grilla` --la grilla MÁS su pie-- y no
       `#cal-wrap`: si fuera solo la grilla, la leyenda se despegaría de ella
       justo en la pantalla donde menos lugar hay. */
    .cal-cuerpo > .cal-grilla{border-top:1px solid var(--line); padding-top:var(--e4)}
  }
  .cal-mes .mes-dia{height:98px; position:relative}
  /* Fin de semana y hoy: el fondo NO es el único portador. El fin de semana se
     lee además en el rótulo de su columna en la cabecera, y el día de hoy lleva
     regla de terracota y `aria-current="date"` en la celda. */
  .cal-mes .mes-dia.finde{background-color:rgba(27,38,36,.04)}
  .cal-mes .mes-dia.hoy{box-shadow:inset 0 -2px 0 var(--accent)}
  .cal-mes .mes-dia.hoy .dnum{color:var(--terra-text); font-weight:700}
  .cal-mes .mes-dia .dnum{font-variant-numeric:tabular-nums; font-weight:600}
  /* La barra vive en la banda de arriba y la tarifa abajo: centrados los dos se
     pisarían. `pointer-events:none` porque la barra es una capa absoluta que
     cruza varias celdas -- volverla clickeable entera le robaría el clic a la
     selección de todas las noches que tapa. Solo el rótulo pintado lo recupera
     (`.fdoc`), que es la misma regla que ya regía en la grilla vieja. */
  .cal-mes .mes-barra{top:26px; height:22px; line-height:22px; font-size:var(--t-xs);
    pointer-events:none;
    --occ-bg:rgba(242,181,68,.28); --occ-line:var(--accent);
    background:var(--occ-bg); color:var(--brown)}
  /* Los cuatro estados fiscales, en la barra. El color acompaña; quien lo lee de
     verdad es el glifo, que va siempre -- sin él, verde y ámbar son el mismo
     gris para quien no los distingue, y ése es justo el par de estados que no
     se puede confundir. Los nombres de clase salen de `app/ari_ui.py`. */
  .cal-mes .mes-barra.f-facturada{--occ-bg:var(--ok-bg); color:var(--ok)}
  .cal-mes .mes-barra.f-emitiendo{--occ-bg:rgba(242,181,68,.18); color:var(--brown)}
  .cal-mes .mes-barra.f-confirmada{--occ-bg:rgba(27,38,36,.045); color:var(--mute)}
  .cal-mes .mes-barra.f-detenida{--occ-bg:var(--warn-bg); color:var(--warn)}
  .cal-mes .mes-barra .fglifo{font-style:normal; margin-right:4px; font-size:var(--t-celda)}
  .cal-mes .mes-barra .fdoc{pointer-events:auto; color:inherit; text-decoration:none}
  .cal-mes .mes-barra .fdoc:hover{text-decoration:underline}
  .cal-mes .mes-barra .fdoc:focus-visible{outline:2px solid var(--accent); outline-offset:1px}
  /* El estado de RESERVA (⚖ fundador 27-sep-2026): un eje distinto del fiscal
     de arriba, así que van DESPUÉS en la hoja -- ganan cuando los dos aplican
     (una cancelada con documento ya emitido sigue queriendo verse cancelada).
     "confirmada" no tiene regla propia: el fondo lo sigue pintando el fiscal,
     que es el caso común y el que ya tenía color. Los colores son tenues
     (tokens existentes, sin hex sueltos) y el texto sigue en contraste AA --
     `--err`/`--err-bg` y `--mute` ya están medidos en el bloque de arriba. */
  .cal-mes .mes-barra.r-solicitud{background:rgba(27,38,36,.03); color:var(--mute);
    box-shadow:inset 0 0 0 1px var(--mute);
    background-image:repeating-linear-gradient(45deg,transparent,transparent 4px,rgba(27,38,36,.06) 4px,rgba(27,38,36,.06) 6px)}
  .cal-mes .mes-barra.r-cancelada{background:var(--err-bg); color:var(--err); text-decoration:line-through}
  /* El micro-ícono del canal de origen: la inicial en un círculo de color, sin
     logo ajeno. `pointer-events:none` lo hereda de `.mes-barra`; el texto
     blanco es chico y decorativo -- el nombre entero que de verdad informa va
     en `title`/`aria-label`, que es lo que lee un lector de pantalla. */
  .cal-mes .mes-barra .ori{font-style:normal; display:inline-flex; align-items:center;
    justify-content:center; width:13px; height:13px; border-radius:50%;
    font-size:9px; font-weight:700; line-height:1; color:#fff; margin-right:4px;
    vertical-align:-1px}
  .cal-mes .mes-barra .ori-airbnb{background:var(--terra); color:var(--brown)}
  .cal-mes .mes-barra .ori-booking{background:var(--brown)}
  .cal-mes .mes-barra .ori-vrbo{background:var(--info)}
  .cal-mes .mes-barra .ori-sitio{background:var(--ok)}
  .cal-mes .mes-barra .ori-directo{background:var(--mute)}
  /* El interior editable ocupa el pie de la celda. `top` y no `height`: la
     celda mide 98px fijos y el resto es la banda del número y de la barra. */
  .cal-mes .cell-in{position:absolute; left:0; right:0; bottom:0; top:54px;
    width:auto; height:auto; min-height:0; padding:4px 8px; justify-content:flex-end}
  /* El día elegido. Es `aria-pressed` y no una clase: la celda ya es
     `role="button"`, así que el estado de "elegido" tiene que ser el que un
     lector de pantalla anuncia, no uno paralelo que solo se vea. */
  .cal-mes .cell-in[aria-pressed="true"]{background:rgba(16,68,79,.14);
    box-shadow:inset 0 0 0 2px var(--accent)}
  /* El renglón vivo de qué hay elegido. Reserva su alto para que la grilla no
     salte cuando el texto pasa de una línea a "4 noches elegidas", y respira
     abajo: lo que sigue es otro campo con su rótulo, y sin aire los dos
     renglones de letra chica se leían como uno solo. */
  .cal-sel{min-height:16px; margin-bottom:var(--e3)}
  /* El cómo del formulario, plegado. Va entre el aviso y los campos, así que
     necesita el aire de un bloque a cada lado -- pegado al rótulo "DESDE" se
     leía como si fuera parte del primer campo. */
  .ari-bulk > .ayuda{margin:var(--e2) 0 var(--e3)}
  /* El panel desactivado (puerta sin anuncio, o en modo lectura). Se ve que
     está ahí y se ve que no se puede tocar: esconderlo era lo que hacía que la
     misma pantalla pareciera dos productos distintos. */
  .ari-fieldset[disabled]{opacity:.55}
  .ari-fieldset[disabled] input, .ari-fieldset[disabled] select,
  .ari-fieldset[disabled] button{cursor:not-allowed}
  /* La confirmación antes de aplicar, en un `<dialog>` nativo abierto por
     atributo (no hay `showModal()` porque no hay JS que lo llame). Se pinta en
     el flujo del panel, con su semántica de diálogo. */
  /* `position:static` no es cosmético: el estilo del navegador para
     `dialog[open]` es `position:absolute`, y así el diálogo se despegaba del
     panel y caía ENCIMA de la grilla, tapando las dos primeras semanas. Se vio
     mirando el render, no en un test. Abierto por atributo -- que es como llega
     acá, sin `showModal()` -- tiene que caer en el flujo del panel. */
  .cal-dialogo{display:block; position:static; width:100%; max-width:none;
    margin:0 0 12px; border:1px solid var(--terra); border-radius:var(--r-md);
    padding:var(--e4); background:var(--bg); color:var(--ink)}
  .cal-dialogo p{font-size:var(--t-md); margin-bottom:var(--e2)}
  .cal-dialogo-no{margin-top:8px}

  /* La lista del paso "¿de cuál?": las PUERTAS de un contribuyente
     (`calendario_puertas.html`) y, desde el 26-ago, los CONTRIBUYENTES de una
     cuenta (`elegir_contribuyente.html`). Las dos comparten la clase porque son
     la misma pregunta a distinta altura del árbol, y la respuesta se ve igual.
     Renglones grandes -- es un destino, no una tabla de datos. */
  .puertas-lista{list-style:none; margin:var(--e3) 0 0; border:1px solid var(--line);
    border-radius:var(--r-md); overflow:hidden}
  .puertas-lista li{padding:12px 16px; border-top:1px solid var(--line)}
  .puertas-lista li:first-child{border-top:0}
  .puertas-lista li > a{display:block; font-size:var(--t-lg); font-weight:600;
    letter-spacing:-.01em; color:var(--ink); text-decoration:none}
  .puertas-lista li > a:hover{color:var(--terra-text)}
  .puertas-lista .ro, .puertas-lista .canales{display:block; font-size:var(--t-xs);
    font-weight:400; color:var(--mute); margin-top:3px}
  .puertas-lista .ro{color:var(--brown)}

  /* ---------- llegadas y salidas: la grilla operativa ----------
     `operacion_calendario.html` es la ÚNICA pantalla que sigue pintando la
     ocupación sobre la celda (`td.occ`, más `checkin`/`checkout`): es solo
     lectura y no lleva las barras de media celda del calendario del emisor.
     Estas tres reglas se fueron con aquel rediseño (#144, 19-ago-2026), que
     movió el color a `.estadia` -- y desde entonces, en Llegadas y salidas, un
     día ocupado y uno libre se veían IGUAL. Vuelven tal cual estaban.

     Van acá y no en el bloque del calendario porque son de esta pantalla, y
     después de `td.hoy` como estaban antes: la fila ocupada de hoy se lee por
     su franja, y la columna de hoy ya se marca en la cabecera. */
  table.cal td.occ{--occ-bg:rgba(242,181,68,.22); --occ-line:var(--accent);
    background:var(--occ-bg); box-shadow:inset 0 -3px 0 var(--occ-line)}
  table.cal td.occ.checkin{box-shadow:inset 0 -3px 0 var(--occ-line), inset 2px 0 0 var(--occ-line)}
  table.cal td.occ.checkout{box-shadow:inset 0 -3px 0 var(--occ-line), inset -2px 0 0 var(--occ-line)}

  /* ---------- tareas ---------- */
  /* La barra del rango: dos atajos, dos flechas y el rango de la semana, que es
     el renglón más largo de las cuatro que usan `.cal-nav` -- "11 set 2026 — 17
     set 2026" mide lo que miden dos fechas enteras. A 375px los cinco no entran,
     y sin permiso para envolver el `<strong>` se partía en cuatro renglones
     adentro de su caja y empujaba la pantalla a 400px de ancho: scroll
     horizontal en la pantalla que se mira parado en la puerta de una casa
     (UI-11, el mismo defecto que el de las tablas anchas).

     Con permiso para envolver, el rango baja entero al renglón de abajo y se
     lee de una: no hace falta además prohibirle el corte -- a 375px ya le sobra
     el ancho de la línea. Es el mismo permiso que tiene `.oc-cab-mes`, y va acá
     --en una clase propia de esta pantalla-- para no mover las otras tres
     barras, que caben. */
  .tareas-nav{flex-wrap:wrap}

  .inline-form{display:flex; gap:var(--e1); align-items:center}
  .inline-form input{width:auto; flex:1; padding:8px var(--e2); font-size:var(--t-md)}
  .inline-form button{margin-top:0; padding:8px 16px; font-size:var(--t-md); min-height:36px}
  /* El campo de la factura de la plataforma, adentro de la tabla de comisiones
     del resumen fiscal (`_factura_plataforma.html`). Reusa `.inline-form`
     entero; lo propio son dos anchos: la celda no se estruja (la tabla ya
     scrollea adentro de `.tabla-scroll`) y la fecha no crece, porque un campo
     de fecha tiene el largo que tiene. */
  .fp{min-width:300px}
  .fp .inline-form input[type="date"]{flex:0 0 auto; min-width:140px}
  /* La evidencia en fotos de la limpieza (⚖ fundador 08-sep). La celda tiene
     ancho mínimo porque adentro van hasta seis miniaturas y un botón: sin él la
     tabla la estruja a nada. El botón de subir es GRANDE a propósito -- se
     aprieta con el pulgar, parado en la puerta de la casa, no con el mouse. */
  .evid{min-width:190px}
  .evid-tiras{display:flex; flex-wrap:wrap; gap:5px}
  .evid-foto{position:relative; display:inline-block; line-height:0}
  .evid-foto img{width:52px; height:52px; object-fit:cover; border-radius:var(--r-sm);
    border:1px solid var(--line)}
  /* La cruz de borrar, encima de su propia miniatura. Chica pero con área
     tocable de 24px: es una acción destructiva y no puede quedar debajo del
     pulgar por accidente, ni tampoco ser imposible de acertar. */
  .evid-borrar{position:absolute; top:-6px; right:-6px; margin:0}
  .evid-borrar button{margin:0; padding:0; width:24px; height:24px; min-height:24px;
    border-radius:var(--r-pill); font-size:var(--t-md); line-height:1; background:var(--ink);
    color:#fff; border:none}
  .evid-cuenta{margin:5px 0 0}
  .evid-subir{display:flex; flex-wrap:wrap; gap:var(--e1); align-items:center; margin-top:7px}
  .evid-subir button{margin-top:0; padding:8px 16px; font-size:var(--t-md); min-height:36px}
  /* El selector de archivo va adentro del `<label>` y escondido: el control
     nativo no se puede vestir, y lo que el teléfono necesita es un blanco
     grande que diga qué hace.

     ⚠ La regla se escribe `.evid-boton input` y NO con el selector de atributo
     que uno pondría: la hoja entera viaja en TODA página, incluida la del
     dueño, y `test_la_superficie_del_dueno_no_guarda_el_p12_ni_el_pin` busca
     ese atributo como texto para probar que ahí no se ofrece subir ningún
     archivo. Es un test que hay que dejar ruidoso, no uno que ablandar. */
  .evid-boton{display:inline-block; padding:9px 16px; border:1px solid var(--line);
    border-radius:var(--r-sm); font-size:var(--t-md); font-weight:600; cursor:pointer;
    background:var(--soft); color:var(--ink)}
  .evid-boton input{display:none}

  /* Las tareas de limpieza, una tarjeta por tarea (`operacion_tareas.html`). Los
     bloques van en fila con wrap en escritorio y apilados en móvil: nada
     scrollea de costado, porque quien limpia entra desde el teléfono. */
  .tareas-lista{display:flex; flex-direction:column; gap:var(--e3)}
  .tarea-card{margin-bottom:0}
  .tarea-cab{display:flex; align-items:flex-start; justify-content:space-between; gap:var(--e3);
    margin-bottom:var(--e3)}
  .tarea-titulo{display:flex; flex-wrap:wrap; align-items:baseline; gap:2px var(--e3); min-width:0}
  .tarea-puerta{font-size:var(--t-lg)}
  .tarea-ventana{color:var(--mute); font-size:var(--t-md)}
  .tarea-bloques{display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
    gap:var(--e3) var(--e4)}
  .tarea-bloque{min-width:0}
  .tarea-bloque h3{font-size:var(--t-xs); color:var(--mute); text-transform:uppercase;
    letter-spacing:.05em; font-weight:600; margin:0 0 var(--e1)}
  .tarea-bloque p{margin:0}
  .tarea-bloque .inline-form{flex-wrap:wrap}
  .tarea-bloque .inline-form input[type="text"], .tarea-bloque .inline-form input:not([type]),
  .tarea-bloque .inline-form input.plata{min-width:0}
  /* La casilla en minúsculas normales y en su propio renglón: el `label` global
     es de rótulo en mayúsculas, que acá apretaba el texto contra el checkbox. */
  .tarea-check{display:flex; align-items:center; gap:var(--e1); flex:1 0 100%; margin:var(--e1) 0 0;
    font-size:var(--t-md); font-weight:400; color:var(--ink); text-transform:none; letter-spacing:0}
  .tarea-check input{flex:0 0 auto; width:auto; margin:0}
  .tarea-pie{display:flex; justify-content:flex-end; margin-top:var(--e3); padding-top:var(--e3);
    border-top:1px solid var(--line)}
  .tarea-pie .inline-form{flex:0 0 auto}
  @media (max-width:640px){
    .tarea-pie .inline-form{flex:1}
    .tarea-pie button{width:100%}
  }

  /* ---------- el editor del sitio web (docs/75 PR 4) ---------- */
  /* Las casillas del catálogo de amenidades, en columnas: son ~45 y en una
     sola columna ocupaban tres pantallas. Cada casilla es un `label.marcable`
     de los de siempre; el objetivo tocable lo da el alto de línea. */
  .sitio-amenidades{display:grid; grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));
    gap:4px var(--e3)}
  .sitio-amenidades .marcable{display:block; padding:7px 0; margin:0}
  /* La galería del sitio: la misma familia que la evidencia de limpieza
     (`.evid-*`), con la miniatura más grande porque acá la foto ES el
     contenido, y debajo los dos botones del orden (sin JavaScript). */
  .sitio-fotos{display:flex; flex-wrap:wrap; gap:var(--e2)}
  .sitio-foto{display:flex; flex-direction:column; align-items:center; gap:4px}
  .sitio-foto img{width:112px; height:84px; object-fit:cover; border-radius:var(--r-sm);
    border:1px solid var(--line)}
  .sitio-foto .evid-foto{line-height:0}
  .sitio-foto .sin-mini{display:flex; align-items:center; justify-content:center;
    width:112px; height:84px; border-radius:var(--r-sm); border:1px dashed var(--line);
    color:var(--mute); font-size:var(--t-sm)}
  .sitio-orden{display:flex; gap:2px}
  .sitio-orden button{margin:0; padding:0 10px; min-height:36px; min-width:36px}
  /* El renglón de la portada: la primera foto es la que ve el huésped y el
     buscador, y el editor lo dice en vez de dejar que se adivine. */
  .sitio-foto .portada{font-size:var(--t-xs); color:var(--terra-text); font-weight:600}
  /* La dirección del sitio: el subdominio con el dominio pegado a la derecha
     -- el host escribe una sola etiqueta, no una URL. */
  .sitio-dir{display:flex; align-items:center; gap:6px}
  .sitio-dir input{flex:1 1 auto; min-width:0}
  .sitio-dir .dom{color:var(--mute); white-space:nowrap}
  /* El estado en la lista: publicada, o la primera cosa que falta. */
  .sitio-estado{white-space:nowrap}
  /* La barra de "cambios sin guardar" del formulario grande (docs/83 §1,
     revisión de Opus): fija abajo SIEMPRE -- a diferencia de `.accion-fija`
     (arriba), que solo flota bajo los 900px, el editor es un formulario
     largo en cualquier ancho y en escritorio no hay barra móvil que le
     ceda el lugar. `sitio_editor.js` le saca el `hidden` cuando el
     formulario grande queda sucio; sigue arrancando oculta sin JavaScript
     (docs/83: el botón único de siempre, `#guardar-secciones`, ya está al
     pie del formulario y alcanza). `--accion-fija-pie` es el mismo hueco
     de la barra móvil que ya calcula `.accion-fija` arriba: 0 en
     escritorio, 56px bajo los 900px. */
  #barra-cambios{
    position:fixed; left:var(--e3); right:var(--e3); z-index:24;
    bottom:calc(var(--accion-fija-pie) + env(safe-area-inset-bottom) + var(--e2));
    display:flex; align-items:center; justify-content:space-between; gap:var(--e3);
    padding:var(--e2) var(--e3); background:var(--bg); border:1px solid var(--line);
    border-radius:var(--r-md); box-shadow:var(--sh-lift);
  }
  #barra-cambios[hidden]{display:none}
  #barra-cambios button{margin:0}
  /* El contenido no puede terminar tapado por la barra visible (mismo
     criterio que `form:has(> .accion-fija)` de arriba): un poco más de
     hueco al pie de `main` alcanza, porque el botón único de siempre ya
     estaba ahí. */
  body:has(#barra-cambios:not([hidden])) .shell .col main{
    padding-bottom:calc(64px + env(safe-area-inset-bottom) + var(--e4))}
  @media (max-width:900px){
    body:has(#barra-cambios:not([hidden])) .shell .col main{
      padding-bottom:calc(56px + 64px + env(safe-area-inset-bottom) + var(--e4))}
  }

  /* ---------- primeros pasos (docs/40 §8) ---------- */
  /* El paso que todavía no se puede dar NO se esconde: se apaga y se le escribe
     el motivo al lado. Un paso ausente no enseña de qué depende. */
  .paso{background:var(--soft); border:1px solid var(--line); border-radius:var(--r-md);
    padding:var(--e4); margin-bottom:var(--e3)}
  .paso-h{display:flex; align-items:center; gap:var(--e2); font-size:var(--t-lg); letter-spacing:-.01em}
  .paso .hint{margin-top:7px}
  .paso-cta{margin-top:var(--e2); font-size:var(--t-md)}
  .paso-cta a{text-decoration:none; font-weight:600}
  .paso-motivo{margin-top:7px; font-size:var(--t-md); color:var(--mute);
    border-left:2px solid var(--line); padding-left:var(--e2)}
  .paso.paso-bloqueado{opacity:.6}

  /* La card de una capacidad que este plan NO compró (⚖ fundador 25-ago): se
     muestra BLOQUEADA, nunca ausente. Toma prestado el idioma que ya usa el
     paso bloqueado de "Primeros pasos" -- apagada, con su badge neutro al lado
     y el candado adentro -- porque es la misma idea: lo que no se puede hacer
     todavía se ve, con la razón. Menos opaca que el paso (`.72` contra `.6`):
     acá adentro hay un NÚMERO que tiene que quedar legible. */
  .card.bloqueada{opacity:.72}
  .card.bloqueada .badge{margin-left:8px; vertical-align:middle}

  /* ---------- el recorrido del dueño (docs/40 fila 4) ---------- */
  /* Una pregunta por pantalla: la barra dice cuánto falta, el riel muestra los
     cuatro pasos SIEMPRE -- incluidos los que todavía no se pueden dar, que van
     apagados y con el motivo debajo. */
  .wiz-cab{margin-top:18px; font-size:var(--t-md); color:var(--mute)}
  .wiz-cab strong{color:var(--ink); font-weight:600}
  .wiz-barra{height:4px; border-radius:var(--r-pill); background:var(--line); margin:8px 0 18px; overflow:hidden}
  .wiz-barra span{display:block; height:100%; background:var(--terra); border-radius:var(--r-pill)}
  .wiz-riel{list-style:none; margin-bottom:18px}
  .wiz-riel li{padding:7px 0; border-bottom:1px solid var(--line)}
  .wiz-riel li:last-child{border-bottom:0}
  .wiz-riel a{display:flex; align-items:center; gap:var(--e2); text-decoration:none; color:var(--mute); font-size:var(--t-md)}
  .wiz-riel a:hover .wiz-t{color:var(--terra-text)}
  .wiz-riel li.actual .wiz-t{color:var(--ink); font-weight:600}
  .wiz-riel li.bloqueado{opacity:.6}
  .wiz-nav{display:flex; gap:18px; margin-top:16px; font-size:var(--t-md)}
  .wiz-nav a{text-decoration:none; font-weight:600}
  /* Los pasos que el dueño hace SOLO, dentro de su sesión de TRIBU-CR: una
     lista numerada de verdad, con la sangría que el `*{padding:0}` le saca y
     el renglón más aireado, porque cada punto es una parada. */
  ol.pasos{padding-left:var(--e4); font-size:var(--t-md); line-height:1.9}
  /* El recap no lleva claves de Hacienda, así que no quiere el corte a
     mitad de palabra que `dl.kv` usa para ellas. */
  dl.wiz-recap dd{word-break:normal}
  /* El recap de una CONFIRMACIÓN (retirar un contribuyente, dar de baja una
     puerta, avisar una liquidación, convertir una reserva): el rótulo se lleva
     el ancho y el valor se pega a la derecha, que es como se lee lo que se está
     por confirmar. `dl.` en el selector para ganarle a `dl.kv`, que reparte al
     revés. Eran cuatro `style=` idénticos, uno por pantalla de confirmación. */
  dl.wiz-recap.confirmacion{grid-template-columns:1fr auto}
  .wiz-h{font-weight:600; font-size:var(--t-md); letter-spacing:-.01em; margin:16px 0 var(--e1)}

  /* Imprimir = el PDF del dueño (owner_liquidacion.html): el papel lleva el
     documento, no el chrome. `.no-imprimir` marca lo que es navegación --
     `.barra-sup` lo lleva, así que ya sale con el resto; lo que NO se apaga
     solo es el hueco que le dejaba (`.shell{padding-top:60px}`, más arriba):
     sin esta línea el papel arrancaba con 60px de margen en blanco. */
  @media print{
    nav.top form, aside.lateral, .menu-btn, .no-imprimir{display:none !important}
    .shell{padding-top:0}
    /* `print-color-adjust:exact` es para lo que SÍ tiene que imprimirse con
       color (badges de estado, filos de aviso); sin las dos líneas de abajo
       también imprime el papel gris de --bg-app (cáscara moderna 21-sep) y la
       sombra de las tarjetas -- ninguna de las dos existe en papel, gastan
       tinta y no separan nada que un hairline no separe mejor (revisión de
       #417). */
    body{-webkit-print-color-adjust:exact; print-color-adjust:exact; background:#fff}
    .card, .cmp-lista, .pt-bloque, .hoy-cifras{box-shadow:none}
  }

  /* ---------- sidebar en pantalla angosta ---------- */
  /* Debajo de 900px una columna de 242px se come un tercio de la pantalla, así
     que el sidebar se pliega detrás del botón "Menú" -- que hasta el PR 2 era
     su propia franja clara y desde el PR 2 vive INTEGRADO a la barra oscura
     (⚖ fundador 21-sep, punto 7 de la maqueta B). Es el checkbox de arriba --
     ni JS ni htmx: el estado no sale del navegador y una barra de navegación
     que dependa de una petición no es una mejora.

     El checkbox es hermano de `.barra-sup` Y de `.shell` (los tres cuelgan
     directo del `<body>`, ver más abajo): el general sibling combinator (`~`)
     solo lee hermanos DIRECTOS, y el botón vive DENTRO de la barra mientras
     el `<aside>` que abre vive DENTRO del shell -- de ahí que las dos reglas
     de abajo bajen un nivel más con un selector descendiente después del `~`. */
  @media (max-width:900px){
    .shell{display:block}
    /* Icono nomás, del mismo alto que la campana y el avatar de al lado --ya
       no es una barra propia, es un control más de la oscura. */
    .menu-btn{
      display:flex; align-items:center; justify-content:center; cursor:pointer;
      width:38px; height:38px; border-radius:var(--r-pill); flex:none;
      background:rgba(255,255,255,.10); border:1px solid rgba(255,255,255,.22);
      color:var(--on-dark); margin:0;
    }
    .menu-btn::before{content:"☰"; font-size:var(--t-lg); line-height:1}
    aside.lateral{
      display:none; width:auto; height:auto; position:static; overflow:visible;
      border-right:0; border-bottom:1px solid var(--line);
    }
    .menu-check:checked ~ .shell aside.lateral{display:flex}
    .menu-check:focus-visible ~ .barra-sup .menu-btn{outline:2px solid var(--on-dark-accent); outline-offset:-2px}
    /* Debajo de 900px los CINCO destinos ya no hace falta repetirlos adentro
       del menú deslizable (⚖ fundador, docs/70 principio 7, sobre #460):
       viven en la barra fija del pie (`_barra_movil.html`, más abajo), al
       alcance del pulgar. Y sus tabs de segundo nivel (`.riel-tabs`) tampoco
       -- `_tabs_grupo.html` ya los pinta arriba del contenido en TODO ancho,
       no solo en escritorio, así que repetirlos acá volvería a ser la misma
       navegación tres veces. Lo que le queda a este menú es lo que no tiene
       otro lugar: el selector de propietario bajo 640px
       (`.emisor-pick-movil`), "Instalar en el teléfono" y la oferta de
       upgrade -- por eso se esconde el `.riel-secciones` navegable entero
       (`nav.sections`, con o sin `.cima`) y no el `<aside>`, que sigue
       siendo quien los contiene a todos. */
    .riel-secciones nav.sections{display:none}
  }

  /* ---------- barra de navegación inferior, en MÓVIL ---------- */
  /* Los destinos del riel, fijos al pie de la pantalla bajo 900px (⚖
     fundador, docs/70 principio 7 "zona del pulgar", aprobado 24-sep-2026,
     sobre #460 -- el riel de cinco destinos planos). `_barra_movil.html` la
     pinta con la MISMA lista (`cima` + `grupos`) que ya arma
     `_nav_emisor`/`nav_cuenta`, así que lo que el rol o el plan no dejan ver
     en el riel de escritorio tampoco aparece acá -- ningún filtro nuevo que
     pueda desalinearse del original.

     `position:fixed` de borde a borde, por encima de `main` (z-index 25,
     debajo de `.barra-sup` -- 30, fija arriba de todo -- y de las migas
     móviles -- 29 -- que ya fijó #416; ninguna de las dos convive en pantalla
     con esto, pero conserva el mismo orden) y por encima de `.accion-fija`
     (24, más abajo): el botón fijo de un formulario tiene que quedar ARRIBA
     de esta barra, nunca tapado por ella. `padding-bottom` con
     `env(safe-area-inset-bottom)` es el hueco del "home indicator" de iOS --
     sin él, un iPhone sin botón físico dibuja la fila de abajo detrás de esa
     franja del sistema. */
  .barra-movil{
    display:none; position:fixed; left:0; right:0; bottom:0; z-index:25;
    background:var(--bg); border-top:1px solid var(--line);
    padding-bottom:env(safe-area-inset-bottom);
  }
  /* Objetivo táctil ≥44px (docs/70 §7): 56px de alto de FILA entera, no solo
     del ícono -- el blanco de toque tiene que ser el botón completo.
     `flex:1` reparte el ancho en partes iguales entre los que traiga el rol
     (pueden ser 3, no siempre 5): un ancho fijo pensado para cinco dejaría
     huecos con menos. */
  .barra-movil a, .barra-movil summary{
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:2px; flex:1; min-height:56px; padding:6px 4px; text-decoration:none;
    color:var(--mute); font-size:11px; font-weight:500; cursor:pointer;
    list-style:none;
    /* `min-width:0`: un ítem flex arranca con `min-width:auto` (= el ancho de
       su rótulo), y a 360px de ancho cinco rótulos podrían sumar más que la
       pantalla y empujar el último ("Más") afuera del borde. Con esto la
       barra reparte SIEMPRE el ancho de la pantalla en partes iguales y el
       rótulo que no entre se recorta con elipsis (`.txt`, abajo) en vez de
       desbordar. */
    min-width:0;
  }
  .barra-movil .txt{max-width:100%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
  .barra-movil summary::-webkit-details-marker{display:none}
  .barra-movil .ico{width:20px; height:20px; opacity:.78}
  /* El activo: el MISMO color que la píldora llena del riel de escritorio
     (`--terra-text`, `nav.sections a.on`) -- es la misma decisión de color,
     no una paleta nueva para móvil. Sin relleno de fondo: la fila es angosta
     y un relleno de borde a borde en las cinco competiría con la barra
     entera: acá alcanza con el color para decir "estás acá". */
  .barra-movil a.on, .barra-movil a.on .ico{color:var(--terra-text); opacity:1}
  .barra-movil a.on{font-weight:600}
  /* "Más" -- el quinto lugar cuando el riel trae más de cinco destinos (desde
     el 26-sep-2026 pasa para el admin de una cuenta bundle: "Sitio web" y
     "Ajustes" viven acá, ver el comentario del parcial): el mismo mecanismo
     que el selector de contribuyente de la barra superior, un `<details>`
     sin una línea de JavaScript, que abre HACIA ARRIBA -- está al pie de la
     pantalla, así que un menú que abriera hacia abajo se saldría de la
     ventana. El panel (fondo, borde, sombra, filas) es el de `.pick-menu`
     compartido con `.emisor-pick` (arriba); acá solo va lo que cambia: hacia
     dónde abre y el ancho. */
  .barra-movil details{position:relative; flex:1}
  .barra-movil details .pick-menu{
    position:absolute; bottom:100%; right:0; left:auto;
    min-width:200px; max-width:calc(100vw - 32px); margin-bottom:8px;
  }
  /* Las filas del menú NO son pastillas de la barra: `.barra-movil a` (arriba)
     les daba 56px de alto, columna centrada y gris; acá vuelven a ser filas
     de menú de 13px, alineadas a la izquierda, como las del selector de
     contribuyente. */
  .barra-movil .pick-menu a{min-height:0; flex:none; text-align:left; font-weight:400}
  @media (max-width:900px){ .barra-movil{display:flex} }
  /* El contenido no puede terminar TAPADO por la barra fija (docs/70 §7): el
     mismo hueco que ella ocupa (56px + el "home indicator") se lo suma acá
     `main`, y solo en las pantallas CON sidebar -- la barra no existe en
     login/código/landing, así que esas no llevan el padding de más. */
  @media (max-width:900px){
    .shell .col main{padding-bottom:calc(56px + env(safe-area-inset-bottom) + var(--e4))}
  }

  /* ---------- el botón primario fijo al pie, en formularios largos ---------- */
  /* `.accion-fija` (⚖ fundador, docs/70 §7): en un formulario largo -- alta
     de propiedad, walk-in, cambiar contraseña, subir la llave del dueño --
     el botón de guardar queda arriba de la barra inferior en vez de al final
     del scroll, para no obligar a volver a subir la pantalla para confirmar.
     Clase utilitaria y no un bloque nuevo de `base.html`: cualquier
     formulario largo la agrega a SU botón sin tocar el marcado de alrededor.

     `--accion-fija-pie` es 0 por default (pantallas SIN `.barra-movil`, como
     el recorrido del dueño por magic link, que no tiene sidebar) y sube a
     56px adentro de `.shell` -- para no tapar la barra de abajo con el
     botón --, así que la misma clase sirve en las dos clases de pantalla sin
     que el formulario tenga que saber cuál es. */
  :root{--accion-fija-pie:0px}
  @media (max-width:900px){ .shell{--accion-fija-pie:56px} }
  @media (max-width:900px){
    /* Revisión de #461: el botón NO se repinta de fondo -- conserva el
       relleno `--accent` del primario (un `background:var(--bg)` acá lo dejaba
       blanco sobre blanco). Flota como píldora con margen, arriba de la barra,
       con la sombra de los menús para despegarse del contenido. */
    .accion-fija{
      position:fixed; left:var(--e3); right:var(--e3); z-index:24; width:auto;
      margin:0; box-shadow:var(--sh-lift);
      bottom:calc(var(--accion-fija-pie) + env(safe-area-inset-bottom) + var(--e2));
    }
    /* El campo que quedaba último en el formulario no se pierde detrás del
       botón fijo: el formulario suma el mismo alto que el botón se lleva del
       flujo normal al salir de posición. `:has()` (Chrome/Edge/Safari/
       Firefox vigentes) evita sumar una segunda clase repetida en cada
       plantilla solo para este padding. */
    form:has(> .accion-fija){padding-bottom:64px}
    /* Y la página entera suma el alto del botón fijo al de la barra: si no,
       lo último de la pantalla (la lista de consentimiento de la llave, el
       pie de un formulario) queda tapado. */
    .shell .col main:has(.accion-fija){
      padding-bottom:calc(56px + 64px + env(safe-area-inset-bottom) + var(--e4))}
    main:has(.accion-fija){padding-bottom:calc(64px + env(safe-area-inset-bottom) + var(--e4))}
    /* El toast global (#toast-wrap, abajo al centro) sube por encima de la
       barra y del botón fijo: si no, tapa el destino central y le roba el
       toque mientras está visible. */
    body:has(.barra-movil) .toast-wrap{
      bottom:calc(56px + env(safe-area-inset-bottom) + var(--e3))}
    body:has(.accion-fija) .toast-wrap{
      bottom:calc(var(--accion-fija-pie) + 64px + env(safe-area-inset-bottom) + var(--e3))}
  }

  @media (max-width:640px){
    /* En 375px, 32px de aire por lado se comen un sexto de la pantalla. */
    .wrap{padding:0 16px}
    nav.top{padding:16px 0}
    main{padding:var(--e3) 0 40px}
    /* Tres tarjetas de 100px de ancho no las lee nadie: en móvil van apiladas. */
    fieldset.opciones .lista{grid-template-columns:1fr}
    /* Dos inputs de ~100px lado a lado (signup, formularios) no se leen a 375px:
       la rejilla de dos columnas colapsa a una sola. */
    .grid{grid-template-columns:1fr}
    /* La tira de contadores del funnel sí sigue siendo horizontal, y a 375px no
       entra: scrollea sola sin encoger las píldoras (siguen tocables). */
    .chips{flex-wrap:nowrap; overflow-x:auto; -webkit-overflow-scrolling:touch}
    .chips a{flex:none; white-space:nowrap}
    /* El indicio de que la barra de tabs sigue a la derecha (⚖ fundador
       29-sep-2026, el teléfono en vertical: Operación trae seis pantallas y
       a 360-430px entran tres). El teléfono no dibuja barra de scroll, así
       que sin esto el carril se ve como si terminara en el tercer tab. Una
       MÁSCARA que disuelve los últimos 28px del carril, y no un degradado
       pintado con el color del fondo: así no hay un segundo consumidor de
       `--bg-app` (`tests/test_tokens.py` fija que sea solo el `body`) y
       funciona sobre cualquier fondo. El `padding-right` del mismo ancho
       deja el último tab entero a la vista cuando el scroll llega al final:
       lo que se disuelve es aire, nunca letras. Sin JS: `nav.js` ya centra
       el tab activo, esto solo dice que hay más. */
    .chips.tabs{
      padding-right:28px;
      -webkit-mask-image:linear-gradient(90deg, #000 calc(100% - 28px), transparent);
      mask-image:linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    }
  }

  /* ================================================================
     De acá para abajo, el vocabulario de UNA pantalla cada vez.

     Arriba está el sistema --lo que usan todas--; abajo, los bloques que
     existen para una sola: la lista de comprobantes, las liquidaciones, el
     resumen fiscal, los primeros pasos, la hoja del dueño, la ocupación y la
     portada. Se apilan en el orden en que se construyeron y no comparten
     selectores entre sí, salvo un caso que sí importa: la hoja del dueño usa
     el `.cifra` de los primeros pasos, y por eso va debajo.

     OJO con el papel: el bloque de la hoja del dueño trae tres reglas de
     `@media print` SIN scope -- `@page`, `body{font-size:12pt}` y el `a` sin
     color. Aplican a toda pantalla que se imprima, no solo a la del dueño. En
     pantalla esto no mueve un pixel; en papel sí.
     ================================================================ */

  /* ========== la lista de comprobantes (handoff-comprobantes) ========== */

  /* ---------- comprobantes: la barra de herramientas ----------
     Una fila flex que envuelve, para los controles que viven ARRIBA de una
     lista: buscar a la izquierda, y a la derecha lo que se lleva o crea. Nació
     el 11-sep-2026 en Comprobantes, donde esos controles eran dos renglones
     apilados --tres botones con una oración debajo, y el buscador aparte-- y
     empujaban la primera fila de datos media pantalla hacia abajo.

     El buscador entra TAL CUAL (`_buscador.html`, con su `hidden` de filtros y
     su GET): acá solo suelta sus márgenes propios, porque el aire lo pone la
     barra. Crece con lo que sobra -- su `input` tiene su propio tope de 340px,
     así que lo que gana es hueco entre "Buscar" y los botones de la derecha, no
     un campo de 600px para teclear un apellido. */
  .barra{display:flex; flex-wrap:wrap; align-items:center; gap:var(--e2);
    margin:var(--e3) 0 var(--e4)}
  .barra .buscador{order:1; flex:1 1 280px; margin:0}
  /* Los botones van juntos y en ese orden: los secundarios y, al final, la
     única acción primaria de la pantalla. */
  .barra-fin{order:2; display:flex; flex-wrap:wrap; align-items:center;
    gap:var(--e2); margin-left:auto}
  /* La línea que el buscador escribe cuando hay término puesto ("mostrando lo
     que coincide con X") no es un control: baja sola al renglón de abajo en vez
     de partir la fila en dos. El `order` es lo único que la mueve -- en el HTML
     sigue pegada a su formulario, que es donde la lee un lector de pantalla. */
  .barra > .hint{order:3; flex:0 0 100%; margin:0}

  @media (max-width:640px){
    /* A 375 el campo se queda con el renglón entero y los botones bajan: los
       tres al lado de un buscador usable no entran, y un buscador de 90px no es
       un buscador. */
    .barra .buscador{flex:0 0 100%}
    .barra-fin{flex:0 0 100%; margin-left:0}
  }

  /* ---------- el botón "Exportar" (jerarquía §3, docs/70, 24-sep-2026) ----------
     Un `<details>` con enlaces de verdad, mismo mecanismo que el selector de
     contribuyente y el resto de los plegables de la app -- cero JavaScript.
     `.pick-menu` es EL MISMO nombre que usa ese selector (`.emisor-pick`,
     `base.html`) para el mismo gesto (un menú flotando debajo del botón que lo
     abre); acá vive su propio scope (`.exportar`) porque el selector de
     contribuyente es del nav y esta pasada no lo toca. */
  .exportar details{position:relative; display:inline-block}
  .exportar summary{list-style:none; cursor:pointer; display:inline-flex; align-items:center; gap:6px}
  .exportar summary::-webkit-details-marker{display:none}
  .exportar details[open] > summary{border-color:var(--accent)}
  .exportar .pick-menu{
    position:absolute; z-index:5; top:calc(100% + 6px); right:0; min-width:200px;
    background:var(--bg); border:1px solid var(--line); border-radius:var(--r-md); padding:var(--e1);
    box-shadow:0 10px 26px rgba(27,38,36,.07);
  }
  .exportar .pick-menu a{
    display:block; padding:8px 12px; border-radius:var(--r-sm); text-decoration:none;
    color:var(--ink); font-size:var(--t-md); white-space:nowrap;
  }
  .exportar .pick-menu a:hover{background:var(--soft)}

  /* ---------- comprobantes: la fila es un renglón ----------
     Punto 2 de la revisión del 28-ago (hallazgo 1). Reemplaza la tabla +
     `.tabla-scroll` de `comprobantes.html`.

     La decisión: a esta pantalla no se entra a leer totales. Se entra a BUSCAR
     uno (el buscador dice "Receptor o clave") o a ver QUÉ SE TRABÓ (los tres
     filtros, y el estado del propietario aterriza acá). Así que la fila no es
     una tabla de siete columnas: es un renglón de identificación, y el motivo
     es lo que la abre.

     Consecuencia: un comprobante aceptado ocupa una línea porque no tiene nada
     que decir; uno retenido o rechazado ocupa el triple. El peso lo pone el
     estado -- la misma lógica de `.card-aviso`, aplicada a la fila.

     No hay grilla, no hay rótulos de columna y no hay cabecera que mantener
     alineada. El renglón envuelve solo: a 375px pasa a dos líneas. */

  .cmp-lista{background:var(--bg); border:0; border-radius:var(--r-lg); box-shadow:var(--sh-float); overflow:hidden}
  .cmp-fila{padding:13px var(--e4); border-bottom:1px solid var(--line)}
  .cmp-fila:last-child{border-bottom:0}
  .cmp-fila:hover{background:var(--soft)}

  .cmp-linea{display:flex; align-items:baseline; gap:var(--e3)}
  /* `align-items:baseline` y no `center`: la fecha, el nombre, el monto y el
     badge tienen tamaños distintos y se leen como un renglón de texto. */
  .cmp-id{flex:1; min-width:0; display:flex; flex-wrap:wrap; align-items:baseline;
    gap:0 8px; font-size:var(--t-md); line-height:1.5}
  .cmp-id .f{font-weight:600; font-variant-numeric:tabular-nums}
  /* Los puntos medios son separadores, no contenido: quedan en `--mute` para
     que el nombre gane el renglón. Van en el HTML y no en un `::before`
     porque el lector de pantalla los lee como la pausa que son. */
  .cmp-id .sep, .cmp-id .meta{color:var(--mute)}
  /* El tiquete al portador no tiene receptor. Se escribe el motivo, no un
     guion, y en `--mute` para que no se lea como un nombre. */
  .cmp-id .q.nadie{color:var(--mute)}

  /* El total NO es titular: es lo que desempata dos comprobantes del mismo día.
     13,5px en peso 600, con la moneda real del campo -- el símbolo lo trae el
     valor, no el CSS. */
  .cmp-monto{font-size:var(--t-md); font-weight:600; white-space:nowrap;
    text-align:right; min-width:104px;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  .cmp-fila .badge{white-space:nowrap}
  /* Las cifras tabulares no alinean nada si la columna se corre: un badge de
     "pendiente" es 8px más ancho que uno de "aceptado" y movía el total de ESA
     fila, que es justo lo que hace imposible comparar dos montos con el ojo. El
     ancho mínimo del badge fija la columna; el del monto, el borde contra el
     que caen los decimales.
     El selector es el badge que viene DESPUÉS de un monto, así que toca solo la
     fila de un comprobante: las otras pantallas que reusan `.cmp-fila` --la
     cartera, el recap del dueño, la portada-- no tienen monto ni columna que
     alinear. */
  .cmp-monto ~ .badge{min-width:86px; text-align:center}
  .cmp-ver{font-size:var(--t-md); font-weight:600; text-decoration:none; white-space:nowrap}

  /* La nota bajo el badge de la columna "Comprobante" de Reservas (⚖ fundador
     04-sep-2026): "sin datos del huésped". Va en bloque y en `--mute` porque no
     es un estado más --el estado es el badge de arriba-- sino el porqué del
     enlace de al lado. */
  .res-nota{display:block; margin-top:4px; font-size:var(--t-xs); color:var(--mute)}

  /* Los motivos. Un `<span>` por motivo y en bloque: unidos con un espacio, dos
     motivos se leían como una sola frase larga. El tinte es el del estado; el
     glifo y la palabra siguen en el badge de arriba, así que el color acompaña
     y no carga solo el significado. */
  .cmp-motivos{margin-top:9px; padding:var(--e2) 13px; border-radius:var(--r-md);
    font-size:var(--t-sm); line-height:1.55; display:flex; flex-direction:column; gap:3px;
    background:var(--warn-bg)}
  .cmp-motivos.bad{background:var(--err-bg)}
  /* CON EL FILTRO PUESTO. Toda la lista está en el mismo estado, así que el
     tinte ya no distingue nada y veinte bloques ámbar apilados son ruido: el
     banner de arriba ya dijo en qué estado estás. La plantilla pone `plano`
     cuando hay filtro. */
  .cmp-motivos.plano{background:var(--soft); border:1px solid var(--line)}

  @media (max-width:640px){
    /* El renglón de identificación se queda con el ancho entero y el monto, el
       badge y el enlace bajan a un segundo renglón. No hay scroll horizontal en
       ningún punto: nada de esto tiene ancho mínimo. */
    .cmp-linea{flex-wrap:wrap; row-gap:8px}
    .cmp-id{flex:0 0 100%}
    /* Piso táctil de móvil en el único control de la fila. */
    .cmp-ver, .fila-ver{margin-left:auto; min-height:36px; line-height:36px}
    .accion-pendiente{display:inline-block; min-height:36px; line-height:36px}
  }

  /* ========== la lista de liquidaciones (handoff-liquidaciones-lista) ========== */

  /* ---------- liquidaciones: la fila es una cuenta ----------
     Punto 2 de la revisión del 28-ago (hallazgo 1), segunda pantalla. Reemplaza
     la tabla + `.tabla-scroll` de `liquidaciones.html`.

     La decisión: las cuatro columnas de plata SON una resta -- la plantilla ya
     lo dice en su comentario ("bruto - plataformas - comision = neto, que es la
     cuenta que el dueño hace con el ojo") -- y la tabla la dejaba implícita en
     el orden de las columnas. Acá se escribe con los signos, y el neto cae
     donde cae el "=".

     ⚠ EL ORDEN DEL DOM NO CAMBIA: periodo, bruto, plataformas, comision, neto.
     El neto queda a la derecha porque es el último término, no porque se lo
     haya movido. Los tests que leen la fila por posición siguen leyendo lo
     mismo -- lo que cambia es que ya no son `<td>`. Ver el README. */

  .liq-lista{border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden}
  .liq-fila{padding:var(--e3) var(--e4); border-bottom:1px solid var(--line)}
  .liq-fila:last-child{border-bottom:0}
  .liq-fila:hover{background:var(--soft)}

  /* La cuenta. `align-items:baseline` y no `center`: el período, los términos y
     el neto tienen tres tamaños y se leen como un renglón. */
  .liq-cuenta{display:flex; align-items:baseline; gap:var(--e3)}
  /* El período empuja: es él el que se come el aire que sobra, así que el "="
     y el neto quedan pegados. Si en vez de esto el que estira son los términos,
     el "=" se separa del resultado media pantalla y la cuenta deja de leerse. */
  .liq-periodo{flex:1; min-width:0; font-size:var(--t-lg); font-weight:600;
    letter-spacing:-.01em; text-transform:capitalize; white-space:nowrap}
  /* Los tres términos y sus dos signos, en un renglón que envuelve. Son la
     comprobación, no el resultado: 12,5px en `--mute`. */
  .liq-terminos{min-width:0; display:flex; flex-wrap:wrap; justify-content:flex-end;
    align-items:baseline; gap:0 7px; font-size:var(--t-sm); color:var(--mute);
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  /* El neto SÍ es el dato primario de esta pantalla: es la plata que se le
     transfiere al dueño y el número por el que llama. Moneda real del campo --
     el símbolo lo trae el valor, no el CSS. */
  .liq-neto{font-size:var(--t-h2); font-weight:600; letter-spacing:-.015em; white-space:nowrap;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}

  /* Aviso y respuesta. Eran las dos columnas más anchas de la tabla y las que
     empujaban el neto afuera del borde. Bajan a un renglón propio con su
     rótulo al lado, así que se entienden sin cabecera. */
  .liq-estados{display:flex; flex-wrap:wrap; align-items:center; gap:8px var(--e4);
    margin-top:var(--e2)}
  .liq-estado{display:flex; align-items:center; gap:8px}
  .liq-estado .badge{white-space:nowrap}
  .liq-estado .cuando{font-size:var(--t-sm); color:var(--mute); white-space:nowrap}
  /* El motivo por el que no hay respuesta, escrito. Reemplaza el guion. */
  .liq-estado .sin{font-size:var(--t-sm); color:var(--mute)}
  .liq-estado a{font-size:var(--t-md); font-weight:600; text-decoration:none; white-space:nowrap}

  /* La objeción, tal cual la escribió el dueño. Es lo único de la pantalla que
     no puede esperar, así que lleva el tinte de error y el ancho entero. Sin
     recortar y sin comillas: el texto es de él, no nuestro. */
  .liq-objecion{margin-top:var(--e2); padding:11px var(--e3); border-radius:var(--r-md);
    background:var(--err-bg); font-size:var(--t-sm); line-height:1.6}

  @media (max-width:900px){
    /* El neto sube al renglón del período antes de que los términos se aprieten
       en una columna de dos palabras. `order` mueve la CAJA, no el DOM: el
       orden de lectura y el de los tests siguen siendo el mismo. */
    .liq-cuenta{flex-wrap:wrap; row-gap:var(--e1)}
    .liq-terminos{order:1; flex:0 0 100%; justify-content:flex-start}
  }
  @media (max-width:640px){
    /* Piso táctil de móvil en el único control de la fila. */
    .liq-estado a{min-height:36px; line-height:36px}
  }

  /* ========== el resumen de la cuenta (handoff-account-detail) ========== */

  /* ---------- account_detail: la pantalla es el estado de la cartera --------
     Punto 3 de la revisión del 28-ago. Sale el índice de ocho cards; queda el
     estado por propietario, que es lo que la pantalla ya hacía bien en su
     segunda mitad.

     ⚠ DEPENDE DE `handoff-comprobantes`, igual que la portada: las filas son
     `.cmp-lista`, `.cmp-fila`, `.cmp-linea`, `.cmp-ver` y `.cmp-motivos`. Lo
     único propio de esta pantalla son las tres reglas de abajo. */

  /* El nombre del propietario con su régimen al lado. `flex-wrap` porque el
     nombre lo escribió el host y puede ser largo: "Inversiones Tamarindo
     Sociedad Anónima" no tiene por qué empujar el badge de estado. */
  .ad-nombre{flex:1; min-width:0; display:flex; flex-wrap:wrap; align-items:baseline;
    gap:var(--e1) 8px}
  .ad-nombre > .n{font-size:var(--t-md); font-weight:600}

  /* Las piezas que componen el "necesita atención": van en el bloque tintado de
     `.cmp-motivos`, porque son lo único de la fila que pide trabajo. Es el mismo
     gesto que el motivo de un comprobante retenido.

     `line-height` más alto que en `.cmp-motivos` a secas: acá el contenido son
     enlaces separados por " · " que envuelven, y apretados se tocan entre
     renglones. */
  .cmp-motivos.ad-piezas{line-height:1.7}
  .cmp-motivos.ad-piezas a{font-weight:600; text-decoration:none}

  /* El IVA acompaña pero NO decide el estado (docs/48): es el número que
     orienta, no una urgencia. Por eso va abajo, en tono de dato, sin tinte y sin
     la escala de cifra -- 19px acá pondría el IVA por encima del estado, que es
     justo lo que la pantalla decidió que no. */
  .ad-iva{margin-top:7px; font-size:var(--t-sm); color:var(--mute); line-height:1.55}
  .ad-iva b{font-weight:600; color:var(--ink); font-variant-numeric:tabular-nums;
    font-feature-settings:"tnum"}

  /* ========== el resumen fiscal (handoff) ========== */
  /* ---------- resumen fiscal: las casillas del formulario de IVA ----------
     La pantalla dejó de ser una pared de tablas y pasó a ser lo que la contadora
     hace con ella: transcribir casilla por casilla a la plataforma. Tres
     decisiones, y ninguna es estética:

       1. UNA casilla es UNA fila -- etiqueta · monto · copiar --, así el ojo no
          pierde el renglón a mitad de camino. Antes el monto y su etiqueta eran
          dos celdas de una grilla de dieciocho filas. La etiqueta es el NOMBRE
          OFICIAL de la casilla en TRIBU-CR, que es como se la busca en la
          plataforma: no hay columna de número porque el formulario 150 no
          numera sus casillas (verificado 03-sep-2026 contra declaraciones
          reales).
       2. El contribuyente que no se está mirando se PLIEGA. Tres emisores por
          dieciocho filas eran cincuenta y cuatro renglones con el mismo peso.
       3. El impuesto determinado sale de la tabla y se vuelve su propia fila
          destacada: es el número por el que la CPA entró a la pantalla.

     `<details>` y no JavaScript, igual que el selector de contribuyente del
     nav: el estado no sale del navegador y cada bloque se puede abrir en su
     propia pestaña. */

  /* La fila de una casilla. `grid` y no `flex`: las tres columnas tienen que
     alinearse ENTRE FILAS -- con flex, una etiqueta larga corre el monto de su
     fila y la columna de números deja de ser una columna. */
  .casilla{
    display:grid; grid-template-columns:1fr minmax(130px, auto) auto;
    gap:var(--e3); align-items:center; padding:9px 0; border-top:1px solid var(--line);
  }
  .casilla:first-of-type{border-top:0}
  .casilla-t{font-size:var(--t-md); min-width:0}
  .casilla-t .hint{margin-top:2px}
  /* El monto: tabular y a la derecha, que es como se lee una columna de plata. */
  .casilla-monto{
    font-size:var(--t-md); text-align:right;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum";
  }
  .casilla.total .casilla-t, .casilla.total .casilla-monto{font-weight:600}
  /* El botón de copiar. Es la interacción que la contadora repite todo el día,
     así que respeta el piso táctil de 36px como cualquier control de fila: es
     `.btn-chico` con menos aire lateral, no un botón nuevo. */
  .casilla-copiar{
    padding:var(--e1) var(--e2); min-height:36px; border-radius:var(--r-sm);
    background:var(--bg); color:var(--mute); border:1px solid var(--line);
    font-size:var(--t-xs); font-weight:600;
  }
  .casilla-copiar:hover{border-color:var(--accent); color:var(--terra-text)}
  /* Copiado: la palabra cambia y el color acompaña. El color no va solo. */
  .casilla-copiar[data-copiado]{border-color:var(--ok); color:var(--ok)}

  /* El link que el anfitrión le manda al huésped para que cargue sus datos
     fiscales. Reusa el botón de copiar de las casillas; lo único propio es la
     fila, porque acá lo que se copia es una URL larga y tiene que poder
     quebrarse sin desbordar la tarjeta en un teléfono. */
  .link-huesped{
    display:flex; align-items:center; gap:var(--e3); flex-wrap:wrap;
    padding:var(--e2) 12px; border:1px solid var(--line); border-radius:var(--r-sm);
    background:var(--soft);
  }
  .link-huesped code{
    flex:1 1 240px; min-width:0; font-size:var(--t-sm); color:var(--mute);
    word-break:break-all;
  }

  /* La fila del número que la pantalla vino a dar. Tinte de marca y no de
     estado: "determinado" no es bueno ni malo, es el resultado. */
  .casilla-clave{
    display:grid; grid-template-columns:1fr auto auto; gap:var(--e3);
    align-items:center; padding:var(--e3) 16px; margin-top:var(--e3);
    border:1px solid var(--terra); border-radius:var(--r-md);
    background:var(--brand-tint);
  }
  .casilla-clave .casilla-t{font-size:var(--t-lg); font-weight:600; letter-spacing:-.01em}
  .casilla-clave .casilla-monto{font-size:var(--t-h2); font-weight:600; letter-spacing:-.02em}
  .casilla-clave .casilla-copiar{
    padding:8px 16px; background:var(--terra-text); color:#fff; border-color:transparent;
    font-size:var(--t-md);
  }
  .casilla-clave .casilla-copiar:hover{filter:brightness(1.08); border-color:transparent; color:#fff}

  /* El desglose de una casilla: de dónde salen los sumandos de un monto que en
     el formulario es UNA sola casilla (el crédito fiscal sale del buzón y de
     las FEC propias, y TRIBU-CR pide el total). Va sangrado y en gris bajo su
     casilla, sin botón de copiar: lo que se transcribe es el total, no el
     sumando -- un botón acá invitaría a pegar el número equivocado. */
  .casilla-desglose{
    padding:2px 0 8px var(--e3); border-left:2px solid var(--line); margin-left:2px;
  }
  .casilla-desglose > span{
    display:flex; justify-content:space-between; gap:var(--e3);
    font-size:var(--t-sm); color:var(--mute); padding:3px 0;
  }
  .casilla-desglose .v{font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}

  /* Las columnas de casillas, una por sección del formulario. Se apilan solas en
     pantalla angosta por el `auto-fit`, sin una media query propia. */
  /* `min(340px,100%)` y no `340px` pelado: con el mínimo duro, a 375px la
     columna mide más que el ancho disponible y el que se desborda es el
     DOCUMENTO -- la pantalla entera se iba de costado para que la casilla
     entrara. Con `min()` la columna cede y la fila de casilla ya sabe apilarse
     (ver la media query de 640px, abajo). */
  .casillas-cols{display:grid; grid-template-columns:repeat(auto-fit, minmax(min(340px,100%),1fr)); gap:var(--e5)}
  /* El rótulo de cada columna: mismo tratamiento que `label` y que los rótulos
     de grupo. Existe para no escribirlo con un `style=` en cada columna. */
  .casillas-rot{margin-bottom:8px}

  /* ---------- el contribuyente plegable ---------- */
  /* Los dos botones de abrir/plegar todas, alineados a la derecha y pegados a
     la primera tarjeta: son del grupo, no de la cabecera de la pantalla. */
  .rf-plegado{display:flex; justify-content:flex-end; gap:var(--e2);
    margin-bottom:var(--e2)}
  .rf-emisor{border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden;
    margin-bottom:var(--e3)}
  .rf-emisor > summary{
    list-style:none; cursor:pointer; display:flex; align-items:center; gap:12px;
    padding:16px var(--e4); background:var(--soft);
  }
  .rf-emisor > summary::-webkit-details-marker{display:none}
  /* El chevrón es un `::before` y no un SVG: el glifo cambia con el estado sin
     tocar el DOM, igual que el botón de plegar del nav. */
  .rf-emisor > summary::before{content:"▸"; color:var(--mute); font-size:var(--t-xs); flex:none}
  .rf-emisor[open] > summary::before{content:"▾"}
  .rf-emisor[open] > summary{border-bottom:1px solid var(--line)}
  .rf-emisor > summary:hover{background:var(--bg)}
  .rf-emisor > summary:focus-visible{outline:2px solid var(--terra-text); outline-offset:-2px}
  .rf-emisor-id{flex:1; min-width:0}
  .rf-emisor-n{font-size:var(--t-lg); font-weight:600; letter-spacing:-.01em}
  .rf-emisor-ced{display:block; font-size:var(--t-sm); color:var(--mute); margin-top:2px;
    font-variant-numeric:tabular-nums}
  /* El número del emisor en la cabecera: se ve PLEGADO, que es el punto -- con
     los tres cerrados la pantalla sigue diciendo cuánto es cada uno. */
  .rf-emisor-cifra{text-align:right; min-width:170px; flex:none}
  .rf-emisor-cifra .rot{display:block; font-size:var(--t-xs); color:var(--mute);
    text-transform:uppercase; letter-spacing:.05em}
  .rf-emisor-cifra .val{display:block; font-size:var(--t-h2); font-weight:600; letter-spacing:-.02em;
    font-variant-numeric:tabular-nums}
  .rf-emisor-cifra .nada{display:block; font-size:var(--t-md); color:var(--mute); line-height:1.4}
  .rf-emisor-cuerpo{padding:var(--e4); display:flex; flex-direction:column; gap:var(--e4)}
  /* El bloque de renta, al pie del contribuyente y separado por hairline: es
     otro formulario y otro período, no una casilla más del de IVA. */
  .rf-renta{border-top:1px solid var(--line); padding-top:16px;
    display:flex; align-items:flex-start; gap:var(--e5); flex-wrap:wrap}
  .rf-renta-datos{flex:1; min-width:260px}
  .rf-renta-pares{display:flex; gap:var(--e5); flex-wrap:wrap; margin-top:8px}
  .rf-renta-pares .par .k{font-size:var(--t-sm); color:var(--mute)}
  .rf-renta-pares .par .v{font-size:var(--t-lg); font-weight:600; font-variant-numeric:tabular-nums}

  /* ---------- el triage de arriba ---------- */
  /* Responde la pregunta con la que se entra --"¿cuáles puedo declarar hoy y
     cuánto falta?"-- antes de scrollear. Lo que tiene fecha de vencimiento
     propia va primero: un comprobante recibido que no se acepta a tiempo pierde
     su crédito de IVA y no se recupera. */
  .rf-triage{display:grid; grid-template-columns:1.15fr repeat(3, 1fr); gap:var(--e3); margin:var(--e4) 0}
  .rf-triage > *{border:1px solid var(--line); border-radius:var(--r-md); padding:var(--e4)}
  .rf-triage .estado{background:var(--soft); display:flex; flex-direction:column; gap:12px}
  .rf-triage .lista{display:flex; flex-direction:column; gap:8px}
  .rf-triage .bloque{border-top:1px solid var(--line); padding-top:12px;
    display:flex; flex-direction:column; gap:8px}
  .rf-triage .cifra{font-size:var(--cifra); font-weight:400; letter-spacing:-.025em;
    font-variant-numeric:tabular-nums; margin:var(--e1) 0}
  .rf-triage .cifra.vacia{color:var(--mute)}
  .rf-triage a{display:inline-block; font-size:var(--t-md); font-weight:600;
    text-decoration:none; padding:7px 0}

  /* La cabecera de la pantalla: título a la izquierda, el mes y la acción
     principal a la derecha. El navegador de mes era un `<p>` con dos enlaces de
     texto -- 18px de alto para el control que más se toca de la pantalla. Ahora
     usa los mismos botones que el resto, con su piso de 36px. */
  /* La cabecera de mes es LA MISMA en el resumen fiscal y en ocupación: las dos
     pantallas se recorren mes a mes con el mismo par de flechas y el mismo
     rótulo en el medio. Estaba declarada dos veces, palabra por palabra. */
  .rf-cab, .oc-cab{display:flex; align-items:flex-start; gap:var(--e4); flex-wrap:wrap}
  .rf-cab-mes, .oc-cab-mes{display:flex; align-items:center; gap:var(--e2)}
  .rf-cab-mes strong, .oc-cab-mes strong{font-weight:600; font-size:var(--t-h2);
    letter-spacing:-.02em; min-width:150px; text-align:center; text-transform:capitalize}
  /* La flecha toma la silueta de `.cal-nav a` -- radio 8px y no píldora -- para
     que las dos maneras de cambiar de mes en la app se vean iguales. */
  .rf-cab-mes .paso{border-radius:var(--r-sm); padding:8px var(--e3)}
  /* `nowrap` no es cosmético: "Exportar el mes" partía en dos renglones y un
     botón primario de la escala de página es de un renglón en todo el sistema. */
  /* El export es `.btn .btn-sec .btn-chico` del sistema; esto solo lo separa de
     las flechas. */
  .rf-cab-mes .btn-export, .oc-export{white-space:nowrap; margin-left:var(--e2)}

  /* "Ver: [contribuyente] [Mostrar]" -- el filtro del resumen fiscal, debajo de
     la fila del mes y solo con cartera. Una fila y no un bloque de formulario:
     es navegación, no carga de datos.

     El `select` se sale del `width:100%` de los campos (regla base) por la misma
     razón que el de `.tel`: acá comparte renglón con su rótulo y su botón, y a
     ancho completo los empujaría a tres renglones. El `max-width` es lo que
     impide que un nombre largo de sociedad estire la fila hasta el borde. */
  .rf-ver{display:flex; align-items:center; gap:var(--e2); flex-wrap:wrap;
    margin-top:var(--e4)}
  .rf-ver label{margin-bottom:0}
  .rf-ver select{width:auto; flex:0 1 auto; min-width:0; max-width:100%}
  .rf-ver button{margin-top:0}

  /* ---------- las secciones plegadas del pie ----------
     Lo que NO se transcribe se pliega, y el dato que la contadora necesita sin
     abrir viaja en el `summary`: la pantalla medía tres pantallas y media de
     alto y el que transcribe scrolleaba por bloques que solo se leen una vez al
     mes. El CONTENIDO sigue en la página, cerrado -- plegar no es esconder.

     Mismo gesto que `.ayuda` --un chevron que gira, el mismo signo cerrado y
     abierto-- pero a escala de sección: acá el `summary` ES el h2 del bloque,
     con su regla arriba. `.ayuda` se sigue usando ADENTRO, para el microcopy.

     Genérica y no `rf-*` a propósito, como `.ayuda` y `details.alta`: es el
     gesto de "sección secundaria plegada", no una pieza de esta pantalla.

     ⚠ "summary" va SIN sus ángulos acá: los comentarios de esta hoja viajan en
     la página, y una etiqueta escrita entera en un comentario es una etiqueta
     más para todo test que parsee el HTML (se llevó puesto el rótulo del
     selector de contribuyente el 11-sep-2026). */
  details.seccion{border-top:1px solid var(--line); padding-top:var(--e4); margin-top:var(--e4)}
  details.seccion > summary{list-style:none; cursor:pointer; display:flex;
    flex-wrap:wrap; align-items:baseline; gap:var(--e2); border-radius:var(--r-xs)}
  details.seccion > summary::-webkit-details-marker{display:none}
  details.seccion > summary:hover .seccion-t{color:var(--terra-text)}
  details.seccion > summary:focus-visible{outline:2px solid var(--terra-text); outline-offset:2px}
  /* Abierta, la sección se despega de su propio título; cerrada no hace falta
     el aire porque abajo viene la regla de la siguiente. */
  details.seccion[open] > summary{margin-bottom:var(--e3)}
  .seccion-t{font-weight:600; font-size:var(--t-h2); letter-spacing:-.02em; min-width:0}
  /* El chevron cuelga del TÍTULO y no del summary: como ítem propio de la fila
     se quedaba solo en su renglón cuando el título no entraba --a 375px "Las
     comisiones de plataforma del mes" envuelve-- y una flecha sola arriba del
     título no se lee como el control que abre la sección. */
  .seccion-t::before{content:"\203A"; display:inline-block; font-size:var(--t-h1);
    line-height:1; color:var(--mute); margin-right:var(--e2);
    transition:transform .12s ease}
  details.seccion[open] > summary .seccion-t::before{transform:rotate(90deg)}
  /* El dato del summary: lo que se lee SIN abrir. Cifras tabulares porque casi
     siempre es plata, y con `min-width:0` para que a 375px envuelva en vez de
     empujar la página a lo ancho. */
  .seccion-dato{font-weight:400; font-size:var(--t-md); color:var(--mute);
    letter-spacing:0; min-width:0; font-variant-numeric:tabular-nums}

  /* `.oc-contexto` --las doce columnas de ocupación-- es el mismo gesto en otra
     pantalla. Sigue con su propio bloque de reglas: el resumen fiscal pasó a
     `details.seccion` el 11-sep-2026 y ocupación no se tocó, así que lo que era
     un selector compartido (`.rf-contexto, .oc-contexto`) quedó solo con la
     mitad de ocupación. Lo único suyo es que no lleva aire arriba. */
  .oc-contexto{border-top:1px solid var(--line); padding-top:var(--e4)}
  .oc-contexto > summary{
    list-style:none; cursor:pointer; font-weight:600; font-size:var(--t-h2); letter-spacing:-.02em;
  }
  .oc-contexto > summary::-webkit-details-marker{display:none}
  .oc-contexto > summary::before{content:"▸"; color:var(--mute); font-size:var(--t-xs); margin-right:8px}
  .oc-contexto[open] > summary::before{content:"▾"}
  .oc-contexto > summary .sub-inline{font-weight:400; font-size:var(--t-md); color:var(--mute);
    margin-left:var(--e2); letter-spacing:0}
  .oc-contexto > summary:focus-visible{outline:2px solid var(--terra-text); outline-offset:2px}
  /* `min()` y no `380px` pelado: con el mínimo duro, a 375px la columna es más
     ancha que la pantalla y empuja el documento entero a lo ancho. */
  .rf-contexto-cols{display:grid; grid-template-columns:repeat(auto-fit, minmax(min(380px,100%),1fr));
    gap:var(--e5); margin-top:var(--e4)}

  /* La tarjeta de lo que volvió por emitir las facturas de compra. NO es una
     casilla --es la resta de dos de ellas-- y por eso no lleva ni botón de
     copiar ni el tinte terracota de `casilla-clave`, que es el número que se
     transcribe. Hairline sobre el crema, como las tarjetas del triage. (Sin su
     rótulo escrito acá, por lo mismo que abajo: los comentarios de esta hoja
     VIAJAN en la página y le sumarían una aparición al conteo de los tests.) */
  .rf-recuperado{border:1px solid var(--line); border-radius:var(--r-md);
    padding:var(--e3) 16px; background:var(--soft)}
  .rf-recuperado .cifra{font-size:var(--t-h1); font-weight:400; letter-spacing:-.025em;
    font-variant-numeric:tabular-nums; margin:var(--e1) 0}
  .rf-recuperado .rf-renta-pares{margin-top:var(--e2)}

  /* El campo donde se pega lo que el banco retuvo por las ventas con tarjeta.
     Es un formulario adentro de una sección de casillas, así que hereda el
     mismo hairline sobre crema de la tarjeta de arriba -- lo que lo separa de
     una casilla es que se ESCRIBE, y por eso el borde en vez de una fila más.
     Los dos formularios (guardar y borrar) van en la misma línea. */
  .rf-retencion{border:1px solid var(--line); border-radius:var(--r-md);
    padding:var(--e3) 16px; background:var(--soft); margin:var(--e3) 0;
    display:flex; flex-wrap:wrap; align-items:center; gap:8px}
  .rf-retencion .rot{flex:1 0 100%}
  .rf-retencion .hint{flex:1 0 100%; margin:0}
  /* El "¿de dónde sale?" plegado toma el renglón entero, igual que el `.hint`
     que reemplazó: si no, se pone a competir por el aire con el botón. */
  .rf-retencion > .ayuda{flex:1 0 100%; margin-top:0}
  .rf-retencion input{max-width:190px}

  @media (max-width:1180px){
    /* Cuatro tarjetas de triage no entran a lo ancho: se parten en dos filas y
       la del estado --la que tiene el vencimiento-- sigue primera. */
    .rf-triage{grid-template-columns:1.15fr 1fr}
  }
  @media (max-width:900px){
    /* Tres tarjetas de triage lado a lado no entran, y la primera es la que
       tiene la fecha de vencimiento: se apilan en ese orden. */
    .rf-triage{grid-template-columns:1fr}
  }
  @media (max-width:640px){
    /* La fila de casilla a 375px: el botón se queda en su renglón y la etiqueta
       --que es el nombre oficial, y es largo-- pasa a ocupar el ancho. El monto
       NO se separa de su etiqueta más de un renglón -- leerlos juntos es todo el
       trabajo de la pantalla. */
    .casilla{grid-template-columns:1fr auto; row-gap:var(--e1)}
    .casilla-t{grid-column:1 / -1}
    .casilla-monto{grid-column:1; text-align:left; font-size:var(--t-lg)}
    .casilla-copiar{grid-column:2}
    .casilla-clave{grid-template-columns:1fr auto; row-gap:8px}
    .casilla-clave .casilla-t{grid-column:1 / -1}
    .casilla-clave .casilla-monto{grid-column:1; text-align:left}
    /* La cabecera del emisor: la cifra baja debajo del nombre en vez de
       apretarse contra los badges. */
    .rf-emisor > summary{flex-wrap:wrap}
    .rf-emisor-cifra{text-align:left; min-width:0; width:100%}
    /* La cabecera de mes a 375px: las dos flechas, el rótulo y los DOS exports
       no entran en un renglón, y sin permiso para envolver el segundo export se
       salía de la pantalla -- era el desborde de costado del documento entero
       (561px de ancho a 375). `.oc-cab-mes` ya tenía este permiso; ésta no. */
    .rf-cab-mes{width:100%; flex-wrap:wrap}
    .rf-cab-mes .btn-export{margin-left:0}
    /* Y el rótulo del mes deja de exigir sus 150px cuando comparte renglón con
       las dos flechas. */
    .rf-cab-mes strong{min-width:0; flex:1}
    /* El campo de la retención: dos inputs y un botón no entran en 343px, y un
       `<input>` no baja de su ancho de contenido -- lo que se desbordaba era la
       fila. (El de la factura de la plataforma no necesita esto: vive en una
       celda, y ahí el ancho de más lo paga `.tabla-scroll`.) */
    .rf-retencion .inline-form{flex-wrap:wrap}
    .rf-retencion input{max-width:none; flex:1 0 100%; min-width:0}
  }

  /* ========== los primeros pasos (handoff-onboarding) ========== */

  /* ---------- escala de cifra y pesos de bloque ----------
     Las dos primeras cosas de la revisión del 28-ago, y no son de la pantalla
     de onboarding: las usa cualquiera. Van acá arriba, antes de las clases
     `pp-*`, porque son del sistema.

     LA CIFRA. Hasta hoy el único número grande de la app era el neto del dueño,
     escrito a mano con `style="font-size:34px"` en `owner_liquidacion.html`.
     Funcionaba: es la pantalla que mejor contesta su propia pregunta. Ahora es
     una clase con dos tamaños, los mismos dos que ya existen en la escala de
     títulos (27 y 34), así que no entra ningún tamaño nuevo al sistema.

     `tabular-nums` va acá adentro: es la razón por la que estaba escrito
     quince veces inline. Una cifra que no alinea sus dígitos no es una cifra. */
  .cifra{
    display:block; font-size:var(--cifra); font-weight:400; letter-spacing:-.025em;
    line-height:1.1; font-variant-numeric:tabular-nums; font-feature-settings:"tnum";
  }
  .cifra-g{font-size:var(--cifra-g); letter-spacing:-.02em}
  /* El denominador de una razón ("2 / 4", "18 / 31"): sigue siendo parte de la
     cifra, pero no es el número que se lee. */
  .cifra .de{color:var(--mute); font-size:.65em}
  /* El rótulo de arriba de una cifra. Mismo tratamiento que `label` -- existe
     para no repetir las cuatro declaraciones en cada tarjeta. */
  /* Cualquier celda o valor de plata, sin ser una cifra destacada. Reemplaza a
     los quince `style="font-variant-numeric:tabular-nums"` de
     `owner_liquidacion` y `liquidacion_avisar`. */
  .num{font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  /* La caja donde se ESCRIBE plata (`class="plata"`, ⚖ fundador 28-sep-2026):
     `nav.js` le pone los puntos de miles al salir, y acá se le da la misma
     cifra tabular que a `.num` para que lo tipeado se lea como el monto de al
     lado. Es el único estilo de la clase: lo demás es del handler. */
  input.plata{font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  /* ⚖ Fundador 24-sep-2026: adentro de una TABLA, una columna de plata se lee
     comparando el borde derecho de los montos, no la palabra -- alinearla a la
     izquierda (lo que traía por default) hacía que los dígitos no cayeran
     unos sobre otros aunque ya fueran tabulares. Solo dentro de una tabla: el
     mismo `.num` sigue sirviendo suelto en un `<dd>` o un `<span>` sin forzarle
     alineación que ahí no pintó nadie. */
  table td.num, table th.num{text-align:right}

  /* LOS PESOS. `.card` era el único recipiente, así que en la portada el aviso
     que abre la pantalla y la nota al pie eran el mismo rectángulo. Estas dos
     clases NO reemplazan a `.card`: la modifican, así que todo lo que hay hoy
     sigue igual hasta que alguien elija un peso.

     `.card.bloque` -- lo que la pantalla vino a resolver. Uno por pantalla.
     `.card.nota`   -- la nota al pie, el puente a otra pantalla. Sin fondo. */
  .card.bloque{background:var(--bg); border-color:var(--terra); padding:var(--e4)}
  .card.bloque > strong{font-size:var(--t-h2); letter-spacing:-.02em}
  .card.nota{background:none; border-style:solid; padding:16px var(--e4)}
  .card.nota > strong{font-size:var(--t-md)}

  /* ---------- primeros pasos ----------
     El paso 3 de esta pantalla ES el paso 2 de cada dueño, sumado. Cuando se
     traba --que es siempre-- lo que lo desbloquea vive en otra pantalla, y
     desde acá era un enlace al pie que decía "Abrir el tablero de propietarios"
     sin decir que ahí estaba la acción. Tres decisiones:

       1. El paso abierto es UNO y se lleva su cola adentro. Los dos primeros,
          cerrados, bajan a un renglón con su dato de cierre y su enlace.
       2. El paso bloqueado NO se esconde -- sigue la regla de `docs/40 §8`,
          solo que ahora en un renglón en vez de una tarjeta.
       3. "Esperando a otro" deja de ser el mismo gris que "bloqueado". El
          modelo ya distinguía los dos estados y el copy ya lo decía con nombre
          y apellido; lo único que faltaba era que se vieran distinto. */

  /* La cabecera: título a la izquierda, el avance como cifra a la derecha. El
     contador era `<strong>Listos: 2/4</strong>` a 14px dentro de una card
     propia, del mismo tamaño que el texto de al lado. */
  .pp-cab{display:flex; align-items:flex-start; gap:var(--e5); flex-wrap:wrap}
  .pp-cab-t .sub{max-width:70ch}
  .pp-avance{flex:none; text-align:right}
  .pp-avance .wiz-barra{width:180px; margin:8px 0 0}

  /* Los pasos cerrados. `grid` y no `flex`: el dato de cierre de los dos
     renglones tiene que arrancar en la misma columna. */
  .pp-hechos{border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden;
    margin:var(--e4) 0}
  .pp-hecha{
    display:grid; grid-template-columns:18px 1fr minmax(0, 22ch) auto;
    gap:12px; align-items:center; padding:11px var(--e4);
    border-bottom:1px solid var(--line);
  }
  .pp-hecha:last-child{border-bottom:0}
  .pp-hecha .n{font-size:var(--t-md); font-weight:600; min-width:0}
  .pp-hecha .dato{font-size:var(--t-md); color:var(--mute); min-width:0}
  .pp-hecha > a{font-size:var(--t-md); font-weight:600; text-decoration:none; white-space:nowrap}
  /* El tilde: el badge "Hecho" con su palabra ocupaba 60px en un renglón que ya
     dice "Activar la cuenta · La cuenta está al día". Acá el color no va
     solo -- el dato de cierre al lado es lo que afirma que está hecho. */
  .pp-tick{
    display:inline-flex; align-items:center; justify-content:center;
    width:18px; height:18px; border-radius:var(--r-pill); flex:none;
    font-size:var(--t-xs); font-weight:700;
    background:var(--ok-bg); color:var(--ok);
  }
  .pp-tick.pendiente{background:rgba(27,38,36,.06); color:var(--mute)}

  /* La lista compacta de los cuatro pasos (#B3, ⚖ fundador 21-sep-2026):
     reusa el mismo `.pp-tick` de arriba -- es el mismo hecho/pendiente de
     siempre, en un renglón más chico que `.pp-hecha`. El `siguiente` es el
     único primario del bloque (regla de #393): los demás pendientes se leen
     con el color de enlace de siempre, sin negrita. */
  .pp-lista{list-style:none; margin:var(--e4) 0 0; padding:0;
    display:flex; flex-direction:column; gap:8px}
  .pp-lista-fila{display:flex; align-items:center; gap:10px; font-size:var(--t-md)}
  .pp-lista-fila .n{color:var(--mute)}
  .pp-lista-fila a{text-decoration:none}
  .pp-lista-fila.primario a{font-weight:600}

  /* El paso bloqueado, en un renglón y con su motivo al lado. */
  .pp-bloqueado{
    display:flex; align-items:center; gap:12px; flex-wrap:wrap;
    border:1px solid var(--line); border-radius:var(--r-md); padding:16px var(--e4);
    opacity:.75;
  }
  .pp-bloqueado .n{font-size:var(--t-md); font-weight:600}
  .pp-bloqueado .motivo{font-size:var(--t-md); color:var(--mute)}

  /* El paso abierto, con la cola de dueños adentro. */
  .pp-paso{border:1px solid var(--terra); border-radius:var(--r-md); overflow:hidden}
  .pp-paso-cab{padding:var(--e4); background:var(--brand-tint);
    border-bottom:1px solid var(--line)}
  .pp-paso-h{display:flex; align-items:center; gap:var(--e2); flex-wrap:wrap}
  .pp-paso-h strong{font-weight:600; font-size:var(--t-h2); letter-spacing:-.02em}
  /* "Esperando a N dueños" sobre el tinte de marca: el badge `--warn` de siempre
     se pierde contra el fondo ámbar-terracota, así que va en blanco. No es un
     estado nuevo, es el mismo `esperando` legible sobre este fondo. */
  .pp-paso-h .badge-espera{background:var(--bg); color:var(--terra-text)}
  .pp-paso-cab p{margin:var(--e2) 0 0; font-size:var(--t-md); line-height:1.55; max-width:80ch}

  /* La cola. Las cuatro columnas se declaran UNA vez y las usan la cabecera y
     cada dueño: si no coinciden, deja de leerse como una lista. */
  .pp-cola-rot, .pp-dueno{
    display:grid; grid-template-columns:1.6fr 1.1fr 1fr auto;
    gap:var(--e3); align-items:center; padding:12px var(--e4);
    border-bottom:1px solid var(--line);
  }
  .pp-cola-rot{padding:9px var(--e4); background:var(--soft)}
  .pp-dueno:hover{background:var(--soft)}
  .pp-dueno-id{min-width:0}
  .pp-dueno-id > a{font-size:var(--t-md); font-weight:600; text-decoration:none; color:var(--ink)}
  .pp-dueno-id > a:hover{color:var(--terra-text)}
  .pp-dueno-ced{display:block; font-size:var(--t-sm); color:var(--mute); margin-top:2px}
  .pp-dueno-senal{font-size:var(--t-sm); color:var(--mute)}
  /* Las acciones de la fila. `gap` y no margen: la fila del dueño que ya está
     firmando lleva una sola, y no tiene que quedar corrida. */
  .pp-dueno-acc{display:flex; gap:var(--e3); justify-content:flex-end}
  .pp-dueno-acc a, .pp-dueno-acc button{
    font-size:var(--t-sm); font-weight:600; text-decoration:none; white-space:nowrap;
    padding:7px 0; margin:0; min-height:36px; background:none; border:0;
    color:var(--terra-text); font-family:inherit; cursor:pointer;
  }
  .pp-dueno-acc button:hover{color:var(--ink)}

  /* El pie de la cola: invitar a otro, y la salida al tablero completo. */
  .pp-cola-pie{display:flex; align-items:center; gap:var(--e3); flex-wrap:wrap;
    padding:var(--e3) var(--e4); background:var(--soft)}
  .pp-cola-pie .texto{font-size:var(--t-md); color:var(--mute); flex:1; min-width:280px}
  .pp-cola-pie a{font-size:var(--t-md); font-weight:600; text-decoration:none}

  @media (max-width:900px){
    .pp-avance{text-align:left}
    .pp-avance .wiz-barra{width:100%}
  }
  @media (max-width:640px){
    /* El renglón del paso cerrado: el dato de cierre baja debajo del nombre y
       el enlace se queda en su renglón. */
    .pp-hecha{grid-template-columns:18px 1fr auto; row-gap:4px}
    .pp-hecha .dato{grid-column:2 / -1}
    /* La fila del dueño: nombre y estado arriba, la señal y las acciones
       debajo. Los rótulos de columna dejan de servir a un renglón. */
    .pp-cola-rot{display:none}
    .pp-dueno{grid-template-columns:1fr auto; row-gap:8px; padding:var(--e3) var(--e4)}
    .pp-dueno-senal{grid-column:1 / -1}
    .pp-dueno-acc{grid-column:1 / -1; justify-content:flex-start; gap:var(--e4)}
  }

  /* ========== la liquidación del dueño (handoff-liquidacion) ========== */

  /* ---------- la liquidación del dueño: pantalla y papel ----------
     Es el entregable mensual y el dueño lo reenvía a su contador, así que la
     pantalla y la hoja son el mismo documento con dos vestidos. Tres
     decisiones, y ninguna cambia un número:

       1. El neto y su desglose comparten UN bloque. Estaban en dos recuadros
          separados por el cuadro fiscal: la cifra arriba, y las tres líneas que
          la explican dos pantallazos más abajo. La regla del research --"el neto
          nunca agrupado"-- solo se puede verificar si se ven juntas.
       2. El historial baja al pie. Es navegación, y tenía el mismo borde, el
          mismo radio y el mismo padding que el cuadre del mes.
       3. El papel se diseña. Hasta hoy `@media print` eran cuatro líneas que
          escondían el chrome; lo que salía era la pantalla sin el menú.

     Usa `.cifra` del bloque de arriba (el del handoff de onboarding). Si todavía
     no pegaste ése, pegalo primero. */

  /* El bloque del neto. Tinte de marca y no de estado: "le corresponde" no es
     bueno ni malo, es el resultado -- el mismo criterio que la fila del
     impuesto determinado en el resumen fiscal. */
  .ol-neto{
    border:1px solid var(--terra); border-radius:var(--r-md); background:var(--brand-tint);
    padding:24px; display:flex; align-items:flex-start; gap:32px; flex-wrap:wrap;
  }
  .ol-neto-cifra{flex:none}
  .ol-neto-cifra .cifra-rot{color:var(--terra-text)}
  /* El desglose. NO es `dl.kv`: ahí la clave va en `--mute` y el monto se pega
     a ella en una columna que se encoge, que es lo correcto para una ficha de
     datos. Estas tres líneas SON la cuenta que el dueño verifica, así que la
     clave va en tinta plena y los montos se alinean contra el neto. `.kv` queda
     como está: la ficha del permiso y los gastos la siguen usando. */
  .ol-desglose{
    flex:1; min-width:320px; display:grid; grid-template-columns:1fr auto;
    gap:7px var(--e4); margin:0; font-size:var(--t-md);
  }
  /* Cada línea del desglose es un `<div>` que envuelve su `<dt>` y su `<dd>`
     --así `.resta` y `.cierre` agarran a los dos-- y `display:contents` lo saca
     de la grilla para que el par siga cayendo en las dos columnas. Iba escrito
     en cada línea: la misma decisión, cinco veces. */
  .ol-desglose > div{display:contents}
  .ol-desglose dt, .ol-desglose dd{margin:0}
  .ol-desglose dd{text-align:right; font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  /* Las dos líneas de comisión van en `--mute`: son restas, no el resultado. */
  .ol-desglose .resta dt, .ol-desglose .resta dd{color:var(--mute)}
  .ol-desglose .cierre dt, .ol-desglose .cierre dd{
    font-weight:600; border-top:1px solid rgba(27,38,36,.12); padding-top:7px;
  }
  .ol-neto > .pie{flex-basis:100%; margin:0}

  /* Un bloque de documento: cabecera con su rótulo y el cuerpo debajo. Lo usan
     el cuadro fiscal y la tabla de estadías. */
  .ol-bloque{border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden}
  .ol-bloque-cab{padding:16px var(--e4); border-bottom:1px solid var(--line);
    display:flex; align-items:baseline; gap:12px; flex-wrap:wrap}
  .ol-bloque-cab strong{font-weight:600; font-size:var(--t-lg); letter-spacing:-.01em}
  .ol-bloque-cab .glosa{font-size:var(--t-md); color:var(--mute)}

  /* Las filas de plata del cuadro fiscal. Dos columnas, y la última fila cierra
     con la cifra que la persona vino a buscar. */
  .ol-kv{display:grid; grid-template-columns:1fr auto}
  .ol-kv > *{padding:var(--e2) var(--e4); border-bottom:1px solid var(--line); font-size:var(--t-md)}
  .ol-kv > .v{text-align:right; font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  .ol-kv > .nada{color:var(--mute)}
  .ol-kv > .cierre{
    padding:12px var(--e4); border-bottom:0; background:var(--soft);
    font-size:var(--t-lg); font-weight:600;
  }
  .ol-kv > .cierre.v{font-size:var(--t-h2); letter-spacing:-.02em}

  /* El detalle del 116 debajo del cuadro: la tabla de cinco columnas que el
     formulario cuelga de su primera casilla. Va con el mismo padding lateral
     que las filas del cuadro para que las dos cosas se lean como una, y su
     `.tabla-scroll` se encarga del ancho a 375px. (Sin el nombre de la casilla
     escrito acá a propósito: los comentarios de esta hoja VIAJAN en la página
     y le sumarían una aparición al conteo de los tests.) */
  .ol-detalle-rci{padding:var(--e3) var(--e4) 16px; border-top:1px solid var(--line)}
  .ol-detalle-rci table{margin-top:var(--e2)}

  /* La misma tarjeta al pie del cuadro fiscal del dueño: lo único que dice lo
     que le volvió y no lo que debe. Mismo padding lateral y misma hairline que
     el detalle de arriba, sobre el crema que ya usa la fila de cierre. */
  .ol-recuperado{padding:var(--e3) var(--e4) 16px; border-top:1px solid var(--line);
    background:var(--soft)}
  .ol-recuperado .cifra{font-size:var(--t-h1); font-weight:400; letter-spacing:-.025em;
    font-variant-numeric:tabular-nums; margin:var(--e1) 0}
  .ol-recuperado-pares{display:flex; gap:var(--e5); flex-wrap:wrap; margin-top:var(--e2)}
  .ol-recuperado-pares .par .k{font-size:var(--t-sm); color:var(--mute)}
  .ol-recuperado-pares .par .v{font-size:var(--t-lg); font-weight:600;
    font-variant-numeric:tabular-nums}

  /* Las estadías. Fila-grilla y no tabla: a 375px la tabla de cinco columnas
     se arrastraba en horizontal, y es el detalle que el contador revisa. */
  .ol-fila{
    display:grid; grid-template-columns:1.4fr 1fr auto minmax(150px, auto);
    gap:var(--e3); align-items:center; padding:11px var(--e4);
    border-bottom:1px solid var(--line);
  }
  .ol-fila.rotulos{padding:9px var(--e4); background:var(--soft)}
  .ol-fila > .n{font-size:var(--t-md); font-variant-numeric:tabular-nums}
  .ol-fila > .plat{font-size:var(--t-md); color:var(--mute)}
  .ol-fila > .v{font-size:var(--t-md); text-align:right;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  .ol-fila.total{border-bottom:0; background:var(--soft); padding:var(--e3) var(--e4)}
  .ol-fila.total > *{font-weight:600}
  /* Con varias puertas entra una columna más y la grilla la declara la fila: la
     plantilla le agrega la clase, no un `style=`. */
  .ol-fila.con-puerta{grid-template-columns:1.4fr 1.2fr 1fr auto minmax(150px, auto)}

  /* La cabecera de la hoja en PANTALLA: título a la izquierda, el botón de
     imprimir a la derecha. Es el mismo par que `.rf-cab` y `.oc-cab`, y hasta
     hoy era el único de los tres escrito en un `style=`. */
  .ol-cab{display:flex; align-items:flex-start; gap:var(--e5); flex-wrap:wrap}
  .ol-cab > .btn{flex:none}

  /* Lo que va DENTRO de un `.ol-bloque` sin ser una fila del cuadro --el aviso
     de que no hay nada, el detalle de gastos, la nota al pie-- se alinea con el
     padding lateral de las filas (`.ol-kv > *`). El mismo par de paddings
     estaba escrito a mano en las dos pantallas que lo usan, así que el pie de
     un bloque podía respirar distinto que el del bloque de al lado.
     `dl.ol-gastos` lleva el elemento en el selector para ganarle a `dl.kv`, que
     pesa lo mismo que una clase más el elemento. */
  .ol-dentro{padding:16px var(--e4)}
  .ol-nota{padding:0 var(--e4) 16px}
  /* El mismo lugar que `.ol-nota`, pero cuando lo que va ahí es un AVISO: el
     buzón que no se pudo leer. En terracota y con aire arriba, porque no cierra
     el bloque -- interrumpe. */
  .hint.ol-aviso{padding:var(--e2) var(--e4) 16px; color:var(--terra-text)}
  dl.ol-gastos{grid-template-columns:1fr auto; padding:16px var(--e4); margin:0}

  /* El pie de la pantalla: el historial y la salida, en un renglón. */
  .ol-pie{border-top:1px solid var(--line); padding-top:18px;
    display:flex; align-items:baseline; gap:var(--e4); flex-wrap:wrap}
  .ol-pie .rot{font-size:var(--t-md); color:var(--mute); font-weight:600}
  .ol-pie a{font-size:var(--t-md); text-decoration:none}

  /* ---------- el papel ----------
     `.solo-imprimir` es la contraparte de `.no-imprimir`, y no es decoración:
     resuelve el único caso del portal donde la ausencia de un dato no se
     anunciaba. El cuadro fiscal entra por htmx (su crédito de IVA sale del
     buzón del proveedor, que es red). Si el dueño imprime antes de que llegue,
     el hueco lleva adentro una frase que solo se ve en papel y dice que ese
     número no se alcanzó a leer. En cuanto htmx hace el swap, el hueco --y la
     frase-- desaparecen. */
  .solo-imprimir{display:none}

  @media print{
    /* Márgenes de la hoja. 18mm es el mínimo que ningún driver recorta y deja
       la caja de texto en ~14cm, que es un renglón cómodo a 12pt. */
    @page{margin:18mm 16mm}
    nav.top form, aside.lateral, .menu-btn, .no-imprimir{display:none !important}
    .shell{padding-top:0}
    /* Mismo motivo que el `@media print` de arriba (revisión de #417): sin
       estas dos líneas el papel de la liquidación sale con el fondo gris de
       --bg-app y la sombra de cualquier tarjeta que quede sin cubrir por las
       reglas de `.ol-bloque`/`.ol-neto` de abajo. */
    body{-webkit-print-color-adjust:exact; print-color-adjust:exact; background:#fff}
    .card, .cmp-lista, .pt-bloque, .hoy-cifras{box-shadow:none}
    .solo-imprimir{display:block}

    /* El papel no lleva tarjetas: lleva secciones con una regla arriba. Los
       bordes redondeados y los fondos de `--soft` gastan tinta y no separan
       nada que la regla no separe mejor. */
    .ol-bloque, .ol-neto{border:0; border-radius:0; background:none; padding:0}
    .ol-bloque-cab{padding:0 0 7px; border-bottom:1px solid var(--ink)}
    .ol-kv > *, .ol-fila{padding-left:0; padding-right:0}
    .ol-kv > .cierre, .ol-fila.total{background:none}
    .ol-fila.rotulos{background:none}

    /* El cuadre y el cuadro fiscal lado a lado: apilados no cerraban la
       primera hoja, y los dos juntos son la posición del mes de un vistazo. */
    .ol-papel-par{display:grid; grid-template-columns:1fr 1fr; gap:32px}

    /* Que una estadía no se corte por la mitad, y que el encabezado de la tabla
       se repita arriba de la continuación. Sin estas dos, un dueño de cuatro
       puertas recibe una segunda hoja con filas huérfanas y sin rótulos. */
    .ol-fila, .ol-bloque{break-inside:avoid}
    .ol-estadias .ol-fila{break-inside:avoid}
    .ol-estadias thead{display:table-header-group}
    /* El bloque del neto nunca arranca al final de una hoja. */
    .ol-neto{break-after:avoid}

    /* Tipografía de papel: el cuerpo sube al piso de impresión y los enlaces
       dejan de ser color -- en papel un link no se puede tocar. */
    body{font-size:12pt}
    a{color:var(--ink); text-decoration:none}
    /* La cabecera del documento: quién es el contribuyente a la izquierda,
       quién preparó la hoja y cuándo a la derecha, y una regla gruesa debajo.
       No lleva número de página: los margin boxes de `@page` no los implementa
       el navegador -- ver el README. */
    .ol-hoja-cab{
      display:flex; align-items:flex-start; gap:24px; justify-content:space-between;
      border-bottom:2px solid var(--ink); padding-bottom:12px; margin-bottom:18px;
      font-size:9.5pt; line-height:1.6;
    }
    .ol-hoja-cab strong{font-weight:600}
    .ol-hoja-preparada{text-align:right; color:var(--mute); flex:none}
    .ol-hoja-legal{font-size:8pt; color:var(--mute); line-height:1.6;
      border-top:1px solid var(--line); padding-top:var(--e2); margin-top:18px}
    /* El `h1` del mes: en papel el título va en la cabecera de arriba, pero el
       encabezado tiene que existir para el lector de pantalla. */
    h1.solo-imprimir{font-size:15pt; margin:0 0 12px}
  }

  /* ========== la ocupación del portafolio (handoff-ocupacion) ========== */

  /* ---------- ocupación de portafolio: las puertas, por dueño ----------
     La pantalla pasó de "siete puertas contadas tres veces" a la pregunta que
     el administrador trae: cómo le fue a cada DUEÑO este mes, y cuánto le queda
     a él. Tres decisiones:

       1. El agrupador es el dueño, no la propiedad. Es a quien se le liquida.
       2. Los cuatro números de resumen van arriba; el cuarto es la comisión del
          administrador, el único de la pantalla que es plata suya.
       3. Las dos series de doce meses bajan a un plegable: acompañan, no son lo
          que se lee primero.

     `<details>` y no JavaScript, igual que el selector de contribuyente del nav.

     La grilla de la fila de puerta se declara UNA vez y la usan la cabecera de
     columnas, cada puerta y el total: si las columnas no coinciden entre los
     tres, la pantalla deja de leerse como una tabla. */

  .oc-kpis{display:grid; grid-template-columns:1fr 1fr 1fr 1.15fr; gap:var(--e3)}
  .oc-kpi{border:1px solid var(--line); border-radius:var(--r-md); padding:var(--e4);
    display:flex; flex-direction:column; gap:var(--e1)}
  .oc-kpi .cifra{font-size:var(--cifra); font-weight:400; letter-spacing:-.025em;
    font-variant-numeric:tabular-nums}
  .oc-kpi .pie{font-size:var(--t-sm); color:var(--mute); line-height:1.5}
  .oc-kpi .pie a{font-weight:600; text-decoration:none}
  /* La comisión lleva tinte de marca y no de estado: no es buena ni mala, es de
     otro bolsillo que las otras tres. El tinte acompaña; la etiqueta lo dice. */
  .oc-kpi.mia{border-color:var(--terra); background:var(--brand-tint)}
  .oc-kpi.mia .rot{color:var(--terra-text)}

  /* La franja de puertas para mirar. Ámbar, arriba de los grupos, con el enlace
     a la acción -- el badge de cada fila queda como recordatorio, no como
     hallazgo. */
  .oc-aviso{border:1px solid var(--warn); border-radius:var(--r-md); padding:16px var(--e4);
    background:var(--warn-bg); display:flex; align-items:center; gap:var(--e3); flex-wrap:wrap}
  .oc-aviso .texto{font-size:var(--t-md); flex:1; min-width:280px}
  .oc-aviso a{font-size:var(--t-md); font-weight:600; text-decoration:none; padding:7px 0}

  .oc-grupos{display:flex; flex-direction:column; gap:var(--e3)}
  .oc-grupo{border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden}
  .oc-grupo-cab{display:flex; align-items:center; gap:12px; padding:var(--e3) var(--e4);
    background:var(--soft); border-bottom:1px solid var(--line)}
  .oc-grupo-id{flex:1; min-width:0}
  .oc-grupo-n{font-size:var(--t-lg); font-weight:600; letter-spacing:-.01em}
  .oc-grupo-sub{font-size:var(--t-sm); color:var(--mute); margin-top:2px}
  .oc-grupo-cifra{text-align:right}
  .oc-grupo-cifra.ancha{min-width:150px}
  .oc-grupo-cifra .rot{display:block; font-size:var(--t-xs); color:var(--mute);
    text-transform:uppercase; letter-spacing:.05em}
  .oc-grupo-cifra .val{display:block; font-size:var(--t-lg); font-weight:600;
    font-variant-numeric:tabular-nums}
  .oc-grupo-cab a{font-size:var(--t-md); font-weight:600; text-decoration:none; white-space:nowrap}

  .oc-fila{display:grid; grid-template-columns:1.5fr 2fr repeat(3, minmax(110px, auto));
    gap:var(--e3); align-items:center; padding:11px var(--e4); border-bottom:1px solid var(--line)}
  .oc-fila:last-child{border-bottom:0}
  .oc-fila:not(.rotulos):not(.total):hover{background:var(--soft)}
  .oc-fila.rotulos{padding:9px var(--e4)}
  .oc-puerta-n{display:flex; align-items:center; gap:8px; min-width:0}
  .oc-puerta-n a{font-size:var(--t-md); font-weight:600; text-decoration:none; color:var(--ink)}
  .oc-puerta-n a:hover{color:var(--terra-text)}
  .oc-num{font-size:var(--t-md); text-align:right; font-variant-numeric:tabular-nums;
    font-feature-settings:"tnum"}
  .oc-num.nada{color:var(--mute)}
  /* La barra de ocupación reemplaza al gráfico de comparación por puerta: dice
     lo mismo en el renglón donde ya están el ADR y el RevPAR de esa puerta. */
  .oc-barra{display:flex; align-items:center; gap:var(--e2)}
  .oc-barra .pista{flex:1; height:9px; background:var(--line); border-radius:var(--r-xs); overflow:hidden}
  .oc-barra .relleno{display:block; height:9px; background:var(--terra); border-radius:var(--r-xs)}
  .oc-barra .relleno.baja{background:var(--warn)}
  .oc-barra .cifra{font-size:var(--t-sm); color:var(--mute); font-variant-numeric:tabular-nums; width:78px}
  /* El total del portafolio: fila de cierre suelta, no la última fila de un
     grupo. Comparte la grilla para que las columnas caigan donde vienen cayendo. */
  .oc-fila.total{border:1px solid var(--line); border-radius:var(--r-md); background:var(--soft);
    padding:var(--e3) var(--e4)}
  .oc-fila.total .oc-num{font-weight:600}
  .oc-fila.total .oc-barra .relleno{background:var(--dark)}
  .oc-total-n{font-size:var(--t-lg); font-weight:600; letter-spacing:-.01em}

  /* `.oc-cab`, `.oc-cab-mes` y su rótulo se declaran con los `.rf-cab*` del
     resumen fiscal: es la misma cabecera de mes en las dos pantallas. */
  /* Las flechas de mes toman la silueta de `.cal-nav a` -- radio 8px y no
     píldora -- para que las dos maneras de cambiar de mes en la app se vean
     iguales. Clase propia y NO `.paso`: en base.html `.paso` ya es la tarjeta
     de "Primeros pasos" y heredaría su radio de 14px y su `margin-bottom`. */
  .oc-mes-paso{display:inline-flex; align-items:center; justify-content:center;
    min-height:36px; padding:8px var(--e3); border:1px solid var(--line-input); border-radius:var(--r-sm);
    text-decoration:none; color:var(--ink); background:var(--bg); font-weight:600; font-size:var(--t-md)}
  .oc-mes-paso:hover{border-color:var(--accent); color:var(--accent)}
  /* `.oc-export` va con `.rf-cab-mes .btn-export`, arriba: mismo botón. */

  /* Las doce columnas plegadas (`.oc-contexto`) se declaran arriba, al lado de
     `details.seccion`: son el mismo gesto en dos pantallas. */

  @media (max-width:900px){
    .oc-kpis{grid-template-columns:1fr 1fr}
    /* La cabecera del dueño: las dos cifras bajan debajo del nombre antes de
       apretarse contra el enlace de liquidación. */
    .oc-grupo-cab{flex-wrap:wrap}
    .oc-grupo-cifra{text-align:left}
  }
  @media (max-width:640px){
    .oc-kpis{grid-template-columns:1fr}
    /* La fila de puerta a 375px: el nombre y la barra ocupan el ancho, y los
       tres números se reparten un renglón propio. Los rótulos de columna dejan
       de servir a un renglón, así que se van. */
    .oc-fila{grid-template-columns:repeat(3, 1fr); row-gap:8px; padding:var(--e3) var(--e4)}
    .oc-fila.rotulos{display:none}
    .oc-puerta-n, .oc-total-n{grid-column:1 / -1}
    .oc-barra{grid-column:1 / -1}
    .oc-num{text-align:left}
    .oc-num:not(:first-of-type){text-align:center}
    .oc-num:last-of-type{text-align:right}
    .oc-cab-mes{width:100%}
    .oc-export{margin-left:0}
  }

  /* ========== la portada (handoff-portada) ========== */

  /* ---------- portada: bloques con peso, y un pie con lo que no pide nada ----
     Punto 2 de la revisión del 28-ago, tercera pantalla. Y donde se ve junto el
     trabajo de los puntos 1 y 2.

     El diagnóstico de la revisión era "cinco tablas apiladas". Leyendo la
     pantalla entera, las tablas son el síntoma: lo que pasa es que un bloque
     que dice "Ninguno" ocupa exactamente lo mismo que uno con doce documentos
     trabados, y la misma identidad (propietario, puerta) se declara en cinco
     `<thead>` distintos.

     Así que: los bloques que tienen algo adentro son bloques; los que no,
     bajan todos juntos a un renglón al pie. La pantalla sigue diciendo QUÉ
     miró -- su propia regla, escrita en el comentario del titular -- pero en
     una línea en vez de en cuatro cards.

     ⚠ DEPENDE DE `handoff-comprobantes`. Las filas de acá son literalmente
     `.cmp-fila`, `.cmp-linea`, `.cmp-id`, `.cmp-ver` y `.cmp-motivos`: no hay
     una sola regla nueva para la fila. Si ese CSS no está puesto, esta pantalla
     sale sin estilos de fila. Es a propósito -- que el patrón sea UNO es la
     mitad del valor. */

  /* El bloque. Reemplaza a `.card` como recipiente de una lista: la cabecera va
     en `--soft` y las filas en blanco, así que el título y el conteo se leen
     como cabecera y no como la primera fila. */
  .pt-bloque{background:var(--bg); border:0; border-radius:var(--r-lg); box-shadow:var(--sh-float); overflow:hidden}
  /* Sin fondo gris (cáscara moderna 21-sep-2026): la cabecera se separa de las
     filas con el hairline de siempre, no con `--soft` -- el bloque entero ya
     flota sobre `--bg-app` con su propia sombra. */
  .pt-bloque-cab{padding:var(--e3) var(--e4); border-bottom:1px solid var(--line)}
  .pt-bloque-cab > strong{font-weight:600; font-size:var(--t-lg); letter-spacing:-.01em}
  .pt-bloque-cab .hint{margin-top:4px; font-size:var(--t-sm); line-height:1.55}
  /* El tinte va en la CABECERA y no en el bloque entero: si tiñe el bloque, las
     filas pierden el fondo blanco y la lista deja de leerse. La regla de 3px es
     la misma de `.card.bad`, en el mismo lugar. */
  .pt-bloque-cab.bad{background:var(--err-bg); border-left:3px solid var(--err)}
  .pt-bloque-cab.warn{background:var(--warn-bg); border-left:3px solid var(--warn)}
  /* El pie del bloque: el enlace que sale de la lista ("Ver el resumen fiscal
     completo"). Cierra el bloque como la cabecera lo abre. */
  .pt-bloque-pie{padding:12px var(--e4); border-top:1px solid var(--line);
    background:var(--soft)}
  .pt-bloque-pie a{font-size:var(--t-md); font-weight:600; text-decoration:none}

  /* Cuánto lleva esperando. Es la única cifra de estas filas y la que ordena la
     lista, así que va en peso 600 y con cifras tabulares -- doce y cuatro se
     comparan a ojo cuando los dígitos ocupan lo mismo. */
  .pt-espera{font-size:var(--t-md); font-weight:600; white-space:nowrap;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}

  /* El IVA por propietario. Acá SÍ hay un número grande (hallazgo 3): el débito
     a 19px y las ventas del mes a 12,5px al lado, que son la magnitud que lo
     explica y no un dato del mismo peso. */
  .pt-ventas{font-size:var(--t-sm); color:var(--mute); white-space:nowrap;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}
  .pt-debito{font-size:var(--t-h2); font-weight:600; letter-spacing:-.015em; white-space:nowrap;
    font-variant-numeric:tabular-nums; font-feature-settings:"tnum"}

  /* El pie: todo lo que se miró y no pide nada. Sin recipiente --el rótulo va
     suelto arriba-- porque un borde alrededor de "no hay nada" es exactamente
     el problema que esto resuelve. */
  .pt-mirado{border-top:1px solid var(--line); margin-top:var(--e4); padding-top:var(--e3);
    display:flex; flex-direction:column; gap:5px}
  .pt-mirado > .ayuda{margin-bottom:var(--e2)}
  /* Adentro de "Revisado hoy" (jerarquía §1c, 24-sep-2026) el `details` de
     la sección ya trae su propia regla arriba y su aire: la hairline del
     `.pt-mirado` justo debajo del rótulo quedaba doblada con la de
     `details.seccion`. */
  .pt-revisado-hoy .pt-mirado{border-top:0; margin-top:0; padding-top:0;
    margin-bottom:var(--e3)}

  /* Y adentro, una LISTA y no un párrafo: cinco rótulos en negrita unidos con
     "·" se leían como una pared de texto de cuatro renglones. En renglones el
     ojo baja por la columna de la derecha --que es la que contesta-- y termina
     en dos segundos.

     A todo el ancho y con una hairline por renglón, igual que las dos salidas
     del pie: es el mismo gesto de índice, y acotarle el ancho a la lista dejaba
     las reglas cortadas a media pantalla arriba de las que sí la cruzan. */
  .pt-revisado{list-style:none}
  .pt-revisado > li{display:flex; align-items:baseline; justify-content:space-between;
    gap:var(--e3); padding:5px 0; font-size:var(--t-sm); line-height:1.5}
  .pt-revisado > li + li{border-top:1px solid var(--line)}
  .pt-revisado .q{font-weight:600}
  .pt-revisado .e{color:var(--mute); text-align:right}
  .pt-revisado .e a{font-weight:600; white-space:nowrap}

  /* Los dos bloques del día cuando no hay ni una llegada ni una salida: un
     renglón tenue. Dos tarjetas que dicen "Ninguna." ocupan lo mismo que dos
     con seis puertas -- el mismo defecto que la lista de arriba arregla. */
  .pt-dia-vacio{font-size:var(--t-sm); color:var(--mute); margin:var(--e3) 0 var(--e4)}
  .pt-dia-vacio .badge{margin-left:6px; vertical-align:middle}

  /* Las dos salidas del pie de la pantalla, como filas de ÍNDICE y no como
     cards: sin fondo, sin borde, regla arriba y la flecha al borde derecho. Dos
     renglones y no uno, porque cada una lleva la explicación de qué hay del
     otro lado -- que es justamente el hallazgo que las puso ahí. */
  .pt-salida{display:block}
  .pt-salida > a{display:flex; align-items:flex-start; justify-content:space-between;
    gap:var(--e3); padding:13px 0; border-top:1px solid var(--line);
    text-decoration:none; color:var(--ink)}
  .pt-salida > a:hover{color:var(--ink)}
  .pt-salida .n{font-size:var(--t-md); font-weight:600}
  .pt-salida .hint{display:block; margin-top:3px; font-weight:400; font-size:var(--t-sm);
    line-height:1.55}
  .pt-salida .flecha{font-size:var(--t-md); line-height:1.5}
  /* La ayuda de una salida cuelga de SU fila, y la fila de abajo abre con una
     hairline: sin el aire de abajo el plegable se lee como encabezado de la
     siguiente y no como nota de la anterior. */
  .pt-salida .ayuda{margin:var(--e1) 0 var(--e3)}

  /* ---------- la ayuda plegable ----------
     El texto que explica POR QUÉ, escondido detrás de una pregunta corta. Nace
     en la portada --era el párrafo del pie y las dos oraciones de las salidas--
     pero no es de la portada: es el gesto para todo texto que se lee UNA vez y
     después estorba. Cualquier pantalla puede usarlo tal cual.

     Sin caja, a propósito: un borde alrededor de la explicación la convierte en
     otro bloque de la pantalla, que es justo lo que esto viene a quitar.
     `details.seccion` es el mismo gesto CON regla arriba, para cuando lo plegado
     es una sección entera y no un renglón. */
  .ayuda{margin-top:var(--e2)}
  .ayuda > summary{display:inline-flex; align-items:center; gap:6px; width:max-content;
    cursor:pointer; list-style:none; color:var(--mute); font-size:var(--t-sm);
    line-height:1.5; border-radius:var(--r-xs)}
  .ayuda > summary::-webkit-details-marker{display:none}
  /* El chevron gira en vez de cambiar de glifo: el mismo signo dice cerrado y
     abierto, y el giro es lo que se lee como "esto se despliega". */
  .ayuda > summary::before{content:"\203A"; font-size:var(--t-lg); line-height:1;
    color:var(--mute); transition:transform .12s ease}
  .ayuda[open] > summary::before{transform:rotate(90deg)}
  .ayuda > summary:hover{color:var(--ink)}
  .ayuda > summary:focus-visible{outline:2px solid var(--terra-text); outline-offset:3px}
  .ayuda-cuerpo{margin-top:7px; font-size:var(--t-sm); color:var(--mute); line-height:1.6}
  .ayuda-cuerpo > p + p{margin-top:5px}
  .ayuda-cuerpo b{font-weight:600; color:var(--ink)}

  @media (max-width:640px){
    /* La fila ya se reordena sola: es `.cmp-fila`, que a 640px baja el enlace a
       su renglón con piso táctil. Acá solo el IVA, que tiene tres piezas. */
    .pt-iva-fila .cmp-linea{flex-wrap:wrap; row-gap:var(--e1)}
    .pt-iva-fila .pt-ventas{order:1; flex:0 0 100%}
  }

  /* ========== ocupación por contribuyente ==========

     La pantalla entera reusa las clases `oc-*` de la cartera; lo único suyo es
     el pie de cada panel de doce meses. El rótulo técnico ("tope de la escala",
     "escala fija de 0 a 100") estaba ARRIBA del gráfico, que es donde va un
     título -- y no lo era: es la letra chica que se lee después de mirar las
     columnas. Debajo y en `.hint`, con aire para el panel que sigue. */
  .oc-pie-grafico{margin:var(--e1) 0 var(--e4)}
  /* Y el rótulo de arriba de cada panel: mismo gesto que `.gr-rot`, con el aire
     que este bloque pide. El segundo panel además se despega del primero. */
  .hint.oc-rot{margin:0 0 8px}
  .hint.oc-rot.segundo{margin-top:16px}
  /* Sin la tarjeta de comisión son TRES, y la grilla de cuatro les dejaba un
     hueco del ancho de una tarjeta a la derecha. El parcial ya decía que "se
     reparten el ancho"; esto es lo que faltaba para que fuera cierto. Vale
     también para la cartera, donde la comisión puede no venir. */
  .oc-kpis.tres{grid-template-columns:repeat(3, 1fr)}
  @media (max-width:900px){ .oc-kpis.tres{grid-template-columns:1fr 1fr} }
  @media (max-width:640px){
    .oc-kpis.tres{grid-template-columns:1fr}
    /* A 390px las dos flechas, el mes y el export no entran en un renglón: el
       export baja solo. `.oc-cab-mes` ya toma el ancho completo ahí arriba, lo
       que le faltaba era permiso para envolver. */
    .oc-cab-mes{flex-wrap:wrap}
  }

  /* ---------- propiedades ----------
     La pantalla de las puertas de un contribuyente: una tabla corta --casi
     siempre una o dos filas-- y, debajo, el alta manual. Abierto de par en par
     el formulario ocupaba dos tercios de la pantalla y la lista se leía como un
     encabezado suyo, al revés de lo que la persona vino a mirar. */
  /* Jerarquía §2 (docs/70, 24-sep-2026): con propiedades ya dadas de alta, el
     botón del alta sube al encabezado, arriba a la derecha del título -- antes
     vivía debajo de la tabla, un tercer camino además del enlace del estado
     vacío y del formulario ya abierto. `space-between` deja el `h1` a la
     izquierda y el botón (con el formulario que cuelga de él) a la derecha;
     `flex-wrap` es lo que lo baja a su propio renglón cuando se abre, porque
     el formulario no entra al lado del título. */
  .prop-cab{display:flex; flex-wrap:wrap; align-items:baseline;
    justify-content:space-between; column-gap:var(--e4)}
  .prop-cab .prop-alta{margin-left:auto}
  /* El alta plegada. `<details>` y no JavaScript, igual que el selector de
     contribuyente del nav; el `open` lo decide el servidor (sin puertas, o con
     un error de validación). El `summary` toma `.btn .btn-sec` del sistema --es
     el mismo botón secundario del resto de la app, no uno nuevo-- y lo único
     que agrega esta regla es sacarle el triangulito nativo: el `display:
     inline-block` de `.btn` ya se lo saca a los navegadores que lo cuelgan de
     `list-item`, y `list-style` cubre al resto. */
  details.alta > summary{list-style:none}
  details.alta > summary::-webkit-details-marker{display:none}
  /* Abierto, el botón queda marcado: mismo criterio que los `details` del nav. */
  details.alta[open] > summary{border-color:var(--terra)}
  details.alta > summary:focus-visible{outline:2px solid var(--terra-text); outline-offset:2px}
  /* La card del formulario CUELGA del botón: un escalón de aire y no los
     `--e3` con los que una card se separa de lo que no tiene que ver con ella. */
  details.alta[open] > .card{margin-top:var(--e2)}
  /* El botón y el tope del plan, en un renglón (⚖ fundador 11-sep-2026). El
     número vivía adentro del formulario, así que plegado --que es como nace--
     no se veía: "¿me queda alguna puerta?" se pregunta ANTES de abrir el alta.

     El renglón envuelve solo: cerrado, el `details` mide lo que mide el botón y
     el tope entra a su derecha; abierto mide lo que mide el formulario, se lleva
     la línea entera y el tope baja -- que es lo que hay que hacer, porque
     compartir el renglón con una grilla de dos columnas le comería el ancho.
     `baseline` y no `center`: son dos cuerpos de letra distintos que se leen
     como un renglón, el mismo criterio que `.liq-cuenta`. */
  .prop-alta{display:flex; flex-wrap:wrap; align-items:baseline; gap:0 var(--e3)}
  .prop-tope{margin-top:var(--e3)}
  /* Los rótulos de la tabla no se parten en dos renglones: "TIPO DE /
     ALOJAMIENTO" y "ANUNCIOS / CONECTADOS" armaban una cabecera de dos pisos
     sobre filas de un piso. El ancho de más se paga adentro de `.tabla-scroll`,
     que es donde esta tabla ya scrollea a 375px. */
  .tabla-prop thead th{white-space:nowrap}
  /* Y el botón de la celda tampoco: la etiqueta del link de la puerta partida
     en dos renglones adentro de una píldora se lee como un párrafo con borde.
     Mismo trato que `.estado-canal`, por el mismo motivo. (La etiqueta NO se
     escribe acá: el test del idioma la busca en la página y el CSS del shell
     viaja en todas, así que la frase literal daría verde por el motivo
     equivocado.) */
  .tabla-prop td button{white-space:nowrap}

  /* ---------- las utilitarias, y por qué son las últimas ---------- */
  /* Reemplazan a un `style=`, y un `style=` le ganaba a TODO. Por eso van al
     final del archivo y con la clase escrita dos veces: `.paso .hint` y
     `dl.kv` pesan dos escalones de especificidad, así que contra una clase
     sola le ganarían, y el renglón que venía respirando 10px pasaría a
     respirar 7 sin que nadie lo pida. Duplicar el nombre iguala el peso y el
     orden del archivo desempata.

     Las primeras cuatro son los escalones de la escala: `margin-top` suelto en
     un `style=` aparecía 37 veces con un valor de `--e1..--e5`. */
  .mt-1.mt-1{margin-top:var(--e1)}
  .mt-2.mt-2{margin-top:var(--e2)}
  .mt-3.mt-3{margin-top:var(--e3)}
  .mt-4.mt-4{margin-top:var(--e4)}
  /* La única que separa arriba Y abajo: un renglón de la lista que respira
     igual de los dos lados (el formulario de cada puerta sin anuncio). */
  .mv-1.mv-1{margin:var(--e1) 0}
  /* Y las otras dos, que NO son de la escala: 8 y 12px estaban escritos a mano
     21 veces en diez plantillas, o sea que abajo de `--e1..--e5` venía
     corriendo una segunda escala de 4px que nadie declaró. Nombrarlas no la
     bendice: la hace visible, que es el paso previo a decidir si se unifica.
     Llevarlas a `--e2`/`--e3` mueve dos píxeles en veintiún lugares, así que
     esa es una decisión del fundador y no de una consolidación.
       `.aireado` -- la ayuda o el desplegable que necesita un poco más de aire
                    que los 5px con los que nace un `.hint`.
       `.separado` -- el bloque separado del anterior: el grupo de un campo
                    (rótulo + control + ayuda), el párrafo que abre un modo de
                    un paso, el desplegable que cuelga de un formulario. */
  .aireado.aireado{margin-top:8px}
  .separado.separado{margin-top:12px}
  /* El botón que CIERRA un formulario largo: se despega del último campo un
     escalón más que el botón suelto (que trae `--e3`). Eran cuatro `style=`
     idénticos, uno por formulario público. */
  .btn-enviar.btn-enviar{margin-top:16px}
  /* Y la que anula el `margin-top` que trae otra regla: la previa de la
     plantilla, que va pegada a su `<textarea>`. */
  .mt-0.mt-0{margin-top:0}
  /* Los cuatro tintes de TEXTO, para lo que no es una píldora: el estado de un
     canal, el motivo de un error, el dato apagado al lado de un nombre. El
     color nunca va solo --el glifo y la palabra están siempre-- pero el tinte
     estaba escrito trece veces en un `style=`, una por cada rama del condicional
     que lo elige, y así el verde de un estado podía dejar de ser el del token. */
  .tinte-ok.tinte-ok{color:var(--ok)}
  .tinte-warn.tinte-warn{color:var(--warn)}
  .tinte-err.tinte-err{color:var(--err)}
  .tinte-mute.tinte-mute{color:var(--mute)}

  /* ---------- topbar, filas y pills (21-sep) ---------- */
  /* ⚖ Fundador 21-sep-2026, comparando la app con Guesty: tres retoques de
     forma y ninguno de contenido. El avatar con menú que reemplaza al botón de
     salir flotando, la fila de tabla que se toca entera, y una sola píldora de
     estado para toda la app. */

  /* EL AVATAR de la barra de arriba. El desplegable es el MISMO `.pick-menu`
     del selector de contribuyente y el menú del pie --misma caja, mismas filas,
     mismo hover-- pero esas reglas están escritas con el prefijo de cada uno
     (`.emisor-pick .pick-menu`, `.menu-cuenta .pick-menu`), así que la tercera
     familia repite las declaraciones acá en vez de meterse en aquel selector:
     `test_candados_plan.py` lee la hoja buscando selectores literales, y ese
     archivo lo están tocando otras manos esta misma semana. Es copia
     deliberada, y el día que haya una cuarta conviene unificar las tres. */
  .menu-usuario{flex:none}
  /* Empuja contra el borde derecho de la barra (⚖ 24-sep-2026, revisión UX: la
     misma cadena que `.menu-campana`, más abajo -- ver el comentario de
     `.busqueda-global`). Se cancela cuando algo a la izquierda ya hace ese
     trabajo: la campana (siempre que exista) o el buscador. */
  .menu-usuario{margin-left:auto}
  .menu-campana ~ .menu-usuario, .busqueda-global ~ .menu-usuario{margin-left:0}
  .menu-usuario details{position:relative}
  /* El `summary` es el disco y nada más: sin la caja ni el chevrón que llevan
     los otros dos desplegables, que son controles de ancho completo adentro de
     una columna. Acá el control ES el avatar.

     ⚠ Las etiquetas se nombran SIN sus angulitos en todo este bloque, y no es
     capricho: varios tests leen la pantalla con expresiones regulares que
     buscan `summary` o `details` literales, y un comentario de CSS que los
     escriba entero les hace empezar a leer acá y traerse media hoja de estilo
     como si fuera el rótulo del selector de contribuyente. Pasó el 21-sep-2026,
     con 29 tests en rojo por una palabra adentro de un comentario. */
  .menu-usuario summary{
    list-style:none; cursor:pointer; display:block;
    border-radius:var(--r-pill); transition:border-color .15s ease;
  }
  .menu-usuario summary::-webkit-details-marker{display:none}
  /* 32px y las iniciales centradas. `--soft` de fondo con hairline --la misma
     factura del chip del dólar que tiene al lado-- y la tinta del cuerpo
     encima: `--ink` sobre `--soft` da 18.12:1, o sea AA de sobra a 11px.
     `user-select:none` porque un disco de dos letras no es texto que nadie
     quiera copiar, y seleccionarlo al abrir el menú se ve como un error. */
  /* 38px y con fondo de acento (⚖ fundador 21-sep, PR 2): mismo alto que la
     campana de al lado y que el aro `rgba(255,255,255,.22)` de los dos --
     hasta el PR 2 el avatar era 32px sobre `--soft`, que era la superficie de
     una barra clara que ya no existe. `--on-dark-accent` (el ámbar de marca)
     con `--dark` encima da 7.29:1, arriba de AA. */
  .menu-usuario .avatar{
    display:flex; align-items:center; justify-content:center;
    width:38px; height:38px; border-radius:var(--r-pill);
    background:var(--on-dark-accent); border:1px solid rgba(255,255,255,.22); color:var(--dark);
    font-size:var(--t-xs); font-weight:700; letter-spacing:.02em;
    user-select:none;
  }
  .menu-usuario summary:hover .avatar,
  .menu-usuario details[open] .avatar{border-color:rgba(255,255,255,.35)}
  /* La caja del desplegable, igual que la de los otros dos. Lo único propio es
     de dónde cuelga: hacia ABAJO y pegado al borde DERECHO, porque el avatar
     vive contra el margen de la pantalla y un menú alineado a la izquierda del
     disco se saldría. Y ancho por contenido con un piso, no `left:0;right:0`:
     aquéllos ocupan el ancho de su columna, éste cuelga de un disco de 32px. */
  .menu-usuario .pick-menu{
    position:absolute; z-index:5; top:calc(100% + 6px); right:0; min-width:208px;
    background:var(--bg); border:1px solid var(--line); border-radius:var(--r-md);
    padding:var(--e1); box-shadow:0 10px 26px rgba(27,38,36,.07);
  }
  .menu-usuario .pick-menu a{
    display:block; padding:8px 12px; border-radius:var(--r-sm); text-decoration:none;
    color:var(--ink); font-size:var(--t-md); white-space:nowrap;
  }
  .menu-usuario .pick-menu a:hover{background:var(--soft)}
  /* Quién sos, arriba de todo y separado de las opciones por la hairline: es
     el sujeto del menú, no una fila que se toca. El correo va en `--mute` y un
     punto más chico -- el nombre es lo que se lee, el correo lo que desempata
     cuando alguien tiene dos sesiones. `break-all` porque un correo largo no
     puede ensanchar el menú hasta salirse de la pantalla. */
  .menu-usuario .quien{
    display:block; padding:var(--e1) 12px var(--e2);
    border-bottom:1px solid var(--line); margin-bottom:var(--e1);
  }
  .menu-usuario .quien .n{display:block; font-size:var(--t-md); font-weight:600}
  .menu-usuario .quien .correo{
    display:block; font-size:var(--t-sm); color:var(--mute); word-break:break-all;
  }
  /* La fila de salir es un `button` adentro de un form POST y las otras tres
     son enlaces: se ven igual porque son la misma clase de cosa para quien
     mira.
     Lo que se deshace acá es el vestido de `.btn-sec .btn-chico`, que el macro
     trae de cuando el botón flotaba suelto en la barra. */
  .menu-usuario .pick-menu form{margin:0}
  .menu-usuario .pick-menu button{
    display:block; width:100%; text-align:left; min-height:0;
    padding:8px 12px; border:0; border-radius:var(--r-sm); background:none;
    color:var(--ink); font-family:inherit; font-size:var(--t-md); font-weight:400;
    cursor:pointer;
  }
  /* Sin el `translateY` ni el `brightness` del botón global: una fila de menú
     que salta un píxel al pasar el mouse se lee como un tembleque. Mismo hover
     que los `<a>` de al lado, que es lo que la hace una fila más. */
  .menu-usuario .pick-menu button:hover{
    background:var(--soft); color:var(--ink);
  }
  /* En pantalla angosta el chip del dólar ya se fue (640px) y la barra queda
     con las migas y el avatar. El menú se ancla al borde derecho igual, pero no
     puede ser más ancho que la pantalla menos el aire del `.wrap`. */
  @media (max-width:640px){
    .menu-usuario .pick-menu{min-width:0; width:max-content; max-width:calc(100vw - 80px)}
  }

  /* LA CAMPANA (B1), a la izquierda del avatar. Copia deliberada de
     `.menu-usuario` de arriba -- mismo `.pick-menu`, mismo motivo (los tests
     que leen la hoja por selector literal) -- con dos diferencias: el control
     es un botón de ícono y no un disco de iniciales, y lleva un badge. */
  .menu-campana{flex:none}
  /* Empuja contra el borde derecho de la barra (⚖ 24-sep-2026, revisión UX:
     ver el comentario de `.busqueda-global`, arriba). Se cancela cuando el
     buscador ya está haciendo ese trabajo. */
  .menu-campana{margin-left:auto}
  .busqueda-global ~ .menu-campana{margin-left:0}
  .menu-campana details{position:relative}
  /* 38px, aro translúcido: mismo alto que el avatar de al lado (⚖ fundador
     21-sep, PR 2). */
  .menu-campana summary{
    list-style:none; cursor:pointer; display:flex; align-items:center; justify-content:center;
    position:relative; width:38px; height:38px; border-radius:var(--r-pill);
    background:transparent; border:1px solid rgba(255,255,255,.22); color:var(--on-dark);
    transition:border-color .15s ease;
  }
  .menu-campana summary::-webkit-details-marker{display:none}
  .menu-campana summary:hover,
  .menu-campana details[open] summary{border-color:rgba(255,255,255,.5)}
  /* El contador de la campana NO es un `.badge` -- es un número, no una
     píldora de estado con palabra, y `.badge-warn` le traía dos cosas que no
     le sirven: el `::before` de 6px (maqueta A, pensado para "el punto es lo
     que distingue el estado de reojo", no para un número que ya se lee solo)
     y un tinte (`--warn-bg`/`--warn`) pensado para fondo CLARO -- sobre
     `.barra-sup` (`--dark`) quedaba desteñido. Bug visto por el fundador en
     staging, 21-sep-2026: se veía el punto Y el número, desalineados,
     saliéndose del botón de 38px.
     Clase propia, círculo sólido en la esquina -- `--on-dark-accent` (coral,
     7.09:1 sobre `--dark`, docs/68) de fondo y `--dark` de texto: mismo par,
     así que el contraste es el mismo 7.09:1 léase del lado que se lea.
     `min-width` fija el círculo a 1 dígito; con 2 o "99+" el padding lo abre
     a píldora solo. */
  .menu-campana .contador-campana{
    position:absolute; top:-4px; right:-4px; min-width:16px; height:16px;
    padding:0 4px; line-height:16px; font-size:10px; font-weight:700;
    text-align:center; border-radius:var(--r-pill);
    background:var(--on-dark-accent); color:var(--dark);
  }
  .menu-campana .pick-menu{
    position:absolute; z-index:5; top:calc(100% + 6px); right:0; min-width:240px;
    background:var(--bg); border:1px solid var(--line); border-radius:var(--r-md);
    padding:var(--e1); box-shadow:0 10px 26px rgba(27,38,36,.07);
  }
  .menu-campana .pick-menu a{
    display:block; padding:8px 12px; border-radius:var(--r-sm); text-decoration:none;
    color:var(--ink); font-size:var(--t-md); white-space:nowrap;
  }
  .menu-campana .pick-menu a:hover{background:var(--soft)}
  .menu-campana .campana-cargando,
  .menu-campana .campana-vacio{
    display:block; padding:8px 12px; color:var(--mute); font-size:var(--t-md);
  }
  @media (max-width:640px){
    .menu-campana .pick-menu{min-width:0; width:max-content; max-width:calc(100vw - 80px)}
  }

  /* LA FILA QUE SE TOCA ENTERA (⚖ fundador 21-sep-2026). El destino lo lleva la
     fila en `data-href` y lo dispara `static/nav.js` -- el enlace de la derecha
     NO se va, y ésa es la mitad que funciona sin JavaScript: el click en él
     hace lo que dice, lo entiende el teclado y se puede abrir en otra pestaña.
     Lo que la fila agrega es el área que el mouse ya intentaba tocar.

     Se resuelve así y no con un `<a>` estirado por `::after` porque de las tres
     listas dos son `<div>` y una es `<tr>`, y un contenedor de posicionamiento
     sobre una fila de tabla es justo lo que los navegadores resolvieron tarde y
     distinto. Una regla que se ve igual en las tres vale las nueve líneas de JS.

     `.cmp-fila` ya traía su `:hover`; acá está el de la tabla y, para las tres,
     el cursor y el foco. `:focus-within` y no un `tabindex` en la fila: el
     enlace de adentro ya es la parada del teclado, y agregar otra sería
     tabular dos veces por la misma cosa. */
  .fila-enlace{cursor:pointer}
  tr.fila-enlace:hover > td{background:var(--crema)}
  .fila-enlace:focus-within{background:var(--crema)}
  tr.fila-enlace:focus-within > td{background:var(--crema)}
  /* El chevrón de la fila clicable (maqueta: columna final de 28px). Es del
     `td` de más a la derecha, a menos que ese `td` ya traiga su propio
     `.fila-ver` -- ahí el chevrón ya está y no se pinta dos veces. */
  tr.fila-enlace > td:last-child:not(:has(.fila-ver)){position:relative; padding-right:36px}
  tr.fila-enlace > td:last-child:not(:has(.fila-ver))::after{
    content:"\203A"; position:absolute; right:var(--e3); top:50%; transform:translateY(-50%);
    color:var(--mute); font-size:1.3em; line-height:1}
  tr.fila-enlace:hover > td:last-child:not(:has(.fila-ver))::after{color:var(--ink)}

  /* UNA SOLA PÍLDORA DE ESTADO (⚖ fundador 21-sep-2026). La forma ya la daba
     `.badge` --11px, 600, radio píldora, `4px var(--e2)`-- y lo que faltaba era
     que la usaran TODAS: el estado de un canal era texto suelto con su glifo,
     o sea que la misma clase de cosa se decía con dos formas distintas.
     Ahora `.estado-canal` es la misma píldora (ver `canales_ota.html` y
     `calendario_recargos.html`) y lo único que cambia entre familias es el
     tinte, que ya salía de los mismos cuatro tokens.

     Y la píldora gana un PUNTO del color semántico adelante. No es adorno: a
     11px, un tinte de fondo al 10% se lee casi igual en verde que en ámbar, y
     el punto es lo que deja distinguir el estado de reojo en una lista de
     treinta filas. `currentColor` y no un color escrito: el punto es el MISMO
     tinte que la palabra, así que ninguna variante puede quedarse con el punto
     de otra.

     El `.badge` PELADO no lo lleva: es el rótulo neutro de marca (el régimen
     del emisor, el contribuyente dado de baja, la ruta de una invitación) y no
     dice bien ni mal --
     un punto de estado ahí afirmaría algo que ese rótulo no afirma. Tampoco
     `.badge-espera`, que es de la misma clase: el paso del onboarding que
     todavía no arrancó. */
  .badge-ok::before, .badge-warn::before, .badge-err::before, .badge-neutro::before, .pill::before,
  .estado-hecho::before, .estado-hecha::before, .estado-pendiente::before,
  .estado-rechazado::before, .estado-en_curso::before, .estado-esperando::before,
  .estado-bloqueado::before{
    content:""; display:inline-block; width:6px; height:6px;
    border-radius:var(--r-pill); background:currentColor;
    margin-right:var(--e1); vertical-align:middle;
  }
  /* LA EXCEPCIÓN, y es una sola: la píldora que ya trae su propio glifo
     (●▲○✕■ / ◐✓ -- el estado de un canal, el del recargo, el de las casillas
     del emisor). Ahí el punto sería el segundo signo no-verbal de la misma
     píldora, y el glifo es el que ya cumple la regla de que el color nunca va
     solo: distingue ○ de ● sin ver el tinte, que es más de lo que hace un
     punto. La forma se unifica igual -- es lo que este cambio vino a hacer. */
  .estado-canal::before{content:none}
  /* ---------- tile de propiedad y selector de emisor (21-sep) ----------
     ⚖ Fundador 21-sep-2026, comparando la app con Guesty: *"lo que más la hace
     viva son las tarjetas de propiedad con imagen y los logos de los canales"*,
     y nuestras propiedades son una tabla de texto.

     EL TILE. Foto de la casa todavía no hay --no existe dónde subirla--, así
     que el lugar de la foto lo ocupa un cuadrado con la inicial sobre un tinte
     derivado del nombre (`app.deps.tile_propiedad`, macro `_tile_propiedad.html`).
     Los SEIS tintes son una paleta CERRADA de tokens: ninguno acuña un color
     nuevo, todos salen de `--terra`/`--coral`/`--brown`/`--dark`/`--ok`/`--warn`
     con `color-mix` (la misma técnica que la card del plan, ~línea 1004).
     La TINTA no siempre es el token del fondo, y no es un descuido: `--coral`
     y `--terra` como texto no llegan al 4,5:1 de WCAG AA sobre claro, así que
     los dos usan `--terra-text`, que es el mismo naranja quemado un punto más
     oscuro (el criterio que ya sigue `.badge`). Aun así la inicial va
     `aria-hidden`: el nombre está escrito al lado, el cuadrado no informa. */
  .prop-tile{display:inline-flex; align-items:center; justify-content:center;
    width:40px; height:40px; flex:0 0 40px; border-radius:var(--r-sm);
    font-size:var(--t-lg); font-weight:700; letter-spacing:-.02em; line-height:1}
  .prop-tile.chico{width:24px; height:24px; flex-basis:24px; font-size:var(--t-xs)}
  .tile-terra{background:color-mix(in srgb, var(--terra) 14%, transparent); color:var(--terra-text)}
  .tile-coral{background:color-mix(in srgb, var(--coral) 28%, transparent); color:var(--terra-text)}
  .tile-brown{background:color-mix(in srgb, var(--brown) 14%, transparent); color:var(--brown)}
  .tile-dark{background:color-mix(in srgb, var(--dark) 12%, transparent); color:var(--dark)}
  .tile-ok{background:var(--ok-bg); color:var(--ok)}
  .tile-warn{background:var(--warn-bg); color:var(--warn)}
  /* La celda Nombre de la lista de propiedades: tile a la izquierda, y al lado
     el nombre con la dirección debajo. La columna Dirección se fue --era un
     ancho entero para un dato casi siempre vacío-- y su contenido entró acá.
     El `min-width` no es cosmético: sin él la tabla le da a esta columna lo que
     le sobra después de los chips y los botones, y "Villa Coco 2" se parte en
     dos renglones al lado de un tile de 40px. */
  .prop-nombre{display:flex; align-items:center; gap:var(--e2); min-width:210px}
  .prop-id{display:flex; flex-direction:column; min-width:0}
  .prop-id .n{font-weight:600; letter-spacing:-.01em}
  .prop-id .dir{font-size:var(--t-sm); color:var(--mute)}
  /* Los chips de canal de esa misma fila: son `.badge` de la familia de siempre
     y lo único que agrega esta regla es que respiren entre ellos. */
  .tabla-prop .badge{margin:0 var(--e1) 3px 0}
  /* El tile de 24px dentro de una fila de las dos grillas de puertas. En las
     dos el ancla ya era el renglón entero, así que alcanza con alinearlo. */
  table.cal td.prop-col > a, .puertas-lista li > a{display:flex; align-items:center; gap:var(--e1)}
  /* Las filas del selector "¿De cuál la querés ver?": el mismo renglón de la
     cartera --nombre, régimen, puertas, veredicto-- dentro del `.puertas-lista`
     que esa pantalla ya usaba. */
  .elegir-fila{display:flex; align-items:center; gap:var(--e2); flex-wrap:wrap}
  .elegir-fila .n{font-size:var(--t-lg); font-weight:600; letter-spacing:-.01em}
  .elegir-fila .puertas{font-size:var(--t-sm); font-weight:400; color:var(--mute)}

  /* ---------- Hoy, vacíos y canales (21-sep) ----------
     El fundador comparó la app con Guesty y aprobó tres cambios: que "Hoy" abra
     con un saludo y las cifras que contesta de un vistazo, que un estado vacío
     pese lo suficiente para leerse como una pantalla diseñada, y que un bloque
     tenga UN primario y no uno por fila. Nada de color nuevo: todo sale de los
     tokens de arriba. */

  /* El encabezado de la portada. El saludo es el `h1` de siempre --no hay
     tamaño nuevo-- y la fecha baja como `.sub`, pegada a él: son un solo
     renglón de información, no un título con subtítulo de pantalla. */
  .hoy-cab{margin-bottom:var(--e4)}
  .hoy-cab .sub{margin-bottom:0}

  /* La tarjeta de Primeros pasos: el texto a la izquierda, el avance a la
     derecha. Misma repartija que `.pp-cab` en la pantalla completa, y por eso
     reusa `.cifra`, `.cifra-rot` y `.wiz-barra` tal cual -- es el mismo dato
     mostrado dos veces, no dos diseños. La lista de los cuatro (#B3) va
     DEBAJO de esta fila y no adentro: por eso el flex vive en `.hoy-pp-fila`
     y no en `.hoy-pp` (la card entera), que ahora apila fila + lista + el
     enlace de agenda en un solo `<div>` de siempre. */
  .hoy-pp-fila{display:flex; align-items:flex-start; gap:var(--e5); flex-wrap:wrap}
  /* `min-width` y no `min-width:0`: con `0` el texto se deja estrujar hasta una
     palabra por renglón antes de que el `flex-wrap` llegue a actuar, que es
     exactamente lo que pasaba en un teléfono. Con un piso, el avance baja. */
  .hoy-pp-t{flex:1 1 280px; min-width:min(100%, 280px)}
  .hoy-pp-avance{flex:none; text-align:right}
  .hoy-pp-avance .wiz-barra{width:160px; margin:8px 0 0}
  @media (max-width:640px){
    /* Ya bajó: el avance se lee de izquierda a derecha como el texto de arriba,
       igual que en la cabecera de la pantalla completa (`.pp-avance`). */
    .hoy-pp-avance{text-align:left}
    .hoy-pp-avance .wiz-barra{width:100%}
  }
  .pp-lista-agenda{margin-top:var(--e3)}

  /* La fila de cifras, jerarquía §1b (docs/70, 24-sep-2026): UNA tarjeta con
     columnas separadas por hairline, no cuatro tarjetas iguales -- cuatro cajas
     idénticas se leen como cuatro pantallas distintas en vez de una sola
     respuesta con varias partes. `auto-fit` y no un número de columnas: las
     columnas son tres o cuatro según el plan y según si el IVA de la cuenta es
     UN número (con varios contribuyentes no lo es), así que la fila se reparte
     sola en vez de dejar un hueco. La tarjeta entera lleva la sombra flotante
     de la cáscara moderna (21-sep-2026); las columnas van sin fondo propio. */
  .hoy-cifras{
    display:grid; grid-template-columns:repeat(auto-fit, minmax(180px, 1fr));
    gap:0; margin-bottom:var(--e4);
    background:var(--bg); border:0; border-radius:var(--r-lg); box-shadow:var(--sh-float);
  }
  .hoy-col{
    padding:var(--e3) var(--e4); border-left:1px solid var(--line);
  }
  .hoy-col:first-child{border-left:0}
  /* ⚖ Fundador 24-sep-2026: los dos tintes de KPI de la maqueta del 21-sep
     (IVA "en curso" en --info-bg, puertas "resueltas" en --ok-bg) se
     retiran. Los mismos tintes ya cargan un significado en los badges de
     esos estados, y repetirlos como fondo de columna hacía que dos KPI se
     leyeran como un aviso ("cuidado" / "bien") cuando son solo un dato más
     de la fila -- las columnas van sin fondo propio, como cualquier hairline
     de la cáscara moderna. */
  /* El rótulo va ARRIBA de la cifra (`.cifra-rot` ya lo pinta) y la explicación
     abajo: lo que se mira es el número del medio. */
  /* ⚖ Fundador 24-sep-2026: el rótulo del KPI baja de versalitas a oración --
     `.cifra-rot` es un selector compartido por trece bloques de la app (rótulos
     de columna, de tarjeta, de cola) y ESTAS cuatro columnas son las únicas
     donde el rótulo va justo debajo de un número gigante (34px): las mayúsculas
     espaciadas competían con la cifra por atención en vez de presentarla. El
     override es solo de estas columnas -- el resto de `.cifra-rot` en la app
     sigue en versalitas, sin tocar. */
  .hoy-col .cifra-rot{
    text-transform:none; letter-spacing:normal; font-size:var(--t-sm);
  }
  /* La cifra de KPI es la que la pantalla vino a contestar: sube a semibold y
     34px, un escalón más que el resto de las cifras de la app (cáscara
     moderna 21-sep-2026). */
  .hoy-col .cifra{margin-top:var(--e1); font-weight:600; font-size:34px}
  .hoy-col .hint{display:block}
  @media (max-width:640px){
    /* Grilla 2×2 (jerarquía §1b): con una sola columna las cuatro cifras se
       leían en una columna larguísima antes de llegar a las colas de abajo. */
    .hoy-cifras{grid-template-columns:repeat(2, 1fr)}
    .hoy-col{border-left:1px solid var(--line)}
    .hoy-col:nth-child(2n+1){border-left:0}
    .hoy-col:nth-child(n+3){border-top:1px solid var(--line)}
  }

  /* El estado vacío con aire (⚖ fundador 21-sep-2026): el `padding` de `--e5`
     ya estaba; lo que faltaba era el MARGEN -- el bloque pegaba contra la
     tabla o la card de arriba y se leía como un renglón perdido en el medio de
     la pantalla en vez de como una pieza. */
  .vacio.vacio{margin:var(--e5) auto}


  /* ---------- toast (docs/70 §3, principio 3 "Retroalimentación") ---------- */
  /* Un solo contenedor para toda la app (`static/toast.js` lo llena): abajo al
     centro para no taparle a nadie el botón primario que acaba de apretar --
     arriba competiría con la barra fija y con el aviso de "cuenta sin
     escritura". `pointer-events:none` en el contenedor y `auto` en cada toast:
     el hueco vacío de abajo no puede robarle un click a lo que hay detrás. */
  .toast-wrap{position:fixed; left:50%; bottom:var(--e4); transform:translateX(-50%);
    z-index:80; display:flex; flex-direction:column; align-items:center;
    gap:var(--e2); pointer-events:none; max-width:calc(100vw - 2 * var(--e4))}
  /* Tokens semánticos solamente, a propósito (docs/70): `--ink`/`--bg` invertidos
     -- una píldora oscura que no compite con ninguna tarjeta de la pantalla --
     y un punto de color adelante distingue "listo" de "no se pudo" (sin borde
     izquierdo: el design system lo descarta). Sobre la tinta oscura van los
     tonos `--on-dark-*`, que son los que pasan contraste ahí. */
  .toast{pointer-events:auto; display:flex; align-items:center; gap:var(--e2);
    padding:var(--e2) var(--e3); border-radius:var(--r-pill); background:var(--ink);
    color:var(--bg); box-shadow:var(--sh-lift); font-size:var(--t-md)}
  .toast::before{content:""; flex:none; width:8px; height:8px; border-radius:50%;
    background:var(--on-dark-ok)}
  .toast[data-tipo="err"]::before{background:var(--on-dark-accent)}
  .toast button{background:none; border:0; color:inherit; font:inherit;
    line-height:1; padding:0; cursor:pointer; opacity:.7}
  .toast button:hover{opacity:1}
  @media (prefers-reduced-motion:no-preference){
    .toast{animation:toast-in .18s ease-out}
  }
  @keyframes toast-in{from{opacity:0; transform:translateY(6px)} to{opacity:1; transform:translateY(0)}}
