/* ============================================================================
   TUXANI — Command Center del operador (Fase 1). Depende de ../../assets/css/tokens.css
   ========================================================================== */

body { min-height: 100vh; }
[hidden] { display: none !important; }
.nv-mono { font-family: var(--nv-f-mono); font-variant-numeric: tabular-nums; }

/* ---------- GATE (login) ---------- */
.nv-gate { min-height: 100vh; display: grid; place-items: center; padding: 32px 20px;
  background: radial-gradient(1000px 520px at 50% -10%, rgba(47,169,140,.16), transparent 70%), var(--nv-abismo); }
.nv-gate-card { width: 100%; max-width: 420px; background: var(--nv-superficie);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-lg); box-shadow: var(--nv-sh-2);
  padding: 32px 28px; text-align: center; }
.nv-gate-card canvas { width: 132px; height: 132px; margin: 0 auto 10px; display: block; }
.nv-gate-card h1 { font-size: 22px; margin-bottom: 4px; }
.nv-gate-card .nv-sub { color: var(--nv-tenue); font-size: 13.5px; margin-bottom: 20px; }
.nv-field { width: 100%; background: var(--nv-suelo); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-sm); padding: 12px 14px; color: var(--nv-texto); font-size: 14px; margin-bottom: 10px; }
.nv-field::placeholder { color: var(--nv-fantasma); }
.nv-btn { font: 700 14px/1 var(--nv-f-body); border-radius: var(--nv-r-md); padding: 13px 18px;
  cursor: pointer; border: 1px solid transparent; transition: var(--nv-dur-fast); width: 100%;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px; }
.nv-btn-primary { background: var(--nv-savia); color: #04140E; }
.nv-btn-primary:hover { background: var(--nv-savia-hi); }
.nv-btn-google { background: #fff; color: #1f1f1f; border-color: var(--nv-linea-fuerte); }
.nv-btn-ms { background: #fff; color: #1f1f1f; border-color: var(--nv-linea-fuerte); }
.nv-btn-apple { background: #000; color: #fff; border-color: #000; }
.nv-gate-card > .nv-btn { margin-bottom: 10px; }
.nv-btn-ghost { background: transparent; border-color: var(--nv-linea-fuerte); color: var(--nv-texto); }
.nv-btn-ghost:hover { background: color-mix(in srgb, var(--nv-savia) 12%, transparent); }
.nv-sep { display: flex; align-items: center; gap: 10px; color: var(--nv-fantasma);
  font-size: 12px; margin: 14px 0; }
.nv-sep::before, .nv-sep::after { content: ""; flex: 1; height: 1px; background: var(--nv-linea); }
.nv-gate-err { color: var(--nv-critico); font-size: 12.5px; min-height: 16px; margin-top: 8px; }
.nv-gate-note { margin-top: 16px; font-size: 11px; color: var(--nv-fantasma); }

/* ---------- APP SHELL ----------
   `height: 100dvh` ademas del `min-height`: sin una altura ACOTADA el panel
   nunca es un contenedor de scroll de verdad (con `overflow-y:auto` pero sin
   tope de altura la caja simplemente crece y quien hace scroll es la pagina),
   y entonces `position: sticky` dentro del panel no se ancla a nada. Eso es
   exactamente lo que le pasaba al composer: la regla base decia `sticky` y no
   servia, asi que siete modulos la anularon con `static` uno por uno.
   Acotando aqui, el panel scrollea por dentro: la barra superior queda
   siempre a la vista y el composer se ancla abajo de verdad.
   Si un navegador no entiende `dvh`, ignora la linea y se queda con el
   `min-height: 100vh`: vuelve al scroll de pagina de antes. Degrada solo. */
.nv-app { display: grid; grid-template-columns: 264px 1fr; min-height: 100vh; height: 100dvh; }

.nv-side { background: var(--nv-abismo); border-right: 1px solid var(--nv-linea);
  display: flex; flex-direction: column; padding: 14px 10px; height: 100vh; position: sticky; top: 0; }
.nv-side .nv-sb { display: flex; align-items: center; gap: 10px; padding: 4px 8px 14px;
  border-bottom: 1px solid var(--nv-linea); margin-bottom: 6px; }
.nv-side .nv-sb canvas { width: 34px; height: 34px; }
.nv-side .nv-sb b { font-family: var(--nv-f-display); font-size: 15px; }
.nv-side .nv-sb span { display: block; font-family: var(--nv-f-mono); font-size: 10px; color: var(--nv-tenue); }
.nv-nav { flex: 1; overflow-y: auto; }
.nv-fam { font-family: var(--nv-f-mono); font-size: 9.5px; letter-spacing: .16em; color: var(--nv-fantasma);
  text-transform: uppercase; padding: 12px 8px 4px; }
.nv-nav-i { display: flex; align-items: center; gap: 10px; padding: 8px 9px; border-radius: var(--nv-r-sm);
  font-size: 13px; color: var(--nv-tenue); cursor: pointer; transition: var(--nv-dur-fast); border: none; background: none; width: 100%; text-align: left; }
.nv-nav-i:hover { background: color-mix(in srgb, var(--nv-savia) 8%, transparent); color: var(--nv-texto); }
.nv-nav-i.nv-act { background: var(--nv-savia); color: #04140E; font-weight: 600; }
.nv-nav-i .nv-ic { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.nv-nav-i .nv-ct { margin-left: auto; font-family: var(--nv-f-mono); font-size: 10.5px;
  background: color-mix(in srgb, var(--nv-savia) 18%, transparent); color: var(--nv-savia); padding: 1px 7px; border-radius: var(--nv-r-pill); }
.nv-nav-i.nv-act .nv-ct { background: rgba(4,20,14,.25); color: #04140E; }
.nv-side .nv-foot { border-top: 1px solid var(--nv-linea); padding-top: 10px; margin-top: 6px; }
.nv-side .nv-foot .nv-u { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-fantasma); padding: 6px 9px 0; word-break: break-all; }

/* MOVIL: la barra lateral pasa a ser un cajon que entra desde la izquierda.
   ESTE BLOQUE TIENE QUE IR DESPUES de las reglas base de `.nv-side`, y no es
   un detalle de estilo: un `@media` NO suma especificidad. Cuando esto vivia
   arriba (antes de la regla base), `position: sticky` ganaba por ser posterior
   y el cajon se quedaba DENTRO del flujo midiendo un alto de pantalla entero
   -- invisible, porque el `transform` de aqui si sobrevivia. Resultado: el
   telefono mostraba una pantalla negra completa y el encabezado quedaba
   debajo. Medido: el contenido empezaba en Y=844 en un movil de 844 de alto.
   Si alguien mueve este bloque hacia arriba, vuelve el fallo entero. Lo
   vigila `functions/qa/verify_movil_shell.mjs`. */
@media (max-width: 860px) { .nv-app { grid-template-columns: 1fr; }
  .nv-side { position: fixed; inset: 0 auto 0 0; width: 264px; transform: translateX(-100%);
    transition: transform var(--nv-dur) var(--nv-ease); z-index: 60;
    /* `dvh` y no `vh`: con top, bottom y height puestos a la vez el elemento
       queda sobre-restringido y gana `height`, asi que en iOS el cajon media
       mas que lo visible y el correo del pie se iba bajo la barra del
       navegador. Si el navegador no entiende `dvh` ignora esta linea y se
       queda con el `height: 100vh` de la regla base: degrada solo. */
    height: 100dvh; }
  .nv-side.nv-open { transform: none; }
  .nv-scrim { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 55; } }

/* `min-height: 0` es obligatorio: un item de grid/flex tiene `min-height:auto`
   por defecto y se niega a encoger por debajo de su contenido, asi que sin
   esto `.nv-work` desborda la altura del shell y el panel vuelve a no
   scrollear por dentro. */
.nv-work { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.nv-wtop { display: flex; align-items: center; gap: 12px; padding: 14px 18px;
  border-bottom: 1px solid var(--nv-linea); position: sticky; top: 0;
  /* 80% -> 93% (agente `compositor-multimodal`, 2026-08-16). Medido: con 80%
     el encabezado deja pasar un 20% del contenido que scrollea por debajo
     (con el blur de 12px encima), y eso es literal lo que reporto el dueno
     -- "no deja ver el encabezado" -- durante el tramo en que cualquier
     contenido (no solo el composer) cruza por detras de esta banda sticky.
     93% conserva el vidrio esmerilado (sigue habiendo blur y una pizca de
     transparencia) pero corta el sangrado a un nivel que ya no se lee como
     contenido superpuesto. NO se sube a 100%: eso mata el efecto vidrio a
     proposito, que es una decision de diseño del dueno, no un descuido --
     si algun dia hace falta opaco del todo, que lo decida el, no este
     cambio. Verificado en tema claro y oscuro, con el encabezado sobre
     contenido claro y sobre contenido oscuro (ver INFORME_COMPOSITOR_MULTIMODAL.md). */
  background: color-mix(in srgb, var(--nv-abismo) 93%, transparent); backdrop-filter: blur(12px); z-index: 20; }
.nv-hamb { display: none; background: none; border: 1px solid var(--nv-linea); color: var(--nv-texto);
  border-radius: 9px; width: 36px; height: 34px; cursor: pointer; }
@media (max-width: 860px) { .nv-hamb { display: grid; place-items: center; } }

/* ---------- VOLVER ----------------------------------------------------------
   El botón de atrás de la barra superior (2026-08-25). Nace `hidden` en el HTML
   y lo enciende `sincronizarVolver()` fuera de Inicio.

   `[hidden]` NECESITA SU PROPIA REGLA y no es un adorno: la del navegador es una
   hoja de USER-AGENT, así que un `display: inline-flex` de autor la gana y el
   botón se vería SIEMPRE, también en la portada — justo la pantalla donde no
   tiene sentido. Es el fallo silencioso clásico de esconder con el atributo y
   pintar con la clase.

   `flex-shrink: 0` porque a 360 px esta barra ya lleva hamburguesa, título,
   tema, monedero, idioma y logo: sin esto el botón es el primero en aplastarse
   y el icono sale ovalado. El que puede encoger es el título, que tiene texto.

   La palabra se esconde por debajo de 560 px y queda solo la flecha, con el
   rótulo vivo en `aria-label` para quien navega a ciegas. No es lo mismo que
   quitarlo: la flecha sola en esa esquina se lee sin ayuda, y el ancho que
   libera es el que hace que el título del módulo siga cabiendo. */
.nv-volver { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  background: none; border: 1px solid var(--nv-linea); color: var(--nv-tenue);
  font: 600 12.5px/1 var(--nv-f-body); padding: 8px 12px 8px 9px;
  border-radius: var(--nv-r-pill); cursor: pointer; transition: var(--nv-dur-fast); }
.nv-volver[hidden] { display: none; }
.nv-volver:hover { color: var(--nv-texto); border-color: var(--nv-linea-fuerte);
  background: color-mix(in srgb, var(--nv-savia) 10%, transparent); }
.nv-volver:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: 2px; }
.nv-volver-ic { display: inline-flex; }
.nv-volver-ic svg { width: 17px; height: 17px; }
@media (max-width: 560px) {
  .nv-volver { padding: 8px; gap: 0; }
  .nv-volver-t { display: none; }
}

.nv-wtop h2 { font-size: 16px; }
.nv-wtop .nv-lang { margin-left: auto; font-family: var(--nv-f-mono); font-size: 11px; color: var(--nv-tenue);
  border: 1px solid var(--nv-linea); padding: 6px 10px; border-radius: var(--nv-r-pill); background: none; cursor: pointer; }
.nv-wtop .nv-av { width: 30px; height: 30px; border-radius: 50%; background: var(--nv-grad); }
.nv-wtop .nv-hlogo { width: 56px; height: 56px; display: block; flex-shrink: 0; overflow: visible; }

/* `--nv-panel-pad` es UNA variable y no un 18 repetido a proposito: la barra
   de pestañas pegada se sale hasta el borde con este mismo valor en negativo y
   tapa con el la rendija que `sticky` deja por encima. Si el padding y ese
   negativo se separan, la barra deja ver el contenido pasando por detras o se
   come la tarjeta de arriba. Se cambia aqui y las dos cosas siguen cuadrando. */
.nv-panel { --nv-panel-pad: 18px; --nv-aire: 16px;
  padding: var(--nv-panel-pad); display: flex; flex-direction: column; gap: var(--nv-aire); overflow-y: auto;
  flex: 1; min-height: 0;
  /* En iPhone la franja del gesto de inicio se come lo ultimo de la lista. */
  padding-bottom: calc(var(--nv-panel-pad) + env(safe-area-inset-bottom, 0px)); }
/* Cuando la pantalla LLEVA composer anclado, este tapa la parte baja del
   panel mientras esta pegado abajo. Sin este hueco el ultimo dato de la lista
   queda debajo del composer y no hay forma de leerlo.
   `:has()` y no una clase por JS: el panel se vacia con `innerHTML=''` en cada
   cambio de modulo y no hay gancho de destruccion donde quitar la clase; una
   clase pegada de un modulo anterior dejaria un hueco fantasma. Si el
   navegador no entiende `:has()`, se queda con el padding de arriba: se pierde
   el hueco, no se rompe nada.
   REVISION 2026-08-16: antes miraba `:not(.nv-comp-plano)` porque ESE era el
   composer anclado (el defecto). Ahora el defecto es `static` y el anclado de
   verdad es `.nv-comp-ancla` (ver `composer.js`/`app.css` mas arriba) -- el
   selector tiene que mirar la clase nueva o reservaria 190px de hueco fantasma
   en las 8 pantallas que ya NO tienen nada pegado abajo. */
.nv-panel:has(> * .nv-comp-ancla),
.nv-panel:has(> .nv-comp-ancla) {
  padding-bottom: calc(190px + env(safe-area-inset-bottom, 0px)); }

.nv-intent { background: linear-gradient(120deg, color-mix(in srgb, var(--nv-savia) 14%, var(--nv-superficie)), var(--nv-superficie));
  border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-md); padding: 16px; display: flex; align-items: center; gap: 14px; }
.nv-intent .nv-ai { width: 38px; height: 38px; border-radius: 10px; background: var(--nv-grad); flex-shrink: 0;
  display: grid; place-items: center; font-size: 18px; }
.nv-intent .nv-k { font-family: var(--nv-f-mono); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--nv-savia); }
.nv-intent b { display: block; font-size: 14.5px; margin: 2px 0; }
.nv-intent p { font-size: 12.5px; color: var(--nv-tenue); margin: 0; }

/* LAS CIFRAS SE SALIAN DE SU TARJETA. Daniel lo reporto dos veces, con
   captura: "COP $6.534.000" y "COP $3.834.000" pintados por fuera de la caja
   de VENTAS MES y de CAJA.

   MEDIDO ANTES DE TOCAR (Chrome headless, metricas de dispositivo reales,
   barrido de 320 a 900px, con la cifra que sale de verdad de `plata()`):

     ancho   tarjeta   hueco util   texto   SE SALE
      320     136.0       108.0      141     +33px
      360     156.0       128.0      141     +13px
      430     123.3        95.3      141   +45.7px
      600     132.0       104.0      141     +37px
      900     141.0       113.0      141     +28px

   Se salia en 8 de los 10 anchos probados, y tambien en escritorio: no era un
   fallo "del movil". Con la cifra mas larga que puede salir de verdad
   ("COP $125.400.000") se salia hasta 73.7px, y el panel entero se iba a
   scroll horizontal (scrollWidth 350 sobre clientWidth 320).

   DE LAS DOS CAUSAS QUE SE SOSPECHABAN, SOLO UNA ERA:

   1. NO era `min-width: auto` del item de grid. Se probo: con
      `.nv-kpi { min-width: 0 }` las medidas salen IDENTICAS al byte (136.0 /
      108.0 / 141 a 320px). No aplica porque la pista es `minmax(120px, 1fr)`:
      su minimo es 120px EXPLICITO, no `auto`, asi que la pista nunca se
      dimensiona por contenido y la tarjeta ya valia exactamente lo que la
      pista. La tarjeta no se negaba a encoger; encogia de mas.

   2. SI era la letra: 24px monoespaciada FIJA. En mono cada caracter ocupa
      ~0.6em, asi que "$125.400.000" mide 169px pase lo que pase, y el hueco
      util de la tarjeta llegaba a bajar a 95px. No cabia por ningun lado.

   Y habia una tercera que no estaba en la hipotesis y solo se ve midiendo:
   `minmax(120px, 1fr)` con `auto-fit` mete una columna mas en cuanto cabe, y
   entonces las tarjetas se hacen MAS ESTRECHAS al ensanchar la pantalla.
   Medido: a 390px la tarjeta era de 171px, y a 430px bajaba a 123.3px. Por eso
   la letra NO puede depender de `vw`: seria mas grande justo donde la tarjeta
   es mas pequeña. Depende del ANCHO DE LA TARJETA, con `cqi`.

   EL ARREGLO, y por que cada pieza:
   - `minmax(150px, ...)`: 120px no da para una cifra de plata a ningun tamaño
     legible. Con 150 desaparece la columna de mas: a 430px la tarjeta pasa de
     123.3px a 191px.
   - `container-type: inline-size` + `clamp(16px, 12cqi, 24px)`: la letra se
     mide contra la tarjeta. En tarjeta ancha se queda en los 24px de siempre
     (nada cambia en escritorio); en la mas estrecha baja a 16px, que sigue
     siendo la pieza dominante de la tarjeta (el rotulo es de 11px).
   - `font-size: 16px` antes del `clamp`: si un navegador no entiende `cqi`
     descarta esa linea y se queda con 16px, que cabe. Degrada solo.
   - `overflow-wrap: anywhere` es una VALVULA, no un recorte. NO se usa
     `overflow: hidden` a proposito: una cifra cortada ("$3.834" por
     "$3.834.000") es un numero FALSO en la pantalla de la plata de alguien.
     Esto parte la linea, no esconde nada.

   MEDIDO DESPUES, mismo barrido: 0 de 40 medidas se salen, y el panel ya no
   tiene scroll horizontal en ningun ancho.

   LIMITE CONOCIDO, y se dice: con un importe de 14 caracteres o mas
   ("$1.254.000.000", o sea mil millones) la valvula entra en las dos tarjetas
   mas estrechas (hueco util 128px) y la cifra parte de linea. Sigue entera y
   dentro de la caja; solo se lee en dos renglones. Hasta 999.999.999 no parte
   en ningun ancho de 320 a 900. Lo vigila el grupo 6 de
   `functions/qa/verify_movil_shell.mjs`. */
.nv-kpirow { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.nv-kpi { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 14px;
  container-type: inline-size; }
.nv-kpi .nv-lb { font-size: 11px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .06em; }
.nv-kpi .nv-vl { font-family: var(--nv-f-mono); font-weight: 700; margin-top: 6px; overflow-wrap: anywhere;
  font-size: 18px; font-size: clamp(18px, 16cqi, 24px); }
/* EL IMPORTE EXACTO, DEBAJO Y EN PEQUEÑO. Es lo que convierte «$14,2 M» de un
   redondeo en un resumen: la cifra al peso sigue en la pantalla, no en un
   tooltip que en un telefono no existe. 11px es el mismo tamaño que el rotulo,
   asi que no compite con la cifra grande; `anywhere` es la misma valvula de
   arriba — parte de linea antes que salirse, porque un importe cortado es un
   dato falso. */
.nv-kpi .nv-vlx { font-family: var(--nv-f-mono); font-size: 11px; color: var(--nv-tenue);
  margin-top: 3px; overflow-wrap: anywhere; letter-spacing: -.01em; }
.nv-kpi.nv-hl { border-color: var(--nv-linea-fuerte);
  background: linear-gradient(180deg, color-mix(in srgb, var(--nv-cosecha) 9%, var(--nv-superficie)), var(--nv-superficie)); }

/* Portada: lo que se dice cuando los numeros estan en cero o incompletos.
   El cero de un usuario nuevo no es una avería, asi que no va en rojo: va con
   el mismo peso visual que una nota al pie, y con los dos botones que lo
   sacan de cero. */
.nv-home-aviso:not(:empty) { margin-top: 14px; display: grid; gap: 12px; }
.nv-home-nota { font-size: 12.5px; color: var(--nv-tenue); margin: 0; }
.nv-home-startact { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

.nv-pcard { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 16px; }
.nv-pcard h4 { font-size: 14px; margin-bottom: 8px; }
.nv-pcard p { font-size: 13px; color: var(--nv-tenue); margin: 0; }
.nv-soon { display: inline-block; font-family: var(--nv-f-mono); font-size: 10px; color: var(--nv-cosecha);
  border: 1px solid color-mix(in srgb, var(--nv-cosecha) 40%, transparent); border-radius: var(--nv-r-pill); padding: 2px 8px; margin-top: 8px; }

/* ---------- Composer (la barra de escribir con los multimodales) ----------
   Lo pinta `app/assets/js/composer.js`, en un solo sitio. Antes estaba copiado
   a mano en diez modulos y cada copia habia derivado por su lado.

   ANCLADO ABAJO: el composer se pega al fondo del panel. Para que `sticky`
   funcione el panel tiene que ser un contenedor de scroll ACOTADO -- ver el
   comentario de `.nv-app`. Mientras eso no estuvo, la regla base decia
   `sticky` y no hacia nada, y siete modulos la anularon con `static` en vez
   de arreglar la causa.

   REVISION 2026-08-16 (agente `compositor-multimodal`, pedido del dueno: «los
   contenedores donde se escribe estan flotantes y se ve feo al scrollear»).
   Medido en navegador a 360px con contenido largo inyectado en Biblioteca
   Viva: el `sticky;bottom` de ESTA regla es un NO-OP en los 8 modulos que la
   usaban tal cual (assets.js x2, crop.js, agro_lab.js, community.js,
   economy.js, livestock.js, studio.js, library.js). La razon: en los ocho el
   composer va ANTES de sus resultados (se pregunta una vez, la respuesta
   aparece debajo) -- nunca es el ultimo hijo de una lista que crece. Un
   `bottom` sin `top` solo frena al elemento cuando SU FLUJO NATURAL lo
   empujaria por DEBAJO del borde inferior del scroll; aqui el composer esta
   cerca de la CABEZA del documento, asi que al bajar se mueve hacia ARRIBA y
   sale por encima -- el `bottom` nunca tiene nada que frenar. Comprobado con
   getBoundingClientRect: a scrollTop=0 comp.top=216.7 (igual a su posicion
   estatica); a scrollTop=261 comp.top=-44.6, ya por encima de headBottom=64
   de `.nv-wtop` -- se movio EXACTAMENTE lo mismo que si fuera `static`. Ese
   trayecto, cruzando por detras del encabezado semitransparente
   (`backdrop-filter: blur`) con esta caja arrastrando una sombra de 22px, es
   el "flota y no deja ver" que reporto el dueno -- no hay solapamiento
   sostenido (el header gana el z-index, 20 contra 5, su texto sigue legible),
   pero el transito es real y feo tal como esta.
   Los 4 modulos que SI necesitan quedar anclados (assistant.js, la mitad de
   `assets.js`, la respuesta de `community.js`, `protected.js`) YA viven
   dentro de su propia columna flex de altura fija y usan `nv-comp-plano` --
   hoy no existe NI UN caso donde este `sticky;bottom` compartido ancle algo
   de verdad. Por eso el DEFECTO pasa a `static` (igual que `nv-comp-plano`,
   que se queda intacto y ahora es redundante con el defecto, no roto) y el
   anclaje de verdad se vuelve EXPLICITO: `nv-comp-ancla` (mas abajo), que
   `NVComposer.crear(t, {ancla:true})` agrega. Ningun llamador de hoy la pide
   -- el mecanismo queda vivo para el dia que exista un chat de verdad (lista
   que crece con el composer siempre al final), no borrado.
   🔴 `verify_movil_shell.mjs §5.6/5.7/5.9/5.10` sigue afirmando el defecto
   VIEJO (sticky-siempre) a proposito de la regresion de 2026-07-30 (siete
   modulos que se salian del anclaje uno por uno). Con este cambio esas cuatro
   comprobaciones SE PONEN ROJAS, y es lo esperado: afirman una decision que
   el dueno acaba de invertir. La prueba no se toca aqui -- `functions/` esta
   fuera del alcance de este agente -- queda para quien la actualice a la
   regla nueva (anclaje EXPLICITO, no por defecto).
   `env(safe-area-inset-bottom)`: en iPhone con gesto de inicio los ultimos
   ~34px del alto no son tocables. Sin esto el boton de enviar cae justo ahi.
   Por eso `nv-comp-ancla` la conserva intacta: el dia que se use, sigue
   respetando el gesto de inicio igual que antes. */
.nv-composer { background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-md);
  padding: 9px 12px; display: flex; flex-direction: column; gap: 4px;
  position: static; }
/* ANCLAJE EXPLICITO — el UNICO composer que de verdad quiere quedar pegado al
   fondo del scroll compartido mientras una lista crece por encima de el
   (patron WhatsApp). `NVComposer.crear(t, {ancla:true})` agrega esta clase.
   Ningun llamador de hoy la usa: los 4 que necesitaban quedar fijos ya viven
   en su propia columna flex (`nv-comp-plano`, mas abajo) y no la necesitan.
   Misma regla de `bottom` y la misma sombra que llevaba el defecto viejo. */
.nv-comp-ancla { position: sticky; bottom: calc(10px + env(safe-area-inset-bottom, 0px)); z-index: 5;
  box-shadow: 0 6px 22px rgba(0,0,0,.22); }
/* Dentro de una hoja o un modal el composer YA esta al fondo de una columna
   flex de altura fija: ahi anclarlo lo referiria al scroll de la pagina, que
   no es el del modal, y se saldria de la tarjeta. Es la UNICA excepcion QUE
   YA EXISTIA antes de la revision de arriba, y se deja intacta a proposito:
   con el defecto ahora en `static`, `nv-comp-plano` queda REDUNDANTE (mismo
   resultado) pero inofensiva -- ningun modulo que la use cambia de aspecto. */
.nv-comp-plano { position: static; bottom: auto; box-shadow: none; }
/* LA FILA VA DEBAJO DEL CAMPO, PEGADA. Antes el campo iba DENTRO de esta fila,
   compitiendo por el ancho con los botones: en un telefono le quedaban unos
   pocos centimetros y no se alcanzaba a leer lo escrito. Ahora el campo va a
   todo el ancho arriba y aqui debajo quedan los botones — multimodales a la
   izquierda, enviar a la derecha, como en cualquier chat.
   El `gap: 4px` del contenedor es a proposito: casi sin aire entre el campo y
   sus botones, para que se lean como una sola pieza. */
.nv-comp-fila { display: flex; align-items: center; gap: 8px; }
.nv-comp-fila .nv-send { margin-left: auto; }
.nv-composer .nv-tool { width: 44px; height: 44px; flex-shrink: 0; border-radius: 11px; border: 1px solid var(--nv-linea);
  background: var(--nv-suelo); display: grid; place-items: center; font-size: 15px; color: var(--nv-tenue); cursor: pointer; margin-bottom: 1px;
  /* `relative` para anclar el punto vivo de `.nv-tool-on::after` (micro
     encendido). Sin esto el punto busca el ancestro posicionado mas cercano y
     el aviso de «te estoy escuchando» aparece lejos del boton, en la esquina
     de la barra.
     CORRECCION 2026-08-16 (lider): este comentario decia «que es el propio
     `.nv-composer` (es sticky)» y se quedo VIEJO el mismo dia — el defecto de
     `.nv-composer` paso a `static` en la revision de `compositor-multimodal`
     (mas arriba, ~:262). La regla NO sobra por eso: al dejar de estar
     posicionado el compositor, el punto se iria a buscar un ancestro aun mas
     lejano, asi que `relative` hace MAS falta que antes, no menos. Se reescribe
     el porque en vez de borrarlo: un comentario que miente ya costo dos
     encargos en esta casa. */
  position: relative; }
.nv-composer .nv-tool:hover { color: var(--nv-texto); border-color: var(--nv-linea-fuerte); }

/* ALTO DEL CAMPO: 4,5 lineas en movil, 6 en escritorio, como pidio Daniel.
   Con `font: 14px/1.45` una linea mide 20,3px y el campo lleva 7px de relleno
   arriba y abajo: 4,5 lineas = 4,5x20,3 + 14 = 105px; 6 lineas = 136px.
   El enunciado del placeholder ocupa dos lineas y antes solo se veia una: no
   se cortaba de lado, se cortaba de ALTO. Con 4,5 lineas se lee entero.
   `max-height` aqui es el UNICO tope de crecimiento del proyecto: antes habia
   tres `Math.min(scrollHeight, 120|132)` sueltos en el JS, ninguno enterado
   del ancho de pantalla. `composer.js` ya no recorta: solo pide altura. */
.nv-composer textarea, .nv-composer input { width: 100%; flex: 1; min-width: 0; background: none; border: none; color: var(--nv-texto);
  font: 400 14px/1.45 var(--nv-f-body); outline: none; resize: none; overflow-y: auto;
  min-height: 105px; max-height: 190px; padding: 7px 0; }
.nv-composer textarea::placeholder, .nv-composer input::placeholder { color: var(--nv-tenue); opacity: 1; }

/* ENVIAR: 44x44 (el minimo tactil), no 36. Antes ni siquiera tenia nombre
   accesible; ahora se lo pone `composer.js` desde i18n en todas las copias.
   La sombra lo despega de la superficie: sobre verde plano y en pantalla
   pequeña el icono se perdia contra el borde de la barra. */
.nv-composer .nv-send { width: 44px; height: 44px; flex-shrink: 0; border-radius: 12px; background: var(--nv-savia); color: #04140E;
  display: grid; place-items: center; font-size: 16px; border: none; cursor: pointer; margin-bottom: 1px;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--nv-savia) 45%, transparent); transition: var(--nv-dur-fast); }
.nv-composer .nv-send:hover { background: var(--nv-savia-hi); transform: translateY(-1px); }
.nv-composer .nv-send:disabled { opacity: .5; cursor: default; transform: none; box-shadow: none; }
.nv-composer .nv-tool svg, .nv-composer .nv-send svg { color: inherit; }

/* Aviso del propio composer (boton multimodal sin conectar, archivo ilegible).
   Vacio no ocupa nada: nada de reservar hueco para un mensaje que casi nunca
   sale. */
.nv-comp-aviso:empty { display: none; }
.nv-comp-aviso { font-size: 12.5px; line-height: 1.4; color: var(--nv-tenue); padding: 2px 2px 0; }
.nv-comp-aviso.is-bad { color: var(--nv-critico); }

@media (min-width: 861px) {
  /* 6 lineas: 6x20,3 + 14 = 136px. */
  .nv-composer textarea, .nv-composer input { min-height: 136px; max-height: 260px; }
}

/* ---------- Marketplace ---------- */
.nv-mki { display: inline-flex; align-items: center; justify-content: center; }
.nv-mk-bar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.nv-seg { display: inline-flex; background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-pill); padding: 3px; }
.nv-seg-b { font: 600 12.5px/1 var(--nv-f-body); color: var(--nv-tenue); background: none; border: none; padding: 8px 14px; border-radius: var(--nv-r-pill); cursor: pointer; transition: var(--nv-dur-fast); }
.nv-seg-b.on { background: var(--nv-savia); color: #04140E; }
/* Pestañas de modulo: se pegan arriba del panel. Ahora que el panel scrollea
   por dentro, la barra superior (`.nv-wtop`) queda SIEMPRE fija, pero estas
   pestañas se iban de la pantalla en cuanto la lista crecia y el usuario ya no
   sabia en que solapa estaba. Solo las de modulo: las de dentro de una tarjeta
   o de un panel propio (`.nv-ib-seg`, en la bandeja) se quedan como estan.
   El halo solido evita que el contenido asome por las esquinas de la pildora. */
.nv-lb-seg, .nv-ac-seg, .nv-tr-seg, .nv-ag-inseg {
  position: sticky; top: 0; z-index: 4;
  box-shadow: 0 0 0 7px var(--nv-abismo); }
.nv-mk-search { flex: 1; min-width: 180px; display: flex; align-items: center; gap: 8px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-pill); padding: 8px 14px; color: var(--nv-tenue); }
.nv-mk-search input { flex: 1; background: none; border: none; outline: none; color: var(--nv-texto); font-size: 14px; }
.nv-mk-pub { padding: 10px 16px; }
.nv-mk-pub span { white-space: nowrap; }
.nv-mk-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
/* LAS 10 PASTILLAS DEL CLUSTER, DE DOS EN DOS — y no es estetica, es el pliegue.
   `market_prospect.js:361` las pinta con `nv-btn`, y `.nv-btn` lleva
   `width: 100%` (linea 22): cada pastilla se comia una fila entera y las diez
   ocupaban 485 px. MEDIDO EN UN NAVEGADOR A 360x780, que es un celular: el boton
   de «buscar posibles clientes fuera» quedaba a 809 px — POR DEBAJO DEL PLIEGUE,
   o sea invisible sin deslizar. Es literalmente la queja del dueño del QA
   2026-08-08 («no tan abajo, que no se sabe»), que subir el boton por si solo NO
   cerraba: lo que lo hundia eran las pastillas de encima.
   Con dos columnas: 485 px -> 267 px, y el boton sube de 809 a 590. Se ve al
   abrir, sin tocar nada y sin scroll horizontal.
   VA SOBRE `.nv-mk-chip` EN SINGULAR a proposito: `.nv-mk-chips` (el contenedor)
   lo comparte `marketplace.js:88`, pero alli los hijos son `.nv-fchip`, que no
   son `nv-btn` y ya venian a su ancho. Tocar el contenedor los habria movido
   tambien. */
.nv-mk-chip { width: auto; flex: 1 1 calc(50% - 4px); min-height: 44px; padding: 10px 12px; }
.nv-fchip { font-size: 12px; padding: 7px 13px; border-radius: var(--nv-r-pill); border: 1px solid var(--nv-linea-fuerte); color: var(--nv-tenue); background: none; cursor: pointer; transition: var(--nv-dur-fast); }
.nv-fchip.on { background: color-mix(in srgb, var(--nv-savia) 14%, transparent); color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-mk-count { font-family: var(--nv-f-mono); font-size: 11.5px; color: var(--nv-tenue); margin: 12px 0 4px; }
.nv-mk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 14px; }
.nv-mk-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); overflow: hidden; box-shadow: var(--nv-sh-1); transition: var(--nv-dur-fast); }
.nv-mk-card:hover { border-color: var(--nv-linea-fuerte); transform: translateY(-2px); }
.nv-mk-media { height: 110px; background: linear-gradient(135deg, #1a4a38, #0d3327); position: relative; }
.nv-mk-trust { position: absolute; top: 9px; right: 9px; display: inline-flex; align-items: center; gap: 2px; font-family: var(--nv-f-mono); font-size: 11px; color: var(--nv-dorado); background: rgba(4,20,14,.6); border: 1px solid rgba(230,194,0,.4); padding: 3px 8px; border-radius: var(--nv-r-pill); }
.nv-mk-body { padding: 13px; }
.nv-mk-title { font-size: 14px; font-weight: 700; }
.nv-mk-price { font-size: 16px; color: var(--nv-cosecha); margin: 4px 0; }
.nv-mk-loc { display: flex; align-items: center; gap: 3px; font-size: 11.5px; color: var(--nv-tenue); }
.nv-mk-act { display: flex; gap: 8px; margin-top: 11px; align-items: center; }
.nv-mk-neg { flex: 1; padding: 9px 12px; font-size: 12.5px; }
.nv-mk-savebtn { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; border: 1px solid var(--nv-linea-fuerte); background: none; color: var(--nv-tenue); cursor: pointer; display: grid; place-items: center; transition: var(--nv-dur-fast); }
.nv-mk-savebtn.on { color: var(--nv-critico); border-color: color-mix(in srgb, var(--nv-critico) 45%, transparent); background: color-mix(in srgb, var(--nv-critico) 12%, transparent); }
.nv-mk-empty { grid-column: 1 / -1; text-align: center; color: var(--nv-tenue); padding: 40px 20px; display: flex; flex-direction: column; align-items: center; gap: 12px; }

/* ---------- Dashboard: accesos rápidos + invitación al asistente ---------- */
.nv-qa { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 10px; }
.nv-qa-tile { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 16px 10px; background: var(--nv-superficie);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); cursor: pointer; color: var(--nv-texto); font-size: 12px; text-align: center; transition: var(--nv-dur-fast); }
.nv-qa-tile:hover { border-color: var(--nv-linea-fuerte); transform: translateY(-2px); background: color-mix(in srgb, var(--nv-savia) 7%, var(--nv-superficie)); }
.nv-qa-ic { width: 22px; height: 22px; display: grid; place-items: center; }
.nv-asst-hint { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; cursor: pointer;
  background: linear-gradient(120deg, color-mix(in srgb, var(--nv-savia) 14%, var(--nv-superficie)), var(--nv-superficie));
  border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-md); padding: 14px 16px; color: var(--nv-texto); font-size: 13.5px; transition: var(--nv-dur-fast); }
.nv-asst-hint:hover { border-color: var(--nv-savia); }
.nv-asst-hint-ic { width: 30px; height: 30px; border-radius: 9px; background: var(--nv-grad); display: grid; place-items: center; flex-shrink: 0; }

/* ---------- Asistente IA flotante (omnipresente) ---------- */
.nv-fab { position: fixed; right: 16px; bottom: 16px; z-index: 70; width: 84px; height: 84px;
  border: none; cursor: pointer; background: transparent; padding: 0; display: grid; place-items: center; transition: var(--nv-dur-fast); }
.nv-fab-logo { width: 84px; height: 84px; display: block; grid-area: 1 / 1; filter: drop-shadow(0 6px 18px rgba(47,169,140,.5)); }
.nv-fab-txt { grid-area: 1 / 1; align-self: center; justify-self: center; z-index: 1; pointer-events: none;
  font: 800 17px/1 var(--nv-f-body); color: #fff; letter-spacing: .5px; text-shadow: 0 1px 4px rgba(0,0,0,.7), 0 0 10px rgba(0,0,0,.4); }
/* El FAB mide 84px y vive pegado abajo a la derecha: justo encima del boton
   de enviar del composer anclado, que tambien va a la derecha. Ese solape es
   la razon literal de «no se ve bien el boton de enviar en el movil»: no era
   contraste, era que el planeta lo tapaba. Cuando la pantalla lleva composer
   anclado, el FAB sube por encima de el.
   `:has()` otra vez para no depender de una clase que nadie quita al cambiar
   de modulo. Sin soporte, el FAB se queda abajo: se vuelve al solape de hoy,
   no aparece un fallo nuevo.
   REVISION 2026-08-16: mismo motivo que el `:has()` de `.nv-panel` de mas
   arriba -- el composer anclado de verdad ahora es `.nv-comp-ancla`, no
   `:not(.nv-comp-plano)`. Con el selector viejo el FAB se seguiria subiendo
   178px en las 8 pantallas que ya no tienen nada pegado abajo, sin motivo. */
.nv-app:has(.nv-panel .nv-comp-ancla) .nv-fab {
  bottom: calc(178px + env(safe-area-inset-bottom, 0px)); }
.nv-fab:hover { transform: translateY(-2px) scale(1.06); }
.nv-fab:hover .nv-fab-logo { filter: drop-shadow(0 10px 24px rgba(47,169,140,.65)); }
.nv-fab svg { color: #04140E; }
.nv-asheet { position: fixed; inset: 0; z-index: 80; background: rgba(0,0,0,.5); display: flex; align-items: flex-end; justify-content: center; }
.nv-asheet-panel { width: 100%; max-width: 460px; max-height: 82vh; background: var(--nv-suelo); border: 1px solid var(--nv-linea-fuerte);
  border-radius: var(--nv-r-lg) var(--nv-r-lg) 0 0; display: flex; flex-direction: column; overflow: hidden; box-shadow: var(--nv-sh-2); animation: nvUp .28s var(--nv-ease); }
@media (min-width: 560px) { .nv-asheet { align-items: center; } .nv-asheet-panel { border-radius: var(--nv-r-lg); max-height: 78vh; } }
@keyframes nvUp { from { transform: translateY(30px); opacity: .4; } to { transform: none; opacity: 1; } }
.nv-asheet-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--nv-linea); }
.nv-asheet-av { width: 34px; height: 34px; border-radius: 10px; background: var(--nv-grad); display: grid; place-items: center; flex-shrink: 0; }
.nv-asheet-head b { font-size: 14px; display: block; }
.nv-asheet-sub { font-size: 11.5px; color: var(--nv-tenue); }
.nv-asheet-x { background: none; border: none; color: var(--nv-tenue); font-size: 15px; cursor: pointer; width: 32px; height: 32px; border-radius: 8px; }
.nv-asheet-x:hover { background: color-mix(in srgb, var(--nv-savia) 12%, transparent); color: var(--nv-texto); }
.nv-asheet-msgs { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.nv-amsg { max-width: 82%; padding: 10px 13px; border-radius: 13px; font-size: 13.5px; line-height: 1.45; }
.nv-amsg.ai { background: var(--nv-superficie); border: 1px solid var(--nv-linea); align-self: flex-start; border-bottom-left-radius: 4px; }
.nv-amsg.me { background: var(--nv-savia); color: #04140E; align-self: flex-end; border-bottom-right-radius: 4px; }
.nv-typing { opacity: .7; font-family: var(--nv-f-mono); }
/* Acción propuesta por el asistente. El usuario lee y decide: nunca navegamos
   solos (la intención viene de un clasificador con confianza, no de una orden). */
.nv-amsg-cta {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 8px;
  margin-top: 2px; padding: 10px 14px; cursor: pointer;
  background: var(--nv-savia); color: #04140E; border: none;
  border-radius: 13px; border-bottom-left-radius: 4px;
  font-size: 13.5px; font-weight: 700; line-height: 1.2;
  transition: transform .12s ease, filter .12s ease;
}
.nv-amsg-cta:hover { filter: brightness(1.08); transform: translateY(-1px); }
.nv-amsg-cta:active { transform: translateY(0); }
.nv-amsg-cta:focus-visible { outline: 2px solid var(--nv-cosecha); outline-offset: 2px; }
.nv-amsg-cta .nv-mki { display: inline-flex; }
/* En el panel de ChatHome el CTA va dentro de la tarjeta de intención. */
.nv-intent .nv-amsg-cta { margin-top: 10px; }
.nv-asheet-panel .nv-composer { border: none; border-top: 1px solid var(--nv-linea); border-radius: 0; }

/* Foto libre (banco de Pexels) dentro del chat de publicar, 2026-08-24. El
   crédito va SIEMPRE visible bajo cada miniatura — nunca oculto detrás de un
   hover, nunca en letra ilegible: es lo que declara que la foto es un
   ejemplo, no el producto real de quien publica (ver `fotos_stock.js`). */
.nv-fotolibre { align-self: stretch; max-width: 100%; display: flex; flex-direction: column; gap: 8px; padding: 10px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: 13px; border-bottom-left-radius: 4px; }
.nv-fotolibre-buscador { display: flex; gap: 8px; }
.nv-fotolibre-buscador input { flex: 1; min-width: 0; padding: 8px 10px; background: none; border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); color: var(--nv-texto); font-size: 13px; }
.nv-fotolibre-buscador button { padding: 8px 14px; background: var(--nv-savia); color: #04140E; border: none; border-radius: var(--nv-r-sm); font-weight: 700; font-size: 13px; cursor: pointer; transition: var(--nv-dur-fast); }
.nv-fotolibre-buscador button:hover { filter: brightness(1.08); }
.nv-fotolibre-buscador button:disabled { opacity: .6; cursor: default; }
.nv-fotolibre-grilla { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; scroll-snap-type: x proximity; }
.nv-fotolibre-item { flex: 0 0 auto; scroll-snap-align: start; width: 128px; display: flex; flex-direction: column; gap: 4px; padding: 0; background: none; border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); overflow: hidden; cursor: pointer; text-align: left; transition: var(--nv-dur-fast); }
.nv-fotolibre-item:hover { border-color: var(--nv-savia); transform: translateY(-1px); }
.nv-fotolibre-item img { width: 128px; height: 88px; object-fit: cover; display: block; }
.nv-fotolibre-credito { display: block; padding: 3px 6px 5px; font-size: 10.5px; line-height: 1.25; color: var(--nv-tenue); }
.nv-fotolibre-vacio { margin: 0; padding: 6px 2px; font-size: 12.5px; color: var(--nv-tenue); }

/* ---------- Inbox / Negociación ---------- */
.nv-ibi { display: inline-flex; align-items: center; justify-content: center; }
.nv-ib { display: grid; grid-template-columns: minmax(260px, 340px) 1fr; gap: 14px; height: calc(100vh - 150px); min-height: 420px; }
.nv-ib-list { display: flex; flex-direction: column; min-height: 0; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); overflow: hidden; }
.nv-ib-search { display: flex; align-items: center; gap: 8px; padding: 11px 13px; border-bottom: 1px solid var(--nv-linea); color: var(--nv-tenue); }
.nv-ib-search input { flex: 1; background: none; border: none; outline: none; color: var(--nv-texto); font-size: 13.5px; }
.nv-ib-seg { margin: 11px 11px 6px; align-self: flex-start; }
.nv-ib-rows { flex: 1; overflow-y: auto; padding: 6px; display: flex; flex-direction: column; gap: 4px; }
.nv-ib-row { display: flex; gap: 10px; align-items: flex-start; text-align: left; width: 100%; padding: 11px 10px; border: 1px solid transparent; border-radius: var(--nv-r-sm); background: none; cursor: pointer; color: var(--nv-texto); transition: var(--nv-dur-fast); }
.nv-ib-row:hover { background: color-mix(in srgb, var(--nv-savia) 7%, transparent); }
.nv-ib-row.on { background: color-mix(in srgb, var(--nv-savia) 12%, transparent); border-color: color-mix(in srgb, var(--nv-savia) 40%, transparent); }
.nv-ib-av { width: 38px; height: 38px; flex-shrink: 0; border-radius: 50%; background: var(--nv-grad); color: #04140E; font-weight: 800; font-size: 15px; display: grid; place-items: center; }
.nv-ib-rmid { flex: 1; min-width: 0; }
.nv-ib-rtop { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.nv-ib-who { font-size: 13.5px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-ib-time { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue); flex-shrink: 0; }
.nv-ib-list-title { font-size: 11.5px; color: var(--nv-cosecha); margin: 1px 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-ib-preview { font-size: 12px; color: var(--nv-tenue); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-ib-rtail { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; flex-shrink: 0; }
.nv-ib-unread { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--nv-savia); color: #04140E; font-size: 11px; font-weight: 800; display: grid; place-items: center; }
.nv-ib-badge.deal { color: var(--nv-savia-hi); display: inline-flex; }
.nv-ib-empty { text-align: center; color: var(--nv-tenue); padding: 40px 20px; display: flex; flex-direction: column; align-items: center; gap: 12px; font-size: 13px; }

.nv-ib-chat { display: flex; flex-direction: column; min-height: 0; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); overflow: hidden; }
.nv-ib-empty-chat { margin: auto; }
.nv-ib-loading { margin: auto; color: var(--nv-tenue); font-family: var(--nv-f-mono); }
.nv-ib-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--nv-linea); }
.nv-ib-back { display: none; background: none; border: none; color: var(--nv-tenue); cursor: pointer; padding: 2px; }
.nv-ib-hinfo { flex: 1; min-width: 0; }
.nv-ib-hwho { font-size: 14px; font-weight: 700; }
.nv-ib-hlisting { font-size: 11.5px; color: var(--nv-cosecha); }
.nv-ib-htrust { display: inline-flex; align-items: center; gap: 2px; font-family: var(--nv-f-mono); font-size: 11.5px; color: var(--nv-dorado); flex-shrink: 0; }
.nv-ib-deal { display: flex; align-items: center; gap: 16px; padding: 11px 14px; background: var(--nv-suelo); border-bottom: 1px solid var(--nv-linea); flex-wrap: wrap; }
.nv-ib-deal.agreed { background: color-mix(in srgb, var(--nv-savia) 10%, var(--nv-suelo)); }
.nv-ib-plabel { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-ib-pval { font-size: 15px; font-weight: 700; }
.nv-ib-price.accent .nv-ib-pval { color: var(--nv-cosecha); }
.nv-ib-status { margin-left: auto; font-size: 11px; font-weight: 700; padding: 4px 10px; border-radius: 999px; }
.nv-ib-status.open { color: var(--nv-cielo); background: color-mix(in srgb, var(--nv-cielo) 14%, transparent); }
.nv-ib-status.deal { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 16%, transparent); }
.nv-ib-status.closed { color: var(--nv-tenue); background: color-mix(in srgb, var(--nv-tenue) 14%, transparent); }
.nv-ib-msgs { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 9px; }
.nv-ib-b { max-width: 76%; padding: 9px 12px 6px; border-radius: 13px; font-size: 13.5px; line-height: 1.45; }
.nv-ib-b.them { background: var(--nv-suelo); border: 1px solid var(--nv-linea); align-self: flex-start; border-bottom-left-radius: 4px; }
.nv-ib-b.me { background: var(--nv-savia); color: #04140E; align-self: flex-end; border-bottom-right-radius: 4px; }
.nv-ib-otag { display: inline-flex; align-items: center; gap: 3px; font-family: var(--nv-f-mono); font-size: 12px; font-weight: 700; margin-top: 3px; padding: 3px 8px; border-radius: 8px; background: rgba(4,20,14,.14); }
.nv-ib-b.them .nv-ib-otag { color: var(--nv-cosecha); background: color-mix(in srgb, var(--nv-cosecha) 14%, transparent); }
.nv-ib-btime { display: block; font-family: var(--nv-f-mono); font-size: 9.5px; opacity: .65; margin-top: 3px; text-align: right; }
.nv-ib-actions { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--nv-linea); }
.nv-ib-abtn { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 10px 12px; font-size: 12.5px; }
.nv-ib-counter { padding: 12px 14px; border-top: 1px solid var(--nv-linea); background: var(--nv-suelo); }
.nv-ib-clabel { font-size: 11px; color: var(--nv-tenue); margin-bottom: 7px; }
.nv-ib-crow { display: flex; gap: 8px; }
.nv-ib-cnum { flex: 1; background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-sm); padding: 10px 12px; color: var(--nv-texto); font-family: var(--nv-f-mono); font-size: 14px; outline: none; }
.nv-ib-csend { padding: 10px 16px; white-space: nowrap; }
/* El composer de Inbox migró a `NVComposer` (2026-08-21): el layout (campo
   arriba, herramientas + enviar debajo) y los colores ya los pone `.nv-composer`
   en composer.js. Aquí solo queda el encaje con el panel de chat — sin
   `display:flex` propio, que pisaba el de `.nv-composer` por venir después en
   la hoja (misma especificidad, gana el que está más abajo). `.nv-ib-input`/
   `.nv-ib-send` ya no los pinta ningún módulo — el campo y el botón de enviar
   ahora son `.nv-comp-campo`/`.nv-send`, definidos junto a `.nv-composer`. */
.nv-ib-composer { border-top: 1px solid var(--nv-linea); border-radius: 0; }
.nv-ib-godeal { margin: 12px 14px; }
.nv-ib-ok { display: flex; align-items: center; gap: 6px; margin: 0 14px 12px; font-size: 12.5px; color: var(--nv-savia-hi); }

@media (max-width: 720px) {
  .nv-ib { grid-template-columns: 1fr; height: calc(100vh - 140px); }
  .nv-ib-chat { display: none; }
  .nv-ib.detail .nv-ib-list { display: none; }
  .nv-ib.detail .nv-ib-chat { display: flex; }
  .nv-ib-back { display: inline-flex; }
}

/* ---------- Deal Room (P1.12) ----------
   Misma rejilla de dos paneles que el Inbox (lista + detalle, y en móvil uno u
   otro): es la continuación natural de la misma conversación, no otra app. */
.nv-dl { display: grid; grid-template-columns: minmax(240px, 320px) 1fr; gap: 14px; height: calc(100vh - 150px); min-height: 420px; }
.nv-dl-list { display: flex; flex-direction: column; min-height: 0; overflow-y: auto; padding: 6px; gap: 4px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-dl-ltitle { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--nv-tenue); padding: 8px 8px 4px; }
.nv-dl-row { display: flex; gap: 10px; align-items: center; text-align: left; width: 100%; padding: 11px 10px; border: 1px solid transparent; border-radius: var(--nv-r-sm); background: none; cursor: pointer; color: var(--nv-texto); transition: var(--nv-dur-fast); }
.nv-dl-row:hover { background: color-mix(in srgb, var(--nv-savia) 7%, transparent); }
.nv-dl-row.on { background: color-mix(in srgb, var(--nv-savia) 12%, transparent); border-color: color-mix(in srgb, var(--nv-savia) 40%, transparent); }
.nv-dl-av { width: 34px; height: 34px; flex-shrink: 0; border-radius: 50%; background: var(--nv-grad); color: #04140E; font-weight: 800; font-size: 14px; display: grid; place-items: center; }
.nv-dl-rmid { flex: 1; min-width: 0; }
.nv-dl-who { font-size: 13.5px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-dl-listing { font-size: 11.5px; color: var(--nv-cosecha); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-dl-badge { font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; flex-shrink: 0; white-space: nowrap; }
.nv-dl-badge.match { color: var(--nv-cielo); background: color-mix(in srgb, var(--nv-cielo) 14%, transparent); }
.nv-dl-badge.deal_room { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 16%, transparent); }
.nv-dl-badge.cerrado { color: var(--nv-tenue); background: color-mix(in srgb, var(--nv-tenue) 14%, transparent); }
.nv-dl-sala { display: flex; flex-direction: column; min-height: 0; overflow-y: auto; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-dl-loading { margin: auto; color: var(--nv-tenue); font-family: var(--nv-f-mono); padding: 24px; }
.nv-dl-empty { text-align: center; color: var(--nv-tenue); padding: 40px 20px; display: flex; flex-direction: column; align-items: center; gap: 12px; font-size: 13px; }
.nv-dl-empty-sala { margin: auto; }
.nv-dl-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--nv-linea); position: sticky; top: 0; background: var(--nv-superficie); z-index: 2; }
.nv-dl-back { display: none; background: none; border: none; color: var(--nv-tenue); cursor: pointer; padding: 2px; }
.nv-dl-hinfo { flex: 1; min-width: 0; }
.nv-dl-hwho { font-size: 14px; font-weight: 700; }
.nv-dl-hlisting { font-size: 11.5px; color: var(--nv-cosecha); }
.nv-dl-card { margin: 12px 14px; padding: 14px; background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-dl-ctitle { display: flex; align-items: center; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--nv-tenue); margin-bottom: 10px; }
.nv-dl-sub { font-size: 12px; color: var(--nv-tenue); margin: -6px 0 12px; line-height: 1.45; }
.nv-dl-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; }
.nv-dl-f { display: flex; flex-direction: column; gap: 4px; }
.nv-dl-flb { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-dl-in { background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-sm); padding: 9px 11px; color: var(--nv-texto); font-family: var(--nv-f-mono); font-size: 13.5px; outline: none; width: 100%; }
.nv-dl-in:focus { border-color: var(--nv-savia); }
.nv-dl-in:disabled { opacity: .6; }
.nv-dl-notas { width: 100%; margin-top: 10px; min-height: 54px; background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-sm); padding: 10px 12px; color: var(--nv-texto); font: 400 13px/1.45 var(--nv-f-body); outline: none; resize: vertical; }
.nv-dl-total { display: flex; align-items: baseline; gap: 8px; margin-top: 12px; font-size: 17px; }
.nv-dl-tlb { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-dl-conf { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 12px; }
.nv-dl-cpill { font-size: 11px; padding: 4px 10px; border-radius: 999px; color: var(--nv-tenue); background: color-mix(in srgb, var(--nv-tenue) 12%, transparent); }
.nv-dl-cpill.on { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 16%, transparent); }
.nv-dl-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; }
.nv-dl-acts .nv-btn { width: auto; flex: 0 0 auto; padding: 9px 15px; font-size: 12.5px; }
.nv-dl-hint { font-size: 11.5px; color: var(--nv-tenue); }
.nv-dl-donde { font-size: 13.5px; font-weight: 600; }
.nv-dl-coord { font-size: 11.5px; color: var(--nv-tenue); margin-top: 2px; }
.nv-dl-btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 11px; }
.nv-dl-b { width: auto; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 8px 13px; font-size: 12.5px; text-decoration: none; }
/* CATEGORÍA + TARJETA DE PRESTADOR (rediseño 2026-08-21, reportado por Daniel:
   "se ve tirado"). Antes era una fila pelada con un `border-top` de divisor —
   ni fondo, ni borde, ni radio: el prestador y su precio se leían igual que
   una línea de log. Ahora cada prestador es su propia tarjeta, con el mismo
   fondo/borde/sombra que el resto de tarjetas de la app (`--nv-superficie`,
   `--nv-linea`, `--nv-r-md`, `--nv-sh-1`) y el precio destacado en vez de
   perdido dentro del texto gris. */
.nv-dl-cat { font: 700 11px/1 var(--nv-f-mono); letter-spacing: .06em; text-transform: uppercase;
  color: var(--nv-savia); margin: 18px 0 9px; }
.nv-dl-cat:first-child { margin-top: 0; }
.nv-dl-prov { display: flex; align-items: center; gap: 12px; padding: 12px 13px; margin-bottom: 8px;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md);
  box-shadow: var(--nv-sh-1); transition: var(--nv-dur-fast); }
.nv-dl-prov:hover { border-color: var(--nv-linea-fuerte); }
.nv-dl-pinfo { flex: 1 1 140px; min-width: 0; }
.nv-dl-ptitle { font-size: 13.5px; font-weight: 700; line-height: 1.3; text-wrap: balance; }
.nv-dl-pmeta { font-size: 11.5px; color: var(--nv-tenue); margin-top: 3px; }
.nv-dl-pb { width: auto; flex: 0 0 auto; padding: 9px 15px; font-size: 12.5px; }
.nv-dl-vacio { text-align: center; color: var(--nv-tenue); font-size: 13px; display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 10px 0; }
.nv-dl-diag { font-size: 11.5px; color: var(--nv-tenue); line-height: 1.5; }
.nv-dl-aviso { margin: 12px 14px 0; padding: 10px 12px; border-radius: var(--nv-r-sm); font-size: 12.5px; line-height: 1.45; }
.nv-dl-aviso.err { color: var(--nv-alerta, #E5534B); background: color-mix(in srgb, var(--nv-alerta, #E5534B) 12%, transparent); }
.nv-dl-aviso.ok { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }

@media (max-width: 720px) {
  .nv-dl { grid-template-columns: 1fr; height: calc(100vh - 140px); }
  .nv-dl-sala { display: none; }
  .nv-dl.detail .nv-dl-list { display: none; }
  .nv-dl.detail .nv-dl-sala { display: flex; }
  .nv-dl-back { display: inline-flex; }
}

/* ---------- Unidad Operativa ---------- */
.nv-uni { display: inline-flex; align-items: center; justify-content: center; }
.nv-un-loading { color: var(--nv-tenue); font-family: var(--nv-f-mono); padding: 30px; text-align: center; }
.nv-un-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 18px 0 6px; }
.nv-un-head .nv-fam { margin: 0; }
.nv-un-add { width: auto; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 8px 13px; font-size: 12.5px; }
.nv-un-empty { text-align: center; color: var(--nv-tenue); padding: 40px 20px; display: flex; flex-direction: column; align-items: center; gap: 12px; font-size: 13px; }
.nv-un-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
.nv-un-card { text-align: left; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; cursor: pointer; color: var(--nv-texto); box-shadow: var(--nv-sh-1); transition: var(--nv-dur-fast); }
.nv-un-card:hover { border-color: var(--nv-linea-fuerte); transform: translateY(-2px); }
.nv-un-ctop { display: flex; gap: 11px; align-items: center; }
.nv-un-cic { width: 40px; height: 40px; flex-shrink: 0; border-radius: 11px; background: color-mix(in srgb, var(--nv-savia) 16%, transparent); display: grid; place-items: center; }
.nv-un-cinfo { min-width: 0; }
.nv-un-cname { font-size: 14.5px; font-weight: 700; }
.nv-un-ccrop { font-size: 12px; color: var(--nv-tenue); }
.nv-un-stage { margin: 14px 0 4px; }
.nv-un-stagelab { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--nv-tenue); margin-bottom: 5px; }
.nv-un-stagelab .nv-mono { color: var(--nv-cosecha); }
.nv-un-bar { height: 7px; background: var(--nv-suelo); border-radius: 999px; overflow: hidden; }
.nv-un-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--nv-cosecha), var(--nv-savia)); }
.nv-un-cfoot { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-top: 11px; }
.nv-un-cyc { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; color: var(--nv-tenue); }
.nv-un-cacts { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-savia-hi); }

.nv-un-dhead { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.nv-un-back { background: none; border: none; color: var(--nv-tenue); cursor: pointer; padding: 2px; display: inline-flex; }
.nv-un-dname { font-size: 16px; font-weight: 800; }
.nv-un-dcrop { font-size: 12px; color: var(--nv-tenue); }
.nv-un-dcard { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; }
.nv-un-drow { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.nv-un-dstage { font-size: 14px; font-weight: 700; color: var(--nv-savia-hi); }
.nv-un-dday { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--nv-tenue); }
.nv-un-dstagelab { display: flex; align-items: baseline; gap: 8px; margin-top: 7px; }
.nv-un-dstagelab .nv-mono { font-size: 15px; color: var(--nv-cosecha); font-weight: 700; }
.nv-un-dstagek { font-size: 11px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }

.nv-un-log { background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 14px; margin: 16px 0; }
.nv-un-logk { font-size: 12.5px; font-weight: 700; margin-bottom: 10px; }
.nv-un-chips { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 11px; }
.nv-un-chip { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; padding: 7px 11px; border-radius: var(--nv-r-pill); border: 1px solid var(--nv-linea-fuerte); background: none; color: var(--nv-tenue); cursor: pointer; transition: var(--nv-dur-fast); }
.nv-un-chip.on { background: color-mix(in srgb, var(--nv-savia) 15%, transparent); color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-un-logrow { display: flex; gap: 7px; align-items: flex-end; }
.nv-un-tools { display: flex; gap: 7px; align-items: center; flex-shrink: 0; padding-bottom: 1px; }
.nv-un-tool { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; border: 1px solid var(--nv-linea-fuerte); background: none; cursor: pointer; display: grid; place-items: center; }
.nv-un-tool:hover { border-color: var(--nv-savia); }
.nv-un-input { flex: 1; min-width: 90px; min-height: 40px; max-height: 156px; background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: 14px; padding: 10px 13px; color: var(--nv-texto); font: 400 14px/1.45 var(--nv-f-body); outline: none; resize: none; overflow-y: auto; }
.nv-un-input:focus { border-color: var(--nv-savia); }
.nv-un-save { width: auto; flex: 0 0 auto; align-self: flex-end; display: inline-flex; align-items: center; gap: 5px; padding: 0 14px; height: 40px; font-size: 12.5px; white-space: nowrap; }
.nv-un-ok { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 12.5px; color: var(--nv-savia-hi); }
/* Contraparte del "listo": si la actividad NO se guardó, se dice. */
.nv-un-err { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 12.5px; color: var(--nv-alerta, #E5534B); }

.nv-un-timeline { display: flex; flex-direction: column; gap: 2px; }
.nv-un-act { display: flex; gap: 12px; padding: 12px 4px; position: relative; }
.nv-un-act:not(:last-child)::before { content: ''; position: absolute; left: 18px; top: 40px; bottom: -2px; width: 2px; background: var(--nv-linea); }
.nv-un-adot { width: 36px; height: 36px; flex-shrink: 0; border-radius: 50%; display: grid; place-items: center; background: color-mix(in srgb, var(--nv-savia) 14%, var(--nv-superficie)); border: 1px solid var(--nv-linea); z-index: 1; }
.nv-un-adot.nv-t-riego { background: color-mix(in srgb, var(--nv-cielo) 16%, var(--nv-superficie)); }
.nv-un-adot.nv-t-cosecha { background: color-mix(in srgb, var(--nv-dorado) 16%, var(--nv-superficie)); }
.nv-un-abody { flex: 1; min-width: 0; padding-top: 2px; }
.nv-un-atop { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.nv-un-atype { font-size: 13px; font-weight: 700; }
.nv-un-atime { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue); flex-shrink: 0; }
.nv-un-anote { font-size: 12.5px; color: var(--nv-tenue); margin-top: 2px; }

.nv-un-cres { margin-top: 6px; padding: 8px 10px; border-radius: var(--nv-r-sm, 8px); background: color-mix(in srgb, var(--nv-dorado) 8%, transparent); border: 1px solid color-mix(in srgb, var(--nv-dorado) 22%, var(--nv-linea)); }
.nv-un-cresl { font-size: 12.5px; color: var(--nv-texto); }
.nv-un-cresl:not(:first-child) { margin-top: 3px; }
.nv-un-cresmini { color: var(--nv-tenue); font-size: 12px; }
.nv-un-cresbad { color: var(--nv-alerta, #E5534B); }

.nv-un-cosbox { margin: 10px 0 12px; padding-top: 10px; border-top: 1px dashed var(--nv-linea); }
.nv-un-cosrow { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 8px; }
.nv-un-cosrow3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; margin-bottom: 8px; }
.nv-un-cosdet { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
.nv-un-costoggle { background: none; border: none; color: var(--nv-savia); font-size: 12px; font-weight: 600; cursor: pointer; padding: 2px 0; text-decoration: underline; text-underline-offset: 2px; }

.nv-un-pccard { margin-top: 12px; display: flex; flex-direction: column; gap: 10px; }
.nv-un-pchead { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 700; color: var(--nv-tenue); }
.nv-un-pcstep { padding-top: 10px; border-top: 1px solid var(--nv-linea); }
.nv-un-pcstep:first-of-type { padding-top: 0; border-top: none; }
.nv-un-pcstepk { font-size: 13px; font-weight: 700; margin-bottom: 4px; }
.nv-un-pcchips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 4px; }
.nv-un-pcchip { font-size: 11.5px; font-family: var(--nv-f-mono); background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-pill); padding: 3px 9px; color: var(--nv-texto); }
.nv-un-pcnota { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.4; }
.nv-un-pcbaja { font-size: 11.5px; color: var(--nv-tenue); font-style: italic; padding-top: 4px; }

@media (max-width: 720px) {
  .nv-un-logrow { flex-wrap: wrap; align-items: center; }
  .nv-un-input { min-width: 100%; order: -1; }
  .nv-un-tools { flex: 1; }
  .nv-un-save { margin-left: auto; }
}

/* ---------- IA Agronómica ---------- */
.nv-agi { display: inline-flex; align-items: center; justify-content: center; }
.nv-ag-intro { display: flex; gap: 10px; align-items: flex-start; background: color-mix(in srgb, var(--nv-savia) 8%, var(--nv-superficie)); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 12px 14px; font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; }
.nv-ag-intro p { flex: 1; }
/* `.nv-ag-plothint` (agro_lab.js, aviso «sin parcela» del diagnóstico) — cero
   reglas, mismo aspecto que `.nv-ag-intro` de arriba pero en columna: lleva
   el texto y, debajo, el botón «Crear parcela». */
.nv-ag-plothint { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; background: color-mix(in srgb, var(--nv-savia) 8%, var(--nv-superficie)); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 12px 14px; margin-bottom: 14px; }
.nv-ag-plothintx { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; }
.nv-ag-form { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; margin: 14px 0; }
.nv-ag-lbl { font-size: 11.5px; font-weight: 700; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 9px; }
.nv-ag-types { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; margin-bottom: 15px; }
.nv-ag-type { display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 11px 10px; border-radius: var(--nv-r-sm); border: 1px solid var(--nv-linea-fuerte); background: var(--nv-suelo); color: var(--nv-tenue); font-size: 13px; font-weight: 600; cursor: pointer; transition: var(--nv-dur-fast); }
.nv-ag-type:hover { border-color: var(--nv-savia); }
.nv-ag-type.on { background: color-mix(in srgb, var(--nv-savia) 15%, transparent); color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-ag-select { width: 100%; background: var(--nv-suelo); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-sm); padding: 11px 13px; color: var(--nv-texto); font-size: 13.5px; outline: none; margin-bottom: 14px; cursor: pointer; }
.nv-ag-select:focus { border-color: var(--nv-savia); }
.nv-ag-comp { margin-bottom: 12px; }
.nv-ag-run { width: 100%; }

.nv-ag-loading { display: flex; align-items: center; gap: 9px; padding: 22px; justify-content: center; color: var(--nv-savia-hi); font-size: 13px; font-family: var(--nv-f-mono); }
.nv-ag-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-savia); border-radius: var(--nv-r-md); padding: 16px; margin-bottom: 16px; }
.nv-ag-card.sev-medio { border-left-color: var(--nv-dorado); }
.nv-ag-card.sev-alto { border-left-color: var(--nv-critico); }
.nv-ag-rhead { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
.nv-ag-ric { width: 40px; height: 40px; flex-shrink: 0; border-radius: 11px; background: color-mix(in srgb, var(--nv-savia) 15%, transparent); display: grid; place-items: center; }
.nv-ag-rinfo { flex: 1; min-width: 0; }
.nv-ag-rtitle { font-size: 15px; font-weight: 800; }
.nv-ag-rplot { font-size: 12px; color: var(--nv-cosecha); }
.nv-ag-sev { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700; padding: 5px 10px; border-radius: 999px; flex-shrink: 0; }
.nv-ag-sev.sev-bajo { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 15%, transparent); }
.nv-ag-sev.sev-medio { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 15%, transparent); }
.nv-ag-sev.sev-alto { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 15%, transparent); }
.nv-ag-summary { font-size: 13.5px; line-height: 1.5; color: var(--nv-texto); margin-bottom: 14px; }
.nv-ag-sec { font-size: 11px; font-weight: 700; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .05em; margin: 14px 0 8px; }
.nv-ag-findings { display: flex; flex-direction: column; gap: 7px; }
.nv-ag-find { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 9px 11px; background: var(--nv-suelo); border-radius: var(--nv-r-sm); }
.nv-ag-fk { font-size: 12.5px; font-weight: 700; min-width: 96px; }
.nv-ag-fv { font-size: 12.5px; color: var(--nv-cosecha); }
.nv-ag-fn { font-size: 12px; color: var(--nv-tenue); flex: 1; min-width: 120px; }
.nv-ag-recs { display: flex; flex-direction: column; gap: 8px; }
.nv-ag-rec { display: flex; align-items: center; gap: 9px; padding: 10px 11px; background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-ag-recbullet { flex-shrink: 0; display: inline-flex; }
.nv-ag-rectext { flex: 1; font-size: 12.5px; line-height: 1.4; }
.nv-ag-recgo { display: inline-flex; align-items: center; gap: 3px; flex-shrink: 0; font-size: 11.5px; font-weight: 700; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 12%, transparent); border: 1px solid color-mix(in srgb, var(--nv-savia) 40%, transparent); border-radius: var(--nv-r-pill); padding: 6px 11px; cursor: pointer; transition: var(--nv-dur-fast); }
.nv-ag-recgo:hover { background: color-mix(in srgb, var(--nv-savia) 22%, transparent); }
.nv-ag-recgo svg { transform: rotate(180deg); }
.nv-ag-model { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue); margin-top: 13px; text-align: right; }

/* EL PRECIO, EN LETRA PEQUEÑA. Pedido del dueño el 2026-08-08: «No pongas que
   cuesta 20 tokens [en grande]. Pon algo en letra pequeña».

   Y NO ES QUE ALGUIEN LO PUSIERA GRANDE A PROPOSITO: `.nv-ag-tokens` NO TENIA
   NI UNA REGLA EN ESTE ARCHIVO. Seis pantallas la pintan (`agro_lab`,
   `agro_reco`, `agro_sat`, `agro_sow`, `crop`, y `assets` con su gemela) y
   todas salian con el tamaño de un parrafo normal, en negro, al lado del boton.
   El «Cuesta 20 tokens» gritado que el dueño fotografio era eso: un `<p>` sin
   estilo. Una regla lo arregla en las seis a la vez.

   ⚠️ SE HACE PEQUEÑO, NO SE HACE DESAPARECER. La regla de la casa es «un botón
   que cuesta se pulsa una vez si no avisa»: el precio se dice ANTES de pulsar,
   siempre. Por eso NO lleva `display:none` en movil ni se esconde tras un
   `title` —en un telefono no hay raton que enseñe un `title`— y el color es
   `--nv-tenue`, que es el mismo del resto de notas al pie y sigue pasando
   contraste. Pequeño es discreto; ausente es una factura sorpresa. */
.nv-ag-tokens { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue);
  margin-top: 9px; text-align: center; letter-spacing: .01em; }

/* EL CONTADOR DE LO GASTADO — «como Claude, que le muestra a uno los tokens y
   es súper rápido». Lo pinta `NVTokens` en app.js.

   Va FIJO y por encima de todo porque la respuesta que acaba de costar tokens
   suele repintar el panel entero: un contador dentro del panel se iria con el
   repintado justo en el momento en que hay algo que contar. `bottom` respeta
   `safe-area-inset` para no quedar debajo de la barra del iPhone, y
   `pointer-events: none` para que nunca tape un boton — es un aviso, no un
   control. Aparece, se lee y se va; el JS le quita la clase a los 6 segundos.
   `prefers-reduced-motion` lo deja aparecer sin deslizarse. */
.nv-tkhud { position: fixed; z-index: 60; right: 12px;
  bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  display: inline-flex; align-items: center; gap: 6px; pointer-events: none;
  font-family: var(--nv-f-mono); font-size: 11px; font-weight: 700;
  color: var(--nv-savia-hi); background: var(--nv-superficie);
  border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-pill);
  padding: 6px 11px; box-shadow: var(--nv-sh-1);
  opacity: 0; transform: translateY(6px); transition: opacity .18s ease, transform .18s ease; }
.nv-tkhud.nv-tkhud-on { opacity: 1; transform: translateY(0); }
.nv-tkhud-ic { opacity: .7; }
@media (prefers-reduced-motion: reduce) {
  .nv-tkhud { transition: opacity .18s ease; transform: none; }
  .nv-tkhud.nv-tkhud-on { transform: none; }
}

.nv-ag-empty { color: var(--nv-tenue); font-size: 13px; padding: 20px; text-align: center; }
/* `.nv-ag-vaciok`/`.nv-ag-vaciod` (título/motivo de `vacio()`, compartido por
   los 7 módulos vía `ctx.vacio()`) no tenían ni una regla — heredaban el
   13px/tenue de `.nv-ag-empty` sin distinguirse entre sí. Mismos valores que
   `.nv-co-vacio-t`/`.nv-co-vacio-d` (Comunidad), que ya resuelve el mismo
   patrón título+motivo en una caja vacía. */
.nv-ag-vaciok { font-size: 14px; font-weight: 700; color: var(--nv-texto); margin-top: 2px; }
.nv-ag-vaciod { font-size: 13px; color: var(--nv-tenue); line-height: 1.5; margin-top: 4px; }

/* Fallo VISIBLE (NVErr). Dos colores a propósito: ámbar = te quedaste sin saldo
   (estado del usuario, se arregla mañana solo); rojo = algo se rompió de verdad.
   Pintarlos igual haría que llame a soporte por una cuota que se renueva. */
.nv-err { display: flex; flex-direction: column; gap: 7px; padding: 16px 18px; margin: 4px 0;
  background: var(--nv-superficie); border: 1px solid var(--nv-critico);
  border-left-width: 3px; border-radius: var(--nv-r-sm); text-align: left; }
.nv-err.is-tokens { border-color: var(--nv-alerta); }
.nv-err-h { display: flex; align-items: center; gap: 8px; }
/* NVIcon escribe el color DENTRO del SVG (stroke="…" fijo, nunca currentColor),
   así que `color:` sobre el contenedor no hace nada: había que pisar el atributo
   de presentación, que en CSS pierde contra cualquier regla. Sin esto el icono
   salía verde savia en los dos estados y el aviso de "sin saldo" no se
   distinguía de un error de verdad más que por el borde. */
.nv-err-i { display: inline-flex; }
.nv-err-i svg [stroke], .nv-err-i svg[stroke] { stroke: var(--nv-critico); }
.nv-err.is-tokens .nv-err-i svg [stroke], .nv-err.is-tokens .nv-err-i svg[stroke] { stroke: var(--nv-alerta); }
.nv-err-t { font-weight: 700; font-size: 13.5px; color: var(--nv-texto); }
.nv-err-d { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.5; margin: 0; }
/* min-height 44px: el botón medía 30px de alto (hallazgo de compra-curso-front,
   2026-08-11) y 44 es el mínimo táctil — en un cartel de error/pago, fallar el
   dedo sobre "Comprar"/"Reintentar" es exactamente donde más duele. */
.nv-err-b { width: auto; align-self: flex-start; padding: 8px 14px; font-size: 12.5px; margin-top: 3px; min-height: 44px; }

/* P3 H5 — el aviso de regreso del checkout de Bold.
   Flotante y por encima de todo a propósito: el productor vuelve de pagar y lo
   PRIMERO que necesita saber es si su plata llegó. Enterrarlo dentro del panel
   lo dejaría compitiendo con el contenido de la pantalla que estuviera abierta.
   Tres colores para tres situaciones distintas, porque "ya está" y "pagaste
   pero no pudimos activarlo" no se pueden ver igual. */
.nv-pago-aviso { position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px;
  z-index: 60; max-width: min(520px, calc(100vw - 24px));
  display: flex; align-items: flex-start; gap: 10px; padding: 14px 16px;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-left-width: 3px; border-radius: var(--nv-r-sm);
  box-shadow: 0 8px 28px rgba(0,0,0,.16); text-align: left; }
.nv-pago-aviso strong { font-size: 13.5px; color: var(--nv-texto); }
.nv-pago-aviso p { margin: 4px 0 0; font-size: 12.5px; color: var(--nv-tenue); line-height: 1.5; }
.nv-pago-aviso > button { margin-left: auto; flex-shrink: 0; padding: 2px 8px; font-size: 14px; line-height: 1; }
.nv-pago-aviso.is-ok { border-left-color: var(--nv-savia); }
.nv-pago-aviso.is-warn { border-left-color: var(--nv-alerta); }
.nv-pago-aviso.is-info { border-left-color: var(--nv-tenue); }
/* La pista de "pagar no te verifica" va debajo del botón y en pequeño: es una
   aclaración, no una advertencia — pintarla como alerta asustaría sin motivo. */
.nv-pf-callhint { font-size: 11.5px; opacity: .85; margin-top: 6px; }
.nv-ag-histlist { display: flex; flex-direction: column; gap: 7px; }
.nv-ag-hrow { display: flex; align-items: center; gap: 11px; text-align: left; width: 100%; padding: 11px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); cursor: default; color: var(--nv-texto); }
.nv-ag-hic { width: 34px; height: 34px; flex-shrink: 0; border-radius: 9px; background: color-mix(in srgb, var(--nv-savia) 12%, transparent); display: grid; place-items: center; }
.nv-ag-hic.sev-medio { background: color-mix(in srgb, var(--nv-dorado) 13%, transparent); }
.nv-ag-hic.sev-alto { background: color-mix(in srgb, var(--nv-critico) 13%, transparent); }
.nv-ag-hmid { flex: 1; min-width: 0; }
.nv-ag-htop { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.nv-ag-htitle { font-size: 13px; font-weight: 700; }
.nv-ag-htime { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue); flex-shrink: 0; }
.nv-ag-hsum { font-size: 12px; color: var(--nv-tenue); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-ag-hsev { flex-shrink: 0; font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.nv-ag-hsev.sev-bajo { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-ag-hsev.sev-medio { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 14%, transparent); }
.nv-ag-hsev.sev-alto { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 14%, transparent); }

@media (max-width: 720px) {
  .nv-ag-rec { flex-wrap: wrap; }
  .nv-ag-recgo { margin-left: 24px; }
}

/* ---------- IA Agronómica · HUB ---------- */
.nv-ag-hub { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; margin-bottom: 6px; }
.nv-ag-tool { text-align: left; display: flex; flex-direction: column; gap: 9px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; cursor: pointer; color: var(--nv-texto); box-shadow: var(--nv-sh-1); transition: var(--nv-dur-fast); }
.nv-ag-tool:hover { border-color: var(--nv-savia); transform: translateY(-2px); }
.nv-ag-tool.off { opacity: .62; cursor: default; }
.nv-ag-tool.off:hover { border-color: var(--nv-linea); transform: none; }
.nv-ag-toolic { width: 42px; height: 42px; border-radius: 11px; background: color-mix(in srgb, var(--nv-savia) 15%, transparent); display: grid; place-items: center; }
.nv-ag-toolbody { flex: 1; }
.nv-ag-toolname { font-size: 14px; font-weight: 700; }
.nv-ag-tooldesc { font-size: 12px; color: var(--nv-tenue); line-height: 1.4; margin-top: 3px; }
.nv-ag-toolcta { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 700; color: var(--nv-savia); }
.nv-ag-toolcta svg { transform: rotate(180deg); }

/* sub-vista: cabecera con volver */
.nv-ag-shead { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.nv-ag-back { background: none; border: none; color: var(--nv-tenue); cursor: pointer; padding: 2px; display: inline-flex; }
.nv-ag-sic { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; background: color-mix(in srgb, var(--nv-savia) 15%, transparent); display: grid; place-items: center; }
.nv-ag-sinfo { flex: 1; min-width: 0; }
.nv-ag-stitle { font-size: 16px; font-weight: 800; }
.nv-ag-sdesc { font-size: 12px; color: var(--nv-tenue); }

/* satélite */
/* LA BARRA ENVUELVE, Y NO ES ADORNO. Desde el hito 34 lleva DOS selectores
   (parcela y periodo) mas el boton: en una fila sola, a 320 px se sale de la
   pantalla. `flex-wrap` la deja caer a dos renglones en vez de desbordar, y el
   `min-width` impide que los selectores se aplasten hasta no poder leer el
   nombre de la parcela. Es el mismo fallo que los numeros de las tarjetas de
   Inicio, que se salian en 8 de 10 anchos: aqui se ataja antes de que aparezca. */
.nv-ag-satbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 14px; }
.nv-ag-satbar .nv-ag-select { margin-bottom: 0; flex: 1 1 auto; min-width: 130px; }
.nv-ag-import { width: auto; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 10px 14px; font-size: 12.5px; }
.nv-ag-idx { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.nv-ag-idxcard { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; }
.nv-ag-idxval { font-size: 30px; font-weight: 800; line-height: 1; }
.nv-ag-idxcard.lvl-high .nv-ag-idxval { color: var(--nv-savia-hi); }
.nv-ag-idxcard.lvl-mid .nv-ag-idxval { color: var(--nv-dorado); }
.nv-ag-idxcard.lvl-low .nv-ag-idxval { color: var(--nv-critico); }
.nv-ag-idxlab { font-size: 11.5px; color: var(--nv-tenue); margin: 6px 0 9px; }
.nv-ag-idxbar { height: 6px; background: var(--nv-suelo); border-radius: 999px; overflow: hidden; }
.nv-ag-idxfill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--nv-cosecha), var(--nv-savia)); }
.nv-ag-satsrc { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue); margin: 10px 0 4px; }
.nv-ag-zones { display: flex; flex-direction: column; gap: 7px; }
.nv-ag-zone { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-ag-zdot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; background: var(--nv-tenue); }
.nv-ag-zone.lvl-high .nv-ag-zdot { background: var(--nv-savia-hi); }
.nv-ag-zone.lvl-mid .nv-ag-zdot { background: var(--nv-dorado); }
.nv-ag-zone.lvl-low .nv-ag-zdot { background: var(--nv-critico); }
.nv-ag-zname { flex: 1; font-size: 13px; font-weight: 600; }
.nv-ag-zndvi { font-size: 13px; color: var(--nv-cosecha); }
.nv-ag-zlvl { font-size: 11px; color: var(--nv-tenue); min-width: 76px; text-align: right; }
.nv-ag-cross { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-top: 14px; padding: 12px 14px; background: color-mix(in srgb, var(--nv-critico) 9%, var(--nv-superficie)); border: 1px solid color-mix(in srgb, var(--nv-critico) 35%, transparent); border-radius: var(--nv-r-md); }
.nv-ag-crosstxt { flex: 1; min-width: 160px; font-size: 12.5px; line-height: 1.4; }

/* modal importar */
.nv-ag-modal { position: fixed; inset: 0; z-index: 90; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; padding: 20px; }
.nv-ag-modalcard { position: relative; width: 100%; max-width: 400px; background: var(--nv-suelo); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-lg); padding: 20px; display: flex; flex-direction: column; gap: 11px; box-shadow: var(--nv-sh-2); }
.nv-ag-modaltitle { font-size: 15px; font-weight: 800; padding-right: 24px; }
.nv-ag-modalbtn { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
/* `.nv-ag-modalmsg` empieza vacío (solo se llena al pulsar un botón) y
   `.nv-ag-modalcard` ya reparte `gap:11px` entre TODOS sus hijos directos:
   sin `:empty`, ese hueco de 11px salía igual antes de que hubiera nada que
   mostrar. Mismo patrón que `.nv-ag-inmsg:empty` un poco más arriba. */
.nv-ag-modalmsg:empty { display: none; }
/* La imagen real de satélite (2026-08-22), dentro de `.nv-ag-modalmsg`. */
.nv-ag-modalimg { width: 100%; border-radius: var(--nv-r-md); display: block; }
.nv-ag-modalnote { font-size: 11.5px; color: var(--nv-tenue); line-height: 1.45; margin-top: 2px; }
.nv-ag-modalx { position: absolute; top: 14px; right: 14px; background: none; border: none; color: var(--nv-tenue); font-size: 15px; cursor: pointer; }

@media (max-width: 720px) { .nv-ag-idx { grid-template-columns: 1fr; } }

/* guía calicata: pasos */
.nv-ag-guide { display: inline-flex; align-items: center; gap: 7px; margin-bottom: 12px; padding: 9px 13px; border-radius: var(--nv-r-pill); border: 1px solid var(--nv-linea-fuerte); background: var(--nv-suelo); color: var(--nv-savia); font-size: 12.5px; font-weight: 600; cursor: pointer; }
.nv-ag-guide:hover { border-color: var(--nv-savia); }
.nv-ag-steps { display: flex; flex-direction: column; gap: 10px; }
.nv-ag-step { display: flex; gap: 11px; align-items: flex-start; }
.nv-ag-stepn { width: 24px; height: 24px; flex-shrink: 0; border-radius: 50%; background: color-mix(in srgb, var(--nv-savia) 18%, transparent); color: var(--nv-savia); font-weight: 800; font-size: 12px; display: grid; place-items: center; }
.nv-ag-steptxt { flex: 1; font-size: 13px; line-height: 1.45; color: var(--nv-texto); }

/* Recomendación Inteligente */
.nv-ag-recoout { margin-top: 4px; }
.nv-ag-covchips { display: flex; flex-wrap: wrap; gap: 7px; }
.nv-ag-covchip { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 600; padding: 6px 11px; border-radius: var(--nv-r-pill); border: 1px solid var(--nv-linea-fuerte); background: var(--nv-suelo); color: var(--nv-texto); }
.nv-ag-covchip.sev-alto { border-color: color-mix(in srgb, var(--nv-critico) 45%, transparent); color: var(--nv-critico); }
.nv-ag-covchip.sev-medio { border-color: color-mix(in srgb, var(--nv-dorado) 45%, transparent); color: var(--nv-dorado); }
.nv-ag-covchip.sev-bajo { border-color: color-mix(in srgb, var(--nv-savia) 45%, transparent); color: var(--nv-savia-hi); }
.nv-ag-covchip.miss { opacity: .7; border-style: dashed; color: var(--nv-tenue); }
.nv-ag-covnote { font-size: 11.5px; color: var(--nv-tenue); margin-top: 7px; font-style: italic; }
.nv-ag-prio { display: flex; flex-direction: column; gap: 8px; }
.nv-ag-prow { display: flex; align-items: flex-start; gap: 11px; padding: 12px; background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-savia); border-radius: var(--nv-r-sm); }
.nv-ag-prow.sev-alto { border-left-color: var(--nv-critico); }
.nv-ag-prow.sev-medio { border-left-color: var(--nv-dorado); }
.nv-ag-prank { width: 24px; height: 24px; flex-shrink: 0; border-radius: 8px; background: color-mix(in srgb, var(--nv-savia) 16%, transparent); color: var(--nv-savia); font-weight: 800; font-size: 12.5px; display: grid; place-items: center; }
.nv-ag-pbody { flex: 1; min-width: 0; }
.nv-ag-ptext { font-size: 13px; font-weight: 600; line-height: 1.4; }
.nv-ag-pwhy { font-size: 11.5px; color: var(--nv-tenue); margin-top: 3px; line-height: 1.4; }
.nv-ag-refs { display: flex; flex-direction: column; gap: 6px; }
.nv-ag-ref { display: flex; align-items: center; gap: 10px; padding: 9px 11px; background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); text-decoration: none; color: var(--nv-texto); }
a.nv-ag-ref:hover { border-color: var(--nv-savia); }
.nv-ag-refbody { flex: 1; min-width: 0; }
.nv-ag-reftitle { font-size: 12.5px; font-weight: 600; line-height: 1.35; }
.nv-ag-refsrc { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-savia-hi); margin-top: 2px; }
.nv-ag-refnote { font-size: 11px; color: var(--nv-tenue); line-height: 1.45; margin-top: 9px; }

/* Insumos */
.nv-ag-inseg { margin-bottom: 14px; align-self: flex-start; }
.nv-ag-inlist { display: flex; flex-direction: column; gap: 8px; }
.nv-ag-incard { display: flex; align-items: center; gap: 11px; padding: 12px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-ag-inic { width: 36px; height: 36px; flex-shrink: 0; border-radius: 10px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-ag-inmid { flex: 1; min-width: 0; }
.nv-ag-inname { font-size: 13.5px; font-weight: 700; }
.nv-ag-inreason { font-size: 12px; color: var(--nv-tenue); line-height: 1.4; }
.nv-ag-inhave { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-size: 11.5px; font-weight: 700; color: var(--nv-savia-hi); }
.nv-ag-invlist { display: flex; flex-direction: column; gap: 7px; }
.nv-ag-invrow { display: flex; align-items: center; gap: 10px; padding: 11px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-ag-invname { flex: 1; font-size: 13px; font-weight: 600; }
.nv-ag-invqty { font-size: 12.5px; color: var(--nv-cosecha); }
.nv-ag-invst { font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; min-width: 66px; text-align: center; }
.nv-ag-invst.st-ok { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-ag-invst.st-low { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 14%, transparent); }
.nv-ag-invst.st-out { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 14%, transparent); }
/* Registro de insumos (Hito 6C). Mismo lenguaje del composer de la bitácora. */
.nv-ag-invdel { background: none; border: none; color: var(--nv-fantasma); cursor: pointer; width: 24px; height: 24px; font-size: 17px; line-height: 1; display: grid; place-items: center; border-radius: 6px; flex-shrink: 0; }
.nv-ag-invdel:hover { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 12%, transparent); }
.nv-ag-incomposer { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 12px; padding: 11px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-md); }
.nv-ag-inf { background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); padding: 9px 11px; color: var(--nv-texto); font: 400 13px/1.3 var(--nv-f-body); outline: none; min-width: 0; }
.nv-ag-inf:focus { border-color: var(--nv-savia); }
.nv-ag-inf::placeholder { color: var(--nv-fantasma); }
.nv-ag-inf-name { flex: 1 1 170px; }
.nv-ag-inf-qty, .nv-ag-inf-min { flex: 0 1 108px; }
.nv-ag-inf-unit { flex: 0 1 130px; }
.nv-ag-insave { width: auto; flex: 0 0 auto; padding: 10px 18px; }
.nv-ag-inmsg { flex: 1 1 100%; font-size: 12px; color: var(--nv-critico); }
.nv-ag-inmsg:empty { display: none; }
/* `.nv-ag-inprecio` — «lo que pagaron otros», dentro de `.nv-ag-inmid`. Cero
   reglas: mismo lenguaje de fila-de-dato que `.nv-ag-find` (fondo `--nv-suelo`,
   `--nv-r-sm`), pero apilada porque lleva línea + nota + fuente. */
.nv-ag-inprecio { margin-top: 8px; padding: 8px 10px; background: var(--nv-suelo); border-radius: var(--nv-r-sm); display: flex; flex-direction: column; gap: 3px; }
.nv-ag-inprecio-vacio { color: var(--nv-tenue); }
.nv-ag-inprlinea { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.nv-ag-inprtxt { font-size: 11.5px; color: var(--nv-tenue); }
.nv-ag-inprval { font-size: 13px; font-weight: 700; color: var(--nv-savia-hi); }
.nv-ag-inprnota { font-size: 10.5px; color: var(--nv-tenue); }
.nv-ag-inprfuente { font-size: 10px; color: var(--nv-fantasma); margin-top: 1px; }
/* `.nv-ag-incomp` — el checkbox «compartir con la comunidad» del composer de
   insumos. Cero reglas: sin esto, `<label>` + checkbox + texto caían en línea
   sin separación ni jerarquía título/motivo dentro de `.nv-ag-incomposer`. */
.nv-ag-incomp { display: flex; align-items: flex-start; gap: 9px; flex: 1 1 100%; cursor: pointer; padding: 2px 0; }
.nv-ag-incompbox { margin-top: 2px; flex-shrink: 0; accent-color: var(--nv-savia); cursor: pointer; }
.nv-ag-incomptxt { display: flex; flex-direction: column; gap: 2px; }
.nv-ag-incomptit { font-size: 12.5px; font-weight: 700; }
.nv-ag-incompd { font-size: 11px; color: var(--nv-tenue); line-height: 1.4; }

/* Qué sembrar */
.nv-ag-sowout { display: flex; flex-direction: column; gap: 11px; }
.nv-ag-sowcard { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-savia); border-radius: var(--nv-r-md); padding: 14px; }
.nv-ag-sowcard.lvl-mid { border-left-color: var(--nv-dorado); }
.nv-ag-sowcard.lvl-low { border-left-color: var(--nv-tenue); }
.nv-ag-sowtop { display: flex; align-items: center; gap: 11px; }
.nv-ag-sowic { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-ag-sowinfo { flex: 1; min-width: 0; }
.nv-ag-sowname { font-size: 15px; font-weight: 800; }
.nv-ag-sowseason { font-size: 11.5px; color: var(--nv-cosecha); }
.nv-ag-sowfit { text-align: right; flex-shrink: 0; }
.nv-ag-sowfitval { font-size: 18px; font-weight: 800; color: var(--nv-savia-hi); }
.nv-ag-sowcard.lvl-mid .nv-ag-sowfitval { color: var(--nv-dorado); }
.nv-ag-sowcard.lvl-low .nv-ag-sowfitval { color: var(--nv-tenue); }
.nv-ag-sowfitlab { font-size: 10px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-ag-sowbar { height: 6px; background: var(--nv-suelo); border-radius: 999px; overflow: hidden; margin: 11px 0; }
.nv-ag-sowfill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--nv-cosecha), var(--nv-savia)); }
.nv-ag-sowwhy { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; }
.nv-ag-sowgo { margin-top: 11px; }

/* ---------- Monitoreo del cultivo ---------- */
.nv-cri { display: inline-flex; align-items: center; justify-content: center; }
.nv-cr-top { display: grid; grid-template-columns: 1fr 1.2fr; gap: 14px; margin-bottom: 4px; }
.nv-cr-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; }
.nv-cr-cardk { font-size: 11px; font-weight: 700; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 12px; }
.nv-cr-phenobig { display: flex; align-items: baseline; gap: 10px; }
.nv-cr-phenopct { font-size: 34px; font-weight: 800; color: var(--nv-savia-hi); line-height: 1; }
.nv-cr-phenolab { font-size: 14px; font-weight: 700; }
.nv-cr-phenometa { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--nv-tenue); margin: 9px 0 12px; }
.nv-cr-vstarget { color: var(--nv-cosecha); font-family: var(--nv-f-mono); }
.nv-cr-spark { display: flex; align-items: flex-end; gap: 5px; height: 46px; }
.nv-cr-sparkbar { flex: 1; background: linear-gradient(180deg, var(--nv-cosecha), var(--nv-savia)); border-radius: 3px 3px 0 0; min-height: 8px; }
.nv-cr-alerts { display: flex; flex-direction: column; gap: 8px; }
.nv-cr-alert { border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-tenue); border-radius: var(--nv-r-sm); padding: 10px 12px; background: var(--nv-suelo); }
.nv-cr-alert.lvl-alta { border-left-color: var(--nv-critico); }
.nv-cr-alert.lvl-media { border-left-color: var(--nv-dorado); }
.nv-cr-alert.lvl-ok { border-left-color: var(--nv-savia-hi); }
.nv-cr-althead { display: flex; align-items: center; gap: 8px; }
.nv-cr-alttitle { flex: 1; font-size: 13px; font-weight: 700; }
.nv-cr-altlvl { font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.nv-cr-altlvl.lvl-alta { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 15%, transparent); }
.nv-cr-altlvl.lvl-media { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 15%, transparent); }
.nv-cr-altlvl.lvl-ok { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 15%, transparent); }
.nv-cr-alttoggle { margin-top: 8px; background: none; border: none; color: var(--nv-savia); font-size: 11.5px; font-weight: 700; cursor: pointer; padding: 0; }
.nv-cr-altdet { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; }
.nv-cr-altline { font-size: 12px; color: var(--nv-tenue); line-height: 1.45; }
.nv-cr-altline b { color: var(--nv-texto); }
.nv-cr-timeline { display: flex; gap: 4px; overflow-x: auto; padding: 4px 0 8px; }
.nv-cr-tlnode { flex: 1; min-width: 74px; display: flex; flex-direction: column; align-items: center; gap: 7px; position: relative; }
.nv-cr-tlnode:not(:last-child)::after { content: ''; position: absolute; top: 7px; left: 50%; width: 100%; height: 2px; background: var(--nv-linea); z-index: 0; }
.nv-cr-tlnode.done::after, .nv-cr-tlnode.current::after { background: var(--nv-savia); }
.nv-cr-tldot { width: 16px; height: 16px; border-radius: 50%; background: var(--nv-suelo); border: 2px solid var(--nv-linea-fuerte); z-index: 1; }
.nv-cr-tlnode.done .nv-cr-tldot { background: var(--nv-savia); border-color: var(--nv-savia); }
.nv-cr-tlnode.current .nv-cr-tldot { background: var(--nv-cosecha); border-color: var(--nv-cosecha); box-shadow: 0 0 0 4px color-mix(in srgb, var(--nv-cosecha) 25%, transparent); }
.nv-cr-tllabel { font-size: 11px; color: var(--nv-tenue); text-align: center; }
.nv-cr-tlnode.current .nv-cr-tllabel { color: var(--nv-texto); font-weight: 700; }
.nv-cr-photos { display: flex; gap: 10px; flex-wrap: wrap; }
.nv-cr-photo, .nv-cr-photoadd { width: 92px; height: 92px; border-radius: var(--nv-r-md); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; }
.nv-cr-photo { background: linear-gradient(135deg, #1a4a38, #0d3327); border: 1px solid var(--nv-linea); color: var(--nv-texto); }
.nv-cr-photolab { font-size: 10.5px; font-family: var(--nv-f-mono); color: var(--nv-cosecha); }
.nv-cr-photoadd { background: none; border: 1px dashed var(--nv-linea-fuerte); color: var(--nv-tenue); font-size: 11px; cursor: pointer; }
.nv-cr-photoadd:hover { border-color: var(--nv-savia); color: var(--nv-savia); }
.nv-cr-planbtn { margin-top: 16px; }
/* `.nv-cr-err` — respaldo de `cargarParcelas()` cuando `NVErr` no está
   disponible (crop.js:369). Cero reglas: mismo lenguaje que `.nv-ag-inmsg`. */
.nv-cr-err { font-size: 12px; color: var(--nv-critico); padding: 12px 0; }
@media (max-width: 720px) { .nv-cr-top { grid-template-columns: 1fr; } }

/* ---------- Biblioteca Viva ---------- */
.nv-lbi { display: inline-flex; align-items: center; justify-content: center; }
.nv-lb-comp { margin: 14px 0; }
.nv-lb-answer { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-savia); border-radius: var(--nv-r-md); padding: 16px; margin-bottom: 16px; }
.nv-lb-answertext { font-size: 13.5px; line-height: 1.55; color: var(--nv-texto); }
.nv-lb-refsnip { font-size: 11.5px; color: var(--nv-tenue); line-height: 1.4; margin: 2px 0; }
.nv-lb-related { display: flex; flex-wrap: wrap; gap: 7px; }
.nv-lb-seg { margin-bottom: 12px; align-self: flex-start; }
.nv-lb-cats { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.nv-lb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
.nv-lb-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 14px; display: flex; flex-direction: column; box-shadow: var(--nv-sh-1); }
.nv-lb-cardtop { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.nv-lb-kind { font-size: 10.5px; font-weight: 700; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 13%, transparent); padding: 3px 9px; border-radius: 999px; text-transform: uppercase; letter-spacing: .04em; }
.nv-lb-save { width: 30px; height: 30px; border-radius: 8px; border: none; background: none; color: var(--nv-tenue); cursor: pointer; display: grid; place-items: center; }
.nv-lb-save.on { color: var(--nv-critico); }
.nv-lb-title { font-size: 14px; font-weight: 700; line-height: 1.35; }
.nv-lb-summary { font-size: 12px; color: var(--nv-tenue); line-height: 1.45; margin: 6px 0 12px; flex: 1; }
.nv-lb-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.nv-lb-src { font-family: var(--nv-f-mono); font-size: 10px; color: var(--nv-savia-hi); }
/* lector asistido */
.nv-lb-reader { max-width: 460px; }
.nv-lb-readsub { font-size: 11.5px; color: var(--nv-tenue); margin: -6px 0 12px; }
.nv-lb-modes { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 12px; }
.nv-lb-mode { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; padding: 7px 11px; border-radius: var(--nv-r-pill); border: 1px solid var(--nv-linea-fuerte); background: var(--nv-suelo); color: var(--nv-tenue); cursor: pointer; transition: var(--nv-dur-fast); }
.nv-lb-mode.on { background: color-mix(in srgb, var(--nv-savia) 15%, transparent); color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-lb-readout { font-size: 13px; line-height: 1.55; color: var(--nv-texto); white-space: pre-line; background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); padding: 13px; min-height: 90px; }

/* ---------- Economía familiar ---------- */
.nv-eci { display: inline-flex; align-items: center; justify-content: center; }
.nv-ec-comp { margin: 14px 0 10px; }
/* Aviso cuando un movimiento NO se guardo: es plata, no puede fallar callado. */
.nv-ec-err { width: 100%; margin-top: 8px; font-size: 12.5px; color: var(--nv-alerta, #E5534B); }
.nv-ec-draftcard { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 13px 15px; border-radius: var(--nv-r-md); border: 1px solid var(--nv-linea); background: var(--nv-superficie); margin-bottom: 14px; }
.nv-ec-draftcard.in { border-left: 3px solid var(--nv-savia); }
.nv-ec-draftcard.out { border-left: 3px solid var(--nv-critico); }
.nv-ec-drafttag { font-size: 11px; font-weight: 700; padding: 4px 10px; border-radius: 999px; }
.nv-ec-drafttag.ingreso { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 15%, transparent); }
.nv-ec-drafttag.gasto { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 15%, transparent); }
.nv-ec-draftamt { font-size: 20px; font-weight: 800; }
.nv-ec-draftcat { font-size: 12px; color: var(--nv-tenue); }
.nv-ec-draftact { display: flex; gap: 8px; margin-left: auto; }
.nv-ec-ok, .nv-ec-no { width: auto; flex: 0 0 auto; padding: 9px 15px; font-size: 12.5px; }
.nv-ec-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.nv-ec-kpi { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; }
.nv-ec-kpi.hl { background: linear-gradient(180deg, color-mix(in srgb, var(--nv-savia) 10%, var(--nv-superficie)), var(--nv-superficie)); border-color: color-mix(in srgb, var(--nv-savia) 35%, transparent); }
.nv-ec-kpilb { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-ec-kpival { font-size: 18px; font-weight: 800; margin-top: 5px; }
.nv-ec-kpi.in .nv-ec-kpival { color: var(--nv-savia-hi); }
.nv-ec-kpi.out .nv-ec-kpival { color: var(--nv-critico); }
.nv-ec-cats { display: flex; flex-direction: column; gap: 9px; }
.nv-ec-catrow { display: flex; align-items: center; gap: 11px; }
.nv-ec-catname { width: 96px; flex-shrink: 0; font-size: 12.5px; }
.nv-ec-catbar { flex: 1; height: 8px; background: var(--nv-suelo); border-radius: 999px; overflow: hidden; }
.nv-ec-catfill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--nv-dorado), var(--nv-critico)); }
.nv-ec-catamt { font-size: 12px; color: var(--nv-tenue); min-width: 80px; text-align: right; }
.nv-ec-analysis { background: color-mix(in srgb, var(--nv-savia) 7%, var(--nv-superficie)); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; margin-top: 6px; }
.nv-ec-anhead { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 13px; margin-bottom: 8px; }
.nv-ec-ansum { font-size: 13px; line-height: 1.5; color: var(--nv-texto); margin-bottom: 10px; }
.nv-ec-anrec { display: flex; align-items: center; gap: 8px; padding: 7px 0; }
.nv-ec-anbullet { color: var(--nv-savia); font-weight: 800; }
.nv-ec-anrectext { flex: 1; font-size: 12.5px; color: var(--nv-tenue); }
.nv-ec-alerts { display: flex; flex-direction: column; gap: 8px; }
.nv-ec-alert { display: flex; align-items: center; gap: 10px; padding: 11px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-tenue); border-radius: var(--nv-r-sm); }
.nv-ec-alert.lvl-alta { border-left-color: var(--nv-critico); }
.nv-ec-alert.lvl-media { border-left-color: var(--nv-dorado); }
.nv-ec-alert.lvl-ok { border-left-color: var(--nv-savia-hi); }
.nv-ec-alertbody { flex: 1; min-width: 0; }
.nv-ec-alerttitle { font-size: 13px; font-weight: 700; }
.nv-ec-alerttext { font-size: 12px; color: var(--nv-tenue); }
.nv-ec-alertlvl { font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.nv-ec-alertlvl.lvl-alta { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 14%, transparent); }
.nv-ec-alertlvl.lvl-media { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 14%, transparent); }
.nv-ec-alertlvl.lvl-ok { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-ec-proj { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.nv-ec-projcard { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; text-align: center; }
.nv-ec-projcard.base { border-color: color-mix(in srgb, var(--nv-savia) 40%, transparent); }
.nv-ec-projlab { font-size: 11px; color: var(--nv-tenue); }
.nv-ec-projval { font-size: 15px; font-weight: 800; margin-top: 5px; }
.nv-ec-projcard.optimista .nv-ec-projval { color: var(--nv-savia-hi); }
.nv-ec-projcard.pesimista .nv-ec-projval { color: var(--nv-critico); }
.nv-ec-hist { display: flex; flex-direction: column; gap: 6px; }
.nv-ec-hrow { display: flex; align-items: center; gap: 11px; padding: 10px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-ec-hic { width: 30px; height: 30px; flex-shrink: 0; border-radius: 8px; display: grid; place-items: center; }
.nv-ec-hic.ingreso { background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-ec-hic.gasto { background: color-mix(in srgb, var(--nv-critico) 12%, transparent); }
.nv-ec-hmid { flex: 1; min-width: 0; }
.nv-ec-hnote { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-ec-hcat { font-size: 11px; color: var(--nv-tenue); }
.nv-ec-hamt { font-size: 13.5px; font-weight: 700; flex-shrink: 0; }
.nv-ec-hamt.ingreso { color: var(--nv-savia-hi); }
.nv-ec-hamt.gasto { color: var(--nv-critico); }
@media (max-width: 720px) {
  .nv-ec-kpis { grid-template-columns: 1fr 1fr; }
  .nv-ec-proj { grid-template-columns: 1fr; }
  .nv-ec-draftact { margin-left: 0; width: 100%; }
}

/* ---------- Sistema Pecuario ---------- */
.nv-lvi { display: inline-flex; align-items: center; justify-content: center; }
.nv-lv-comp { margin: 14px 0 10px; }
.nv-lv-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.nv-lv-kpi { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; }
.nv-lv-kpi.hl { background: linear-gradient(180deg, color-mix(in srgb, var(--nv-savia) 10%, var(--nv-superficie)), var(--nv-superficie)); border-color: color-mix(in srgb, var(--nv-savia) 35%, transparent); }
.nv-lv-kpi.warn { border-color: color-mix(in srgb, var(--nv-dorado) 40%, transparent); }
.nv-lv-kpilb { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-lv-kpival { font-size: 20px; font-weight: 800; margin-top: 5px; }
.nv-lv-kpi.in .nv-lv-kpival { color: var(--nv-savia-hi); }
.nv-lv-kpi.warn .nv-lv-kpival { color: var(--nv-dorado); }
.nv-lv-herds { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
.nv-lv-herd { display: flex; gap: 11px; align-items: center; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 14px; }
.nv-lv-herdic { width: 42px; height: 42px; flex-shrink: 0; border-radius: 11px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-lv-herdmid { flex: 1; min-width: 0; }
.nv-lv-herdtop { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.nv-lv-herdname { font-size: 14px; font-weight: 700; }
.nv-lv-herdsp { font-size: 10.5px; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 13%, transparent); padding: 2px 8px; border-radius: 999px; flex-shrink: 0; }
.nv-lv-herdcount { font-size: 12px; color: var(--nv-tenue); }
.nv-lv-herdprod { font-size: 13px; color: var(--nv-cosecha); margin-top: 3px; }
/* El titular del lote YA salía con `is-bien|is-medio|is-mal` desde que existe el
   módulo, y ninguna de las tres tenía estilo: el estado se calculaba, se mandaba
   y no se veía. Estas tres líneas lo encienden. */
.nv-lv-herdprod.is-bien { color: var(--nv-ok); }
.nv-lv-herdprod.is-medio { color: var(--nv-alerta); }
.nv-lv-herdprod.is-mal { color: var(--nv-critico); }

/* Indicadores del lote elegido. Sin reglas de `@media`, todas base: la lección
   del hilo 8 es que un `@media` NO suma especificidad, así que escribirlo antes
   de la regla base la deja ganando y el bloque no hace nada. */
.nv-lv-inds { display: flex; flex-direction: column; gap: 6px; }
.nv-lv-ind { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-sm); border-left-width: 3px; }
.nv-lv-ind.is-bien { border-left-color: var(--nv-ok); }
.nv-lv-ind.is-medio { border-left-color: var(--nv-alerta); }
.nv-lv-ind.is-mal { border-left-color: var(--nv-critico); }
/* El que se puede calcular y aún no tiene datos NO es un error: va en gris de
   apagado, no en rojo. El productor no ha hecho nada mal — le falta registrar. */
.nv-lv-ind.is-falta { border-left-color: var(--nv-fantasma); }
.nv-lv-indname { font-size: 13px; font-weight: 600; flex: 1; min-width: 120px; }
.nv-lv-indval { font-size: 14px; font-weight: 800; }
.nv-lv-ind.is-bien .nv-lv-indval { color: var(--nv-ok); }
.nv-lv-ind.is-medio .nv-lv-indval { color: var(--nv-alerta); }
.nv-lv-ind.is-mal .nv-lv-indval { color: var(--nv-critico); }
/* La procedencia ocupa toda la fila (`width:100%` sobre `flex-wrap`) para que
   caiga debajo y no compita con el número. */
.nv-lv-indmeta { font-size: 11px; color: var(--nv-tenue); font-family: var(--nv-f-mono); width: 100%; }
.nv-lv-indfalta { font-size: 12px; color: var(--nv-tenue); line-height: 1.35; }

/* LA LISTA DE ANIMALES — «está mal ordenada y no se entiende» (dueño).
   MEDIDO en el lote bovino de demo, 9 fichas a 360 px: ni `.nv-lv-angrid`, ni
   `.nv-lv-an`, ni sus dos `span` tenían UNA SOLA regla en toda la hoja. Salían
   como botones CRUDOS del navegador —fondo gris rgb(107,107,107), borde de
   2 px, 13.33 px de fuente de sistema— y en `inline-block`, o sea puestos uno
   detrás de otro EN LA MISMA LÍNEA. Las nueve fichas se leían de corrido:
   «CO-0102Hembra · 8 añosCO-0118Hembra · 5 años…».
   Una fila por animal y con la pinta de `.nv-lv-ind`, que es la otra lista de
   esta misma pantalla: si las dos listas del mismo panel no se parecen, la
   persona cree que son cosas distintas.
   EL PUNTO ENTRE LA CHAPETA Y LOS DATOS NO VA AQUÍ: va en el texto, en
   `livestock.js` — ver el comentario de allí y el motivo.
   `var(--nv-aire, 16px)` con respaldo porque este mismo `.nv-lv-angrid` se
   reusa dentro de la hoja de vida (los hijos del animal), que se pinta en una
   caja fuera de `.nv-panel`, que es quien define la variable. */
.nv-lv-angrid { display: flex; flex-direction: column; gap: calc(var(--nv-aire, 16px) / 2); }
.nv-lv-an { display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 10px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-sm); border-left-width: 3px; color: var(--nv-texto);
  font: inherit; line-height: 1.45; }
.nv-lv-an:hover { border-color: var(--nv-linea-fuerte); }
/* El vendido y el muerto NO son un error: se apagan, no se pintan de rojo. */
.nv-lv-an.is-vendido, .nv-lv-an.is-muerto { border-left-color: var(--nv-fantasma); }
.nv-lv-anchap { font-size: 13.5px; font-weight: 700; }
.nv-lv-anmeta { font-size: 12.5px; color: var(--nv-tenue); }

.nv-lv-alerts { display: flex; flex-direction: column; gap: 8px; }
.nv-lv-areas { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 11px; }
.nv-lv-area { text-align: left; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 14px; cursor: default; color: var(--nv-texto); }
.nv-lv-area:hover { border-color: var(--nv-linea-fuerte); }
.nv-lv-areaic { width: 34px; height: 34px; border-radius: 9px; background: color-mix(in srgb, var(--nv-savia) 13%, transparent); display: grid; place-items: center; margin-bottom: 9px; }
.nv-lv-areaname { font-size: 12px; color: var(--nv-tenue); }
.nv-lv-areametric { font-size: 17px; font-weight: 800; color: var(--nv-savia-hi); margin: 2px 0; }
.nv-lv-areanote { font-size: 11px; color: var(--nv-tenue); line-height: 1.35; }
.nv-lv-events { display: flex; flex-direction: column; gap: 6px; }
.nv-lv-ev { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-lv-evic { width: 30px; height: 30px; flex-shrink: 0; border-radius: 8px; background: color-mix(in srgb, var(--nv-savia) 12%, transparent); display: grid; place-items: center; }
.nv-lv-evmid { flex: 1; min-width: 0; }
.nv-lv-evnote { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-lv-evtype { font-size: 11px; color: var(--nv-tenue); }
@media (max-width: 720px) { .nv-lv-kpis { grid-template-columns: 1fr; } }

/* ---------- Agricultura Protegida ---------- */
.nv-pri { display: inline-flex; align-items: center; justify-content: center; }
/* La tarjeta de la casa (invernadero) entera — reportado 2026-08-21
   ("apeñuscado", botones tocándose). `.nv-pr-house-card` no tenía NI UNA
   regla en toda la hoja: sin fondo, sin borde, sin aire entre lo que
   agrupa (cabecera, alertas, lecturas, cosecha, labores, el cuadro de
   anotar y el botón de consejo son 6-8 hijos directos, todos pegados). */
.nv-pr-house-card { display: flex; flex-direction: column; gap: var(--nv-aire);
  background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-lg); padding: 16px; box-shadow: var(--nv-sh-1); min-width: 0; }
.nv-pr-composer { display: flex; flex-direction: column; gap: 8px; }
.nv-pr-house { display: flex; align-items: center; gap: 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; }
.nv-pr-houseic { width: 44px; height: 44px; flex-shrink: 0; border-radius: 12px; background: color-mix(in srgb, var(--nv-savia) 15%, transparent); display: grid; place-items: center; }
.nv-pr-housename { font-size: 15px; font-weight: 800; }
.nv-pr-housesub { font-size: 12px; color: var(--nv-cosecha); }
/* Piso de 150px (antes 130px): a 130px, avisos como "Medido hace 15 días —
   no decidas con este dato" partían casi cada palabra en su propia línea.
   No arregla el texto largo, pero le da el ancho mínimo para respirar. */
.nv-pr-metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
.nv-pr-metric { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; }
.nv-pr-metric.warn { border-color: color-mix(in srgb, var(--nv-dorado) 45%, transparent); }
.nv-pr-mtop { display: flex; align-items: center; gap: 6px; color: var(--nv-tenue); }
.nv-pr-mk { font-size: 11.5px; }
.nv-pr-mval { font-size: 20px; font-weight: 800; color: var(--nv-savia-hi); margin: 6px 0 2px; }
.nv-pr-metric.warn .nv-pr-mval { color: var(--nv-dorado); }
.nv-pr-mrange { font-size: 10.5px; color: var(--nv-tenue); font-family: var(--nv-f-mono); }
.nv-pr-mold { font-size: 10.5px; color: var(--nv-dorado); margin-top: 4px; line-height: 1.4; }
.nv-pr-mage { font-size: 10.5px; color: var(--nv-tenue); margin-top: 4px; }
.nv-pr-housemid { flex: 1 1 auto; min-width: 0; }
.nv-pr-stage { display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); padding: 10px 12px; }
.nv-pr-stagek { font-size: 12.5px; color: var(--nv-tenue); }
.nv-pr-cyclechip { font: 600 11px/1 var(--nv-f-body); color: var(--nv-savia);
  background: color-mix(in srgb, var(--nv-savia) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--nv-savia) 30%, transparent);
  border-radius: var(--nv-r-pill); padding: 6px 11px; cursor: pointer; flex-shrink: 0; }
.nv-pr-harvestbody { flex: 1 1 auto; min-width: 0; }
.nv-pr-note, .nv-pr-freenote, .nv-pr-fnote { font-size: 12px; color: var(--nv-tenue); line-height: 1.5; }
.nv-pr-out { display: flex; flex-direction: column; gap: 8px; }
.nv-pr-sub { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 9px; }
.nv-pr-subrow { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 11px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-pr-subk { font-size: 12px; color: var(--nv-tenue); }
.nv-pr-subv { font-size: 13px; font-weight: 700; color: var(--nv-texto); }
.nv-pr-tasks { display: flex; flex-direction: column; gap: 7px; }
.nv-pr-task { display: flex; align-items: center; gap: 11px; text-align: left; width: 100%; padding: 11px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); cursor: pointer; color: var(--nv-texto); transition: var(--nv-dur-fast); }
.nv-pr-task:hover { border-color: var(--nv-linea-fuerte); }
.nv-pr-taskbox { width: 24px; height: 24px; flex-shrink: 0; border-radius: 7px; border: 1.5px solid var(--nv-linea-fuerte); display: grid; place-items: center; }
.nv-pr-task.done .nv-pr-taskbox { background: var(--nv-savia); border-color: var(--nv-savia); }
.nv-pr-task.done .nv-pr-taskbox svg { color: #04140E; }
.nv-pr-tasktext { flex: 1; font-size: 13px; }
.nv-pr-task.done .nv-pr-tasktext { color: var(--nv-tenue); text-decoration: line-through; }
.nv-pr-harvest { display: flex; align-items: center; gap: 11px; margin-top: 16px; padding: 14px; background: linear-gradient(120deg, color-mix(in srgb, var(--nv-savia) 12%, var(--nv-superficie)), var(--nv-superficie)); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-md); }
.nv-pr-harvestk { font-size: 11px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-pr-harvestv { font-size: 13.5px; font-weight: 700; margin-top: 2px; }

/* El formulario de "nueva casa de cultivo" dentro del overlay reusado
   `.nv-ag-modal`. Es mas alto que los avisos de Activos (8 campos), asi que
   lleva su propio ancho y su propio scroll: sin `max-height` un movil no
   alcanza los botones de guardar. */
.nv-pr-formcard { max-width: 440px; max-height: 86vh; overflow-y: auto; gap: 0; }
.nv-pr-f { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.nv-pr-f label { font-size: 12px; color: var(--nv-tenue); font-weight: 600; }
.nv-pr-f .nv-field { margin-bottom: 0; }
.nv-pr-acts { display: flex; gap: 9px; margin-top: 6px; }
.nv-pr-acts .nv-btn { flex: 1; }

/* ---------- Activos y Mantenimiento ---------- */
.nv-asi { display: inline-flex; align-items: center; justify-content: center; }
.nv-as-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.nv-as-card { text-align: left; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; cursor: pointer; color: var(--nv-texto); box-shadow: var(--nv-sh-1); transition: var(--nv-dur-fast); }
.nv-as-card:hover { border-color: var(--nv-linea-fuerte); transform: translateY(-2px); }
.nv-as-ctop { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.nv-as-cic { width: 40px; height: 40px; border-radius: 11px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-as-cst { font-size: 10.5px; font-weight: 700; padding: 4px 10px; border-radius: 999px; }
.nv-as-cst.st-ok { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-as-cst.st-maint { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 14%, transparent); }
.nv-as-cst.st-alert { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 14%, transparent); }
.nv-as-cname { font-size: 14.5px; font-weight: 700; }
.nv-as-ctype { font-size: 12px; color: var(--nv-tenue); }
.nv-as-bar { height: 6px; background: var(--nv-suelo); border-radius: 999px; overflow: hidden; margin: 11px 0 9px; }
.nv-as-fill { height: 100%; border-radius: 999px; background: var(--nv-savia); }
.nv-as-fill.st-maint { background: var(--nv-dorado); }
.nv-as-fill.st-alert { background: var(--nv-critico); }
.nv-as-cfoot { display: flex; align-items: center; gap: 4px; font-size: 11.5px; color: var(--nv-tenue); }
/* detalle */
.nv-as-dhead { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.nv-as-back { background: none; border: none; color: var(--nv-tenue); cursor: pointer; padding: 2px; display: inline-flex; }
.nv-as-dinfo { flex: 1; min-width: 0; }
.nv-as-dname { font-size: 16px; font-weight: 800; }
.nv-as-dtype { font-size: 12px; color: var(--nv-tenue); }
.nv-as-dcard { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; }
.nv-as-specs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 13px; }
.nv-as-speck { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-as-specv { font-size: 14px; font-weight: 700; margin-top: 3px; }
.nv-as-next { display: flex; align-items: center; gap: 10px; padding-top: 13px; border-top: 1px solid var(--nv-linea); }
.nv-as-nextk { font-size: 11px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-as-nextv { font-size: 13px; font-weight: 700; color: var(--nv-cosecha); }
.nv-as-aibtn { margin: 14px 0; }
.nv-as-logs { display: flex; flex-direction: column; gap: 2px; }
.nv-as-log { display: flex; gap: 12px; padding: 12px 4px; position: relative; }
.nv-as-log:not(:last-child)::before { content: ''; position: absolute; left: 18px; top: 40px; bottom: -2px; width: 2px; background: var(--nv-linea); }
.nv-as-logic { width: 36px; height: 36px; flex-shrink: 0; border-radius: 50%; display: grid; place-items: center; background: color-mix(in srgb, var(--nv-savia) 12%, var(--nv-superficie)); border: 1px solid var(--nv-linea); z-index: 1; }
.nv-as-logic.type-novedad { background: color-mix(in srgb, var(--nv-critico) 12%, var(--nv-superficie)); }
.nv-as-logic.type-mantenimiento { background: color-mix(in srgb, var(--nv-dorado) 12%, var(--nv-superficie)); }
.nv-as-logmid { flex: 1; min-width: 0; padding-top: 2px; }
.nv-as-logtop { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.nv-as-logtype { font-size: 13px; font-weight: 700; }
.nv-as-logtime { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue); }
.nv-as-lognote { font-size: 12.5px; color: var(--nv-tenue); margin-top: 2px; }
/* asistente IA del activo (modal) */
.nv-as-aimodal { max-width: 460px; height: 70vh; max-height: 560px; display: flex; flex-direction: column; }
.nv-as-aimsgs { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 9px; margin: 10px 0; }
@media (max-width: 720px) { .nv-as-specs { grid-template-columns: 1fr 1fr; } }

/* ---------- Mis Publicaciones ---------- */
.nv-mni { display: inline-flex; align-items: center; justify-content: center; }
.nv-mn-bar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 4px; }
.nv-mn-new { width: auto; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 10px 15px; margin-left: auto; }
.nv-mn-list { display: flex; flex-direction: column; gap: 9px; }
.nv-mn-row { display: flex; align-items: center; gap: 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-savia); border-radius: var(--nv-r-md); padding: 13px; }
.nv-mn-row.st-paused { border-left-color: var(--nv-dorado); opacity: .85; }
.nv-mn-row.st-draft { border-left-color: var(--nv-tenue); opacity: .8; }
.nv-mn-media { width: 46px; height: 46px; flex-shrink: 0; border-radius: 11px; background: linear-gradient(135deg, #1a4a38, #0d3327); display: grid; place-items: center; }
.nv-mn-mid { flex: 1; min-width: 0; }
.nv-mn-top { display: flex; align-items: center; gap: 8px; }
.nv-mn-title { font-size: 14px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nv-mn-st { font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 999px; flex-shrink: 0; }
.nv-mn-st.st-active { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-mn-st.st-paused { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 14%, transparent); }
.nv-mn-st.st-draft { color: var(--nv-tenue); background: color-mix(in srgb, var(--nv-tenue) 16%, transparent); }
.nv-mn-price { font-size: 14px; color: var(--nv-cosecha); margin: 2px 0; }
.nv-mn-stats { display: flex; gap: 13px; }
.nv-mn-stat { display: inline-flex; align-items: center; gap: 3px; font-size: 11.5px; color: var(--nv-tenue); }
.nv-mn-act { display: flex; gap: 7px; flex-shrink: 0; }
.nv-mn-actbtn { width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--nv-linea-fuerte); background: none; color: var(--nv-tenue); cursor: pointer; display: grid; place-items: center; transition: var(--nv-dur-fast); }
.nv-mn-actbtn:hover { border-color: var(--nv-savia); color: var(--nv-savia); }
.nv-mn-actbtn.primary { background: color-mix(in srgb, var(--nv-savia) 14%, transparent); border-color: color-mix(in srgb, var(--nv-savia) 40%, transparent); color: var(--nv-savia); }
@media (max-width: 720px) { .nv-mn-new { margin-left: 0; } }
/* RENGLON 22 — publicidad y urgencias: insignia "Destacada" + boton + popover
   con los 3 tramos (3/7/15 dias). Mismos tokens que el resto de esta pantalla
   (--nv-savia/--nv-dorado/--nv-superficie/--nv-linea-fuerte). */
.nv-mn-act { position: relative; }
.nv-mn-dest { display: inline-flex; align-items: center; gap: 3px; font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 999px; color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 16%, transparent); flex-shrink: 0; }
.nv-mn-destpop { position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; display: flex; flex-direction: column; gap: 6px; min-width: 190px; padding: 10px; border-radius: var(--nv-r-md); background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); box-shadow: 0 8px 24px rgba(0,0,0,.25); }
.nv-mn-destpop-tit { font-size: 12px; font-weight: 700; color: var(--nv-tenue); margin-bottom: 2px; }
.nv-mn-destpop-opt { border: 1px solid var(--nv-linea-fuerte); background: none; color: inherit; border-radius: 8px; padding: 7px 9px; font-size: 12.5px; text-align: left; cursor: pointer; transition: var(--nv-dur-fast); }
.nv-mn-destpop-opt:hover { border-color: var(--nv-savia); color: var(--nv-savia); }
.nv-mn-destpop-opt:disabled { opacity: .55; cursor: default; }
.nv-mn-destpop-msg { font-size: 11.5px; color: var(--nv-rojo, #E05555); min-height: 14px; }

/* ---------- Perfil ---------- */
.nv-pfi { display: inline-flex; align-items: center; justify-content: center; }
.nv-pf-head { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; }
.nv-pf-avatar { width: 62px; height: 62px; flex-shrink: 0; border-radius: 50%; background: var(--nv-grad); background-size: cover; background-position: center; color: #04140E; font-weight: 800; font-size: 26px; display: grid; place-items: center; }
.nv-pf-name { font-size: 18px; font-weight: 800; }
.nv-pf-email { font-size: 13px; color: var(--nv-tenue); }
/* Cambiar foto de perfil: el botón va SUPERPUESTO al círculo, esquina
   inferior derecha — mismo patrón que cualquier editor de avatar. */
.nv-pf-avatarwrap { position: relative; flex-shrink: 0; }
.nv-pf-fotobtn { position: absolute; right: -2px; bottom: -2px; width: 24px; height: 24px; border-radius: 50%; background: var(--nv-superficie); border: 2px solid var(--nv-fondo); display: grid; place-items: center; padding: 0; cursor: pointer; color: var(--nv-texto); }
.nv-pf-fotobtn:disabled { opacity: .5; cursor: default; }
/* Link de texto "Cambiar foto", visible, junto al nombre/correo — mismo
   `fotoInput` que el ícono de la esquina, para quien no ve el ícono. */
.nv-pf-fotolink { display: block; margin-top: 4px; padding: 0; border: none; background: none; color: var(--nv-savia-hi); font-size: 12.5px; font-weight: 600; text-decoration: underline; cursor: pointer; }
.nv-pf-fotolink:disabled { opacity: .5; cursor: default; }
.nv-pf-fotomsg { font-size: 12px; margin: 0 0 10px; min-height: 14px; }
.nv-pf-fotomsg.ok { color: var(--nv-savia-hi); }
.nv-pf-fotomsg.err { color: var(--nv-critico); }
/* Que Tuxani me llame */
.nv-pf-call { background: linear-gradient(135deg, color-mix(in srgb, var(--nv-savia) 18%, var(--nv-superficie)), var(--nv-superficie)); border: 1px solid color-mix(in srgb, var(--nv-savia) 40%, transparent); border-radius: var(--nv-r-lg); padding: 16px; margin-bottom: 18px; }
.nv-pf-calltop { display: flex; gap: 11px; align-items: flex-start; margin-bottom: 12px; }
.nv-pf-callic { width: 40px; height: 40px; flex-shrink: 0; border-radius: 11px; background: var(--nv-grad); display: grid; place-items: center; }
.nv-pf-callic svg { color: #04140E; }
.nv-pf-calltitle { font-size: 15px; font-weight: 800; }
.nv-pf-callsub { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; }
.nv-pf-callbtn { width: 100%; margin-top: 4px; }
.nv-pf-callmsg { font-size: 12px; margin-top: 8px; min-height: 16px; }
.nv-pf-callmsg.ok { color: var(--nv-savia-hi); }
.nv-pf-callmsg.err { color: var(--nv-critico); }
/* El código de vinculación de WhatsApp. Grande y espaciado a propósito: se
   copia MIRÁNDOLO para escribirlo en otra app, así que los seis dígitos tienen
   que leerse de un vistazo y sin confundir un 0 con una O. `tabular-nums`
   mantiene el ancho fijo mientras corre la cuenta atrás — sin eso el número
   baila cada segundo. */
.nv-pf-wacodigo { background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: 12px; padding: 14px 13px; margin-bottom: 10px; text-align: center; }
.nv-pf-wanum { font-size: 30px; font-weight: 800; letter-spacing: 6px; font-variant-numeric: tabular-nums; color: var(--nv-texto); }
.nv-pf-wapaso { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; margin-top: 7px; }
.nv-pf-input { width: 100%; background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-input, 12px); padding: 11px 13px; color: var(--nv-texto); font-size: 14px; outline: none; margin-bottom: 9px; }
.nv-pf-input:focus { border-color: var(--nv-savia); }
.nv-pf-form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.nv-pf-field { display: flex; flex-direction: column; }
.nv-pf-label { font-size: 11px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 5px; }
.nv-pf-field .nv-pf-input { margin-bottom: 0; }
.nv-pf-save { width: auto; padding: 11px 20px; margin-top: 12px; }
.nv-pf-savemsg { font-size: 12.5px; margin-top: 8px; min-height: 14px; }
.nv-pf-savemsg.ok { color: var(--nv-savia-hi); }
/* Un guardado que no ocurrió va en rojo, no en verde. Ver barrida de honestidad:
   la capa de datos resolvía `ok:true` aunque el POST hubiera fallado. */
.nv-pf-savemsg.err { color: var(--nv-critico); }
.nv-pf-retry { margin-left: 10px; padding: 2px 10px; font-size: 12px; }

/* «No se pudieron leer tus parcelas» — no tumba el hub de agronomía, que sirve
   para lo demás; solo explica por qué los selectores están vacíos. */
.nv-ag-plotsfail { display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin-top: 12px; padding: 10px 12px; font-size: 12.5px; color: var(--nv-critico);
  border: 1px solid color-mix(in srgb, var(--nv-critico) 35%, transparent);
  border-radius: var(--nv-r-md); }
.nv-ag-plotsretry { padding: 2px 10px; font-size: 12px; }
.nv-pf-prefs { display: flex; flex-direction: column; gap: 8px; }
.nv-pf-pref { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 13px 15px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-pf-prefk { font-size: 13.5px; font-weight: 600; }
.nv-pf-themebtn { width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--nv-linea-fuerte); background: var(--nv-suelo); cursor: pointer; display: grid; place-items: center; }
.nv-pf-toggle { width: 48px; height: 27px; border-radius: 999px; border: none; background: var(--nv-linea-fuerte); cursor: pointer; position: relative; transition: var(--nv-dur-fast); }
.nv-pf-toggle::after { content: ''; position: absolute; top: 3px; left: 3px; width: 21px; height: 21px; border-radius: 50%; background: #fff; transition: var(--nv-dur-fast); }
.nv-pf-toggle.on { background: var(--nv-savia); }
.nv-pf-toggle.on::after { left: 24px; }
.nv-pf-logout { width: auto; display: inline-flex; align-items: center; gap: 7px; padding: 11px 18px; color: var(--nv-critico); border-color: color-mix(in srgb, var(--nv-critico) 40%, transparent); }
.nv-pf-logout svg { color: var(--nv-critico); }
@media (max-width: 720px) { .nv-pf-form { grid-template-columns: 1fr; } }

/* ---- KYC: tipo de documento + foto por ranura (agente kyc-front, P3) ----
   Antes de esto el bloque de verificación solo pedía nombre y número: el
   backend exige ADEMÁS una foto por ranura (`documento/subir`) y aprobar sin
   el frente responde 409. Ver `profile.js` `pintarVerificacion()`. */
.nv-pf-doctype { cursor: pointer; }
.nv-pf-docnote { font-size: 11.5px; color: var(--nv-tenue); line-height: 1.4; margin: -3px 0 10px; display: none; }
.nv-pf-slots { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 4px; }
.nv-pf-slotwrap { display: flex; flex-direction: column; align-items: center; gap: 5px; width: 92px; }
.nv-pf-slotlabel { font-size: 10.5px; color: var(--nv-tenue); text-align: center; line-height: 1.25; min-height: 26px; }
.nv-pf-slot { display: flex; flex-direction: column; align-items: center; gap: 4px; width: 100%; padding: 10px 6px; border-radius: var(--nv-r-md); border: 1px dashed var(--nv-linea-fuerte); background: var(--nv-superficie); color: var(--nv-texto); cursor: pointer; font-size: 10.5px; }
.nv-pf-slot span { line-height: 1.1; }
.nv-pf-slot.done { border-style: solid; border-color: color-mix(in srgb, var(--nv-savia) 55%, transparent); color: var(--nv-savia-hi); }
.nv-pf-slot:disabled { opacity: .6; cursor: default; }
.nv-pf-slotmsg { font-size: 10px; text-align: center; min-height: 12px; }
.nv-pf-slotmsg.err { color: var(--nv-critico); }

/* ---------- Inteligencia de Mercado ---------- */
.nv-mii { display: inline-flex; align-items: center; justify-content: center; }
/* El campo arriba a todo el ancho; el microfono y el boton de buscar debajo,
   pegados (gap chico a proposito, para que se lean como una sola pieza).
   Antes los tres iban en fila y en movil el campo se quedaba en la mitad. */
.nv-mi-bar { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; margin: 14px 0; }
.nv-mi-bar .nv-ag-select { margin-bottom: 0; flex: 1 0 100%; width: 100%; }
.nv-mi-bar .nv-mi-btn { flex: 1; min-width: 0; }
/* El microfono de la barra de mercado. 44px porque es un objetivo tactil: por
   debajo de eso, un dedo falla. `is-on` mientras escucha, para que se vea que
   el telefono esta oyendo y no parezca que no hizo nada. */
.nv-mi-mic { width: 44px; height: 44px; padding: 0; flex: 0 0 auto; display: inline-flex;
  align-items: center; justify-content: center; border-color: var(--nv-linea-fuerte); }
.nv-mi-mic.is-on { background: var(--nv-savia); color: #04140E; border-color: var(--nv-savia); }
.nv-mi-mic.is-on svg { animation: nv-mi-pulso 1.1s ease-in-out infinite; }
@keyframes nv-mi-pulso { 0%,100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .nv-mi-mic.is-on svg { animation: none; } }
.nv-mi-btn { width: auto; flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; padding: 11px 16px; }
.nv-mi-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; margin-bottom: 10px; }
.nv-mi-ctop { display: flex; align-items: center; gap: 11px; }
.nv-mi-cic { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-mi-cinfo { flex: 1; min-width: 0; }
.nv-mi-cname { font-size: 14.5px; font-weight: 700; }
.nv-mi-cmeta { font-size: 12px; color: var(--nv-tenue); }
.nv-mi-act { display: flex; gap: 8px; }
.nv-mi-chat { width: auto; flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 9px 12px; font-size: 12.5px; text-decoration: none; }
.nv-mi-price { font-size: 15px; font-weight: 800; color: var(--nv-cosecha); flex-shrink: 0; }

/* ---------- SICC agro (hilo 3): lo que reemplazó a la maqueta ----------
   Las clases que vivían aquí —los medidores de «oportunidad» y «saturación»,
   el bloque de «cómo abordarlo», la rejilla de fortalezas y debilidades— se
   fueron con los datos inventados que pintaban. No se dejan huérfanas: un
   estilo sin dueño invita a volver a usarlo. */
.nv-mk-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin: 4px 0 10px; }
.nv-mk-prod { font-size: 17px; font-weight: 800; }
.nv-mk-dia { font-size: 11.5px; color: var(--nv-tenue); }
.nv-mk-alts { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 12px; }
.nv-mk-altk { font-size: 11.5px; color: var(--nv-tenue); }
.nv-mk-alt { cursor: pointer; border: 1px solid var(--nv-linea); background: var(--nv-superficie); }
.nv-mk-alt:hover { border-color: var(--nv-savia); }

/* El aviso va arriba y se lee ANTES que el número: el de «precio mayorista» es
   el que evita que el productor crea que lo están robando. Un pie de página no
   lo lee nadie. */
.nv-mk-avisos { display: flex; flex-direction: column; gap: 7px; margin: 10px 0 16px; }
.nv-mk-aviso { display: flex; gap: 8px; align-items: flex-start; padding: 10px 12px; font-size: 12.5px; line-height: 1.45; color: var(--nv-texto); background: color-mix(in srgb, var(--nv-dorado) 9%, var(--nv-superficie)); border: 1px solid color-mix(in srgb, var(--nv-dorado) 28%, transparent); border-radius: var(--nv-r-sm); }
.nv-mk-aviso .nv-mii { color: var(--nv-dorado); flex-shrink: 0; margin-top: 1px; }

.nv-mk-resumen { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }
.nv-mk-rbloque { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 14px; }
.nv-mk-rlab { font-size: 11px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-mk-rval { font-size: 24px; font-weight: 800; color: var(--nv-savia-hi); margin: 3px 0; }
.nv-mk-rsub { font-size: 12px; color: var(--nv-tenue); }

.nv-mk-plazas { display: flex; flex-direction: column; gap: 7px; }
.nv-mk-plaza { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-mk-pizq { min-width: 0; }
.nv-mk-pnom { font-size: 13.5px; font-weight: 700; }
.nv-mk-pmeta { font-size: 11.5px; color: var(--nv-tenue); }
.nv-mk-pfuente { font-size: 11px; color: var(--nv-dorado); margin-top: 2px; }
.nv-mk-pder { text-align: right; flex-shrink: 0; }
.nv-mk-precio { font-size: 15px; font-weight: 800; color: var(--nv-cosecha); }
/* Precio clickeable -> serie de 30 días por plaza (2026-08-21). Reset de
   <button> para que se siga viendo IGUAL que `.nv-mk-precio` (mismo color,
   mismo peso) y solo se note que es tocable por el subrayado punteado y el
   estado de foco/hover — un botón que se ve distinto a un dato es un botón
   que rompe la lectura de la fila. */
.nv-mk-preciobtn { background: none; border: none; padding: 0; cursor: pointer; font-family: inherit;
  text-decoration: underline dotted color-mix(in srgb, var(--nv-cosecha) 55%, transparent); text-underline-offset: 3px;
  transition: var(--nv-dur-fast); }
.nv-mk-preciobtn:hover, .nv-mk-preciobtn:focus-visible { color: var(--nv-savia-hi); }
.nv-mk-extra { font-size: 11px; color: var(--nv-tenue); }

/* Modal de la serie por plaza. Reusa `.nv-ag-modalcard` para el marco (mismo
   ancho, radio y sombra que el resto de tarjetas modales) y solo agrega lo
   propio: cabecera con el nombre de la plaza + X, y el cuerpo con el gráfico. */
.nv-plz-scrim { position: fixed; inset: 0; z-index: 120; background: rgba(4,20,14,.72);
  display: flex; align-items: center; justify-content: center; padding: 16px; }
.nv-plz-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.nv-plz-body { display: flex; flex-direction: column; gap: 10px; }
.nv-plz-stats { display: flex; flex-direction: column; gap: 6px; padding: 10px 0; border-top: 1px dashed var(--nv-linea); }
.nv-plz-kv { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; color: var(--nv-tenue); }
.nv-plz-kv b { color: var(--nv-texto); font-variant-numeric: tabular-nums; }
.nv-mk-chip { align-self: flex-start; font: 700 11.5px/1 var(--nv-f-body); padding: 6px 11px; border-radius: var(--nv-r-pill); }
.nv-mk-chip.ok { color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-mk-chip.bad { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 14%, transparent); }

.nv-mk-hist, .nv-mk-ref { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; }
.nv-mk-refl { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-top: 8px; }
.nv-mk-dato, .nv-mk-par { display: flex; flex-direction: column; gap: 2px; padding: 5px 0; }
.nv-mk-dato { flex-direction: row; justify-content: space-between; align-items: baseline; }
.nv-mk-dlab { font-size: 12px; color: var(--nv-tenue); }
.nv-mk-dval { font-size: 13.5px; font-weight: 700; }

.nv-mk-callaron { margin-top: 16px; }
.nv-mk-callada { display: flex; justify-content: space-between; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--nv-linea); }
.nv-mk-cnom { font-size: 12.5px; }
.nv-mk-cdia { font-size: 11.5px; color: var(--nv-tenue); }

.nv-mk-nada { display: flex; gap: 9px; align-items: flex-start; padding: 14px; background: var(--nv-suelo); border: 1px dashed var(--nv-linea); border-radius: var(--nv-r-md); font-size: 13px; line-height: 1.5; color: var(--nv-tenue); }
.nv-mk-vacio { font-size: 13px; color: var(--nv-tenue); }
.nv-mk-diag { font-size: 11.5px; color: var(--nv-tenue); margin-top: 8px; }
.nv-mk-desde { font-size: 12px; color: var(--nv-tenue); margin-bottom: 12px; }
.nv-mk-fam { font-size: 11.5px; color: var(--nv-dorado); margin-top: 8px; }
.nv-mk-mia { border-color: color-mix(in srgb, var(--nv-savia) 45%, transparent); }
.nv-mk-comps { display: flex; flex-direction: column; }

/* ─── SALIR A BUSCAR FUERA — el botón que subió arriba (hito A) ─────────────
   `.nv-mk-posibles` y `.nv-mk-posout` NO TENÍAN NI UNA REGLA. Mientras el botón
   vivía al final de la pantalla nadie lo notó; ahora va pegado a la barra de
   buscar, y sin aire se lee como parte de ella — que es justo lo contrario de
   lo que hace falta en el único botón del módulo que cuesta 6 tokens.

   MEDIDO A 360 px, QUE ES UN TELÉFONO. El botón a ancho completo y el precio
   DEBAJO, no al lado: en 360 px «cuesta 6 tokens · sale a buscar en internet»
   junto a un botón deja las dos cosas ilegibles, y el precio es exactamente lo
   que no se puede perder. */
.nv-mk-posibles { display: flex; flex-direction: column; align-items: flex-start; gap: 7px;
  margin: 14px 0 6px; padding-top: 14px; border-top: 1px solid var(--nv-linea); }
.nv-mk-posbtn { width: 100%; display: inline-flex; align-items: center; justify-content: center;
  gap: 8px; text-align: center; }
.nv-mk-posout:not(:empty) { margin-bottom: 14px; }

/* El municipio preguntado ANTES de buscar. Vacío no ocupa: hasta que contesta
   el perfil —y para quien ya dijo dónde está— aquí no hay nada, y un hueco en
   blanco encima del mercado se lee como algo que no cargó. */
.nv-mk-munpre:not(:empty) { margin-bottom: 14px; }
/* El botón de enviar del buscador de municipios: debajo del campo y a ancho
   completo, la misma forma que la barra de buscar. */
.nv-mk-munsend { width: 100%; margin-top: 8px; }

/* «¿Y qué hago con esto?» — lo único del módulo que cuesta tokens. El precio
   se ve en el propio botón, ANTES de pulsarlo: uno que cobra sin avisar se
   pulsa una vez y no se vuelve a tocar nunca. */
.nv-mk-brief { margin-top: 18px; }
.nv-mk-briefbtn { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.nv-mk-costo { font-size: 11px; color: var(--nv-tenue); border: 1px solid var(--nv-linea); border-radius: 999px; padding: 2px 8px; }
.nv-mk-briefcaja { padding: 15px; background: color-mix(in srgb, var(--nv-savia) 8%, var(--nv-superficie)); border: 1px solid color-mix(in srgb, var(--nv-savia) 28%, transparent); border-radius: var(--nv-r-md); }
.nv-mk-briefres { font-size: 13.5px; line-height: 1.55; color: var(--nv-texto); }
.nv-mk-briefacc { margin: 11px 0 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; font-size: 13px; line-height: 1.5; }
.nv-mk-briefcuid { display: flex; gap: 8px; align-items: flex-start; margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--nv-linea); font-size: 12.5px; line-height: 1.45; color: var(--nv-tenue); }
@media (max-width: 720px) { .nv-mk-resumen, .nv-mk-refl { grid-template-columns: 1fr; } }

/* ---------- Transporte Uber Rural ---------- */
.nv-tri { display: inline-flex; align-items: center; justify-content: center; }
.nv-tr-seg { margin-bottom: 14px; align-self: flex-start; }
.nv-tr-form { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; margin-bottom: 6px; }
.nv-tr-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 11px; }
.nv-tr-field { display: flex; flex-direction: column; }
.nv-tr-form .nv-ag-select { margin-bottom: 11px; }
.nv-tr-urgent { display: flex; align-items: center; gap: 8px; font-size: 13px; margin: 4px 0 12px; cursor: pointer; }
.nv-tr-search { width: 100%; }
.nv-tr-offers { margin-top: 6px; }
.nv-tr-card { display: flex; align-items: center; gap: 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; margin-bottom: 9px; flex-wrap: wrap; }
.nv-tr-cic { width: 44px; height: 44px; flex-shrink: 0; border-radius: 12px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-tr-cic.type-walking { background: color-mix(in srgb, var(--nv-cosecha) 16%, transparent); }
.nv-tr-cic.type-truck { background: color-mix(in srgb, var(--nv-cielo) 16%, transparent); }
.nv-tr-cmid { flex: 1; min-width: 140px; }
.nv-tr-ctop { display: flex; align-items: center; gap: 8px; }
.nv-tr-cname { font-size: 14px; font-weight: 700; }
.nv-tr-ctype { font-size: 10.5px; font-weight: 700; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 13%, transparent); padding: 2px 8px; border-radius: 999px; }
.nv-tr-cov { display: flex; align-items: center; gap: 3px; font-size: 12px; color: var(--nv-tenue); margin: 3px 0; }
.nv-tr-meta { display: flex; gap: 12px; font-size: 11.5px; color: var(--nv-tenue); flex-wrap: wrap; }
.nv-tr-rating { color: var(--nv-dorado); }
.nv-tr-right { display: flex; flex-direction: column; align-items: flex-end; gap: 7px; flex-shrink: 0; }
.nv-tr-price { font-size: 15px; font-weight: 800; color: var(--nv-cosecha); }
.nv-tr-req { width: auto; padding: 8px 16px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; }
.nv-tr-ok { flex-basis: 100%; font-size: 12px; color: var(--nv-savia-hi); margin-top: 4px; }
.nv-tr-privacy { font-size: 11.5px; color: var(--nv-tenue); font-style: italic; margin-top: 8px; text-align: center; }
/* mis viajes */
.nv-tr-trip { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; margin-bottom: 10px; }
.nv-tr-triptop { display: flex; align-items: center; gap: 11px; margin-bottom: 14px; }
.nv-tr-route { flex: 1; min-width: 0; }
.nv-tr-routetxt { font-size: 14px; font-weight: 700; }
.nv-tr-routemeta { font-size: 12px; color: var(--nv-tenue); }
.nv-tr-steps { display: flex; gap: 4px; }
.nv-tr-step { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; position: relative; }
.nv-tr-step:not(:last-child)::after { content: ''; position: absolute; top: 7px; left: 50%; width: 100%; height: 2px; background: var(--nv-linea); }
.nv-tr-step.done::after { background: var(--nv-savia); }
.nv-tr-stepdot { width: 15px; height: 15px; border-radius: 50%; background: var(--nv-suelo); border: 2px solid var(--nv-linea-fuerte); z-index: 1; }
.nv-tr-step.done .nv-tr-stepdot { background: var(--nv-savia); border-color: var(--nv-savia); }
.nv-tr-step.current .nv-tr-stepdot { background: var(--nv-cosecha); border-color: var(--nv-cosecha); box-shadow: 0 0 0 4px color-mix(in srgb, var(--nv-cosecha) 25%, transparent); }
.nv-tr-steplabel { font-size: 10.5px; color: var(--nv-tenue); text-align: center; }
.nv-tr-step.current .nv-tr-steplabel { color: var(--nv-texto); font-weight: 700; }
@media (max-width: 720px) { .nv-tr-row2 { grid-template-columns: 1fr; } }

/* ---------- Precio sugerido del flete (renglon 65 · pantalla, 2026-08-17) ----
   La cifra NO va en el verde de «todo bien» ni en el rojo de «algo se rompio»:
   va en el color de cosecha, el mismo que ya usa `.nv-tr-price`. Es un dato de
   plata, no un estado. Y el aviso obligatorio va PEGADO a la cifra, dentro de
   la misma caja y con su borde arriba: un descargo al pie de la pantalla, lejos
   del numero, es un descargo que nadie lee. */
.nv-tr-cotpeso { margin-bottom: 4px; }
.nv-tr-cot { width: 100%; margin-bottom: 8px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.nv-tr-cotout:empty { display: none; }
.nv-tr-cotload { font-size: 12.5px; color: var(--nv-tenue); padding: 6px 0; }
.nv-tr-cotbox { background: var(--nv-suelo); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; }
.nv-tr-cothead { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.nv-tr-cott { font-size: 12px; font-weight: 700; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-tr-cotcifra { font-size: 22px; font-weight: 800; color: var(--nv-cosecha); }
.nv-tr-cotdet { margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--nv-linea); }
.nv-tr-cotdet-t { font-size: 11.5px; font-weight: 700; color: var(--nv-tenue); margin-bottom: 6px; }
.nv-tr-cotrow { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 12.5px; padding: 2px 0; }
.nv-tr-cotrow-k { color: var(--nv-tenue); flex: 1; min-width: 0; }
.nv-tr-cotrow-v { font-weight: 700; text-align: right; }
/* El aviso NO es letra pequeña de relleno: dice que el precio es una sugerencia
   entre las partes y que la distancia va en linea recta. Se le da tamaño de
   leerse (12px, interlineado 1,45) y no 10px de descargo escondido. */
.nv-tr-cotaviso { font-size: 12px; line-height: 1.45; color: var(--nv-tenue); margin: 11px 0 0; padding-top: 10px; border-top: 1px solid var(--nv-linea); }
.nv-tr-cotmot { padding: 2px 0 8px; }
.nv-tr-cotmot-t { font-size: 11px; font-weight: 700; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-tr-cotmot-d { font-size: 12.5px; line-height: 1.45; margin: 4px 0 0; }
.nv-tr-cotelige { font-size: 11.5px; color: var(--nv-tenue); margin: 8px 0 5px; }
.nv-tr-cotops { display: flex; flex-wrap: wrap; gap: 6px; }
.nv-tr-cotop { width: auto; padding: 6px 12px; font-size: 12.5px; }

/* ---------- Servicios Rurales ---------- */
.nv-svi { display: inline-flex; align-items: center; justify-content: center; }
.nv-sv-reco { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; background: color-mix(in srgb, var(--nv-savia) 10%, var(--nv-superficie)); border: 1px solid color-mix(in srgb, var(--nv-savia) 32%, transparent); border-radius: var(--nv-r-md); padding: 14px; margin: 14px 0; }
.nv-sv-recoic { width: 40px; height: 40px; flex-shrink: 0; border-radius: 11px; background: var(--nv-grad); display: grid; place-items: center; }
.nv-sv-recoic svg { color: #04140E; }
.nv-sv-recobody { flex: 1; min-width: 160px; }
.nv-sv-recok { font-size: 11px; font-weight: 700; color: var(--nv-savia); text-transform: uppercase; letter-spacing: .04em; }
.nv-sv-recotxt { font-size: 12.5px; line-height: 1.45; color: var(--nv-texto); margin-top: 2px; }
.nv-sv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.nv-sv-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; display: flex; flex-direction: column; }
.nv-sv-ctop { display: flex; align-items: center; gap: 11px; }
.nv-sv-cic { width: 42px; height: 42px; flex-shrink: 0; border-radius: 11px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-sv-cinfo { flex: 1; min-width: 0; }
.nv-sv-cname { font-size: 14px; font-weight: 700; }
.nv-sv-ccat { font-size: 11.5px; color: var(--nv-savia); }
.nv-sv-rate { font-size: 12px; color: var(--nv-dorado); flex-shrink: 0; }
.nv-sv-cnote { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; margin: 10px 0; flex: 1; }
.nv-sv-cfoot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 6px; }
.nv-sv-meta { display: flex; flex-direction: column; gap: 3px; }
.nv-sv-price { font-size: 13px; color: var(--nv-cosecha); font-weight: 700; }
.nv-sv-avail { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; color: var(--nv-tenue); }
.nv-sv-book { width: auto; padding: 8px 15px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; }
.nv-sv-ok { font-size: 12px; color: var(--nv-savia-hi); margin-top: 9px; }

/* ---------- Comunidad ---------- */
.nv-coi { display: inline-flex; align-items: center; justify-content: center; }
.nv-co-comp { margin: 14px 0; }
.nv-co-list { display: flex; flex-direction: column; gap: 8px; }
/* P3 H1 — foro vacio y fallo al publicar. Un foro nuevo esta vacio de verdad:
   decirlo con palabras es distinto de dejar el hueco en blanco, que se ve igual
   que un error. `.nv-co-err` solo posiciona; el recuadro lo pinta `.nv-err`. */
.nv-co-vacio { text-align: center; color: var(--nv-tenue); padding: 36px 20px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.nv-co-vacio-t { font-size: 14px; font-weight: 700; color: var(--nv-texto); }
.nv-co-vacio-d { font-size: 13px; color: var(--nv-tenue); max-width: 380px; line-height: 1.5; }
.nv-co-err:empty { display: none; }
/* P3 H2 — "sin calificaciones todavia" ocupa el sitio donde antes iba un 4.9 ★
   inventado. Va en gris tenue: es una ausencia de dato, no una advertencia. */
.nv-sv-norate { font-size: 11px; color: var(--nv-tenue); white-space: nowrap; }
.nv-sv-goinbox { margin-top: 8px; width: 100%; }

/* P3 H2.1 — confianza. El VERIFICADO (quien es) y las ESTRELLAS (que tal
   trabajo) se ven distinto a proposito: son cosas distintas y confundirlas hace
   que un sello pagado parezca una nota alta. */
.nv-verif { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
  padding: 2px 7px; border-radius: 999px; font-size: 10px; font-weight: 700; letter-spacing: .02em;
  color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--nv-savia) 40%, transparent); }
.nv-verif-i { display: inline-flex; }
.nv-sv-cnameline { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }

/* Selector de estrellas: un tap y listo, sin boton de enviar aparte. Un paso
   menos en un telefono de gama baja con datos malos es un paso que no se pierde. */
.nv-stars { display: flex; align-items: center; gap: 4px; margin-top: 8px; flex-wrap: wrap; }
.nv-stars-l { font-size: 12px; color: var(--nv-tenue); margin-right: 4px; }
.nv-star { background: none; border: none; cursor: pointer; font-size: 20px; line-height: 1;
  padding: 0 2px; color: var(--nv-linea-fuerte); transition: var(--nv-dur-fast); }
.nv-star:hover, .nv-star.on { color: var(--nv-dorado); }

/* Un trabajo (reserva o viaje) en curso. */
.nv-work-list { display: flex; flex-direction: column; gap: 10px; }
.nv-work-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); padding: 14px; }
.nv-work-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.nv-work-rol { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--nv-tenue); }
.nv-work-title { font-size: 14px; font-weight: 600; margin: 6px 0 4px; }
.nv-work-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.nv-work-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.nv-work-b { font-size: 12px; padding: 6px 12px; }
.nv-work-hint, .nv-work-done { font-size: 12px; color: var(--nv-tenue); margin-top: 8px; }
.nv-work-st { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 999px;
  background: var(--nv-superficie-2); color: var(--nv-tenue); }
.nv-work-st.st-completado { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); }
.nv-work-st.st-cancelado { color: var(--nv-critico); background: color-mix(in srgb, var(--nv-critico) 12%, transparent); }
.nv-work-st.st-en_ruta { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 14%, transparent); }
.nv-pf-verif:empty { display: none; }

/* P3 H4 — el curso por dentro. El avance ya no es un adorno: cada leccion que se
   marca vista mueve la barra, y el porcentaje lo calcula el servidor. */
.nv-ac-rdesc { font-size: 13px; color: var(--nv-tenue); line-height: 1.55; margin: 8px 0 14px; }
.nv-ac-pct { font-size: 12px; color: var(--nv-tenue); margin: 6px 0 16px; font-family: var(--nv-f-mono); }
.nv-ac-lessons { display: flex; flex-direction: column; gap: 8px; }
.nv-ac-lesson { display: flex; align-items: center; gap: 11px; padding: 12px 14px;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-ac-lesson.vista { border-color: color-mix(in srgb, var(--nv-savia) 40%, transparent); }
.nv-ac-lnum { width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%; display: grid; place-items: center;
  background: var(--nv-superficie-2); color: var(--nv-tenue); font-size: 12px; font-weight: 700; }
.nv-ac-lesson.vista .nv-ac-lnum { background: color-mix(in srgb, var(--nv-savia) 20%, transparent); color: var(--nv-savia-hi); }
.nv-ac-lmid { flex: 1; min-width: 0; }
.nv-ac-ltitle { font-size: 13.5px; font-weight: 600; }
.nv-ac-lmeta { font-size: 11px; color: var(--nv-tenue); margin-top: 2px; }
.nv-ac-lbtn { font-size: 12px; padding: 6px 12px; flex-shrink: 0; text-decoration: none; }
.nv-ac-lock, .nv-ac-seen { flex-shrink: 0; color: var(--nv-tenue); font-size: 14px; }
.nv-ac-lesson.vista .nv-ac-seen { color: var(--nv-savia-hi); }
/* Publicar un curso: "Vende tu conocimiento" dejo de ser un alert. */
.nv-ac-pubbtn { width: 100%; margin-top: 16px; }
.nv-ac-newlessons { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.nv-ac-newlesson { display: flex; gap: 8px; flex-wrap: wrap; }
.nv-ac-newlesson input { flex: 1; min-width: 140px; }
.nv-co-post { display: flex; gap: 11px; text-align: left; width: 100%; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; cursor: pointer; color: var(--nv-texto); transition: var(--nv-dur-fast); }
.nv-co-post:hover { border-color: var(--nv-linea-fuerte); }
.nv-co-av { width: 40px; height: 40px; flex-shrink: 0; border-radius: 50%; background: var(--nv-grad); color: #04140E; font-weight: 800; display: grid; place-items: center; }
.nv-co-av.sm { width: 32px; height: 32px; font-size: 13px; }
.nv-co-mid { flex: 1; min-width: 0; }
.nv-co-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.nv-co-topic { font-size: 10.5px; font-weight: 700; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 13%, transparent); padding: 2px 9px; border-radius: 999px; }
.nv-co-time { font-family: var(--nv-f-mono); font-size: 10.5px; color: var(--nv-tenue); }
.nv-co-title { font-size: 14px; font-weight: 700; line-height: 1.35; margin: 5px 0; }
.nv-co-meta { display: flex; gap: 12px; font-size: 11.5px; color: var(--nv-tenue); flex-wrap: wrap; }
.nv-co-rep { color: var(--nv-dorado); }
.nv-co-dhead { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px; }
.nv-co-dtitle { flex: 1; font-size: 16px; font-weight: 800; line-height: 1.35; }
.nv-co-dmeta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-bottom: 12px; border-bottom: 1px solid var(--nv-linea); margin-bottom: 12px; }
.nv-co-unlock { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 700; color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 12%, transparent); border: 1px solid color-mix(in srgb, var(--nv-dorado) 35%, transparent); border-radius: 999px; padding: 6px 12px; cursor: pointer; }
.nv-co-replies-list { display: flex; flex-direction: column; gap: 9px; }
.nv-co-reply { display: flex; gap: 10px; padding: 11px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-co-replybody { flex: 1; min-width: 0; }
.nv-co-replytop { display: flex; gap: 10px; align-items: baseline; }
.nv-co-author { font-size: 12.5px; font-weight: 700; }
.nv-co-replytext { font-size: 13px; color: var(--nv-texto); line-height: 1.5; margin-top: 3px; }

/* ---------- Economía del Conocimiento ---------- */
.nv-kni { display: inline-flex; align-items: center; justify-content: center; }
.nv-kn-sell { width: auto; display: inline-flex; align-items: center; gap: 7px; padding: 11px 18px; margin: 14px 0; }
.nv-kn-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.nv-kn-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; display: flex; flex-direction: column; }
.nv-kn-ctop { display: flex; align-items: center; gap: 9px; margin-bottom: 9px; }
.nv-kn-cic { width: 38px; height: 38px; flex-shrink: 0; border-radius: 10px; background: color-mix(in srgb, var(--nv-savia) 14%, transparent); display: grid; place-items: center; }
.nv-kn-kind { font-size: 10.5px; font-weight: 700; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 13%, transparent); padding: 2px 9px; border-radius: 999px; text-transform: uppercase; letter-spacing: .04em; }
.nv-kn-live { font-size: 10px; font-weight: 800; color: #fff; background: var(--nv-critico); padding: 2px 8px; border-radius: 999px; margin-left: auto; letter-spacing: .05em; }
.nv-kn-title { font-size: 14px; font-weight: 700; line-height: 1.35; }
.nv-kn-by { font-size: 12px; color: var(--nv-tenue); margin: 4px 0 12px; flex: 1; }
.nv-kn-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.nv-kn-meta { display: flex; flex-direction: column; gap: 2px; }
.nv-kn-price { font-size: 14px; color: var(--nv-cosecha); font-weight: 800; }
.nv-kn-rate { font-size: 11px; color: var(--nv-dorado); }
.nv-kn-buy { width: auto; padding: 8px 16px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; }

/* ---------- Cerebro del Fundador ---------- */
.nv-fdi { display: inline-flex; align-items: center; justify-content: center; }
.nv-fd-tabs { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 8px; margin-bottom: 14px; border-bottom: 1px solid var(--nv-linea); }
.nv-fd-tab { flex-shrink: 0; font-size: 12.5px; font-weight: 600; padding: 8px 13px; border-radius: var(--nv-r-pill); border: 1px solid var(--nv-linea-fuerte); background: none; color: var(--nv-tenue); cursor: pointer; transition: var(--nv-dur-fast); white-space: nowrap; }
.nv-fd-tab.on { background: var(--nv-savia); color: #04140E; border-color: var(--nv-savia); }
.nv-fd-kpis { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 11px; }
.nv-fd-kpi { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 14px; position: relative; }
.nv-fd-klb { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }

/* P3 · Hito 16 — el motor de precios, visible.
   EL COLOR ES EL MENSAJE y por eso solo hay dos: verde si el boletín entró, ámbar
   si lleva días sin entrar. No hay un «porcentaje de salud» ni una escala de
   tres: un panel con semáforos intermedios invita a mirar el color en vez del
   número, y el número —el último día con dato— es lo único que decide. */
.nv-fd-motor { display: flex; flex-direction: column; gap: 8px;
  padding: 12px 14px; margin-bottom: 14px;
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm, 10px);
  background: var(--nv-fondo-2, rgba(255,255,255,.03)); }
.nv-fd-motor-tit { font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--nv-texto-2, #9fb8ad); }
.nv-fd-motor-estado { display: flex; align-items: baseline; gap: 8px; font-size: 15px; }
.nv-fd-motor-estado b { font-size: 17px; }
.nv-fd-motor-estado.bien { color: var(--nv-savia, #4ade80); }
.nv-fd-motor-estado.mal { color: var(--nv-dorado, #E6C200); }
.nv-fd-motor-cifras { display: flex; flex-wrap: wrap; gap: 6px; }
.nv-fd-motor-nota { font-size: 12px; line-height: 1.35; color: var(--nv-texto-2, #9fb8ad); }
/* Ámbar, no rojo: una plaza sin sembrar no es una avería del sistema — es algo
   que hay que ir a mirar. El rojo se gasta rápido si se usa para todo. */
.nv-fd-motor-mal { font-size: 12.5px; line-height: 1.35; color: var(--nv-dorado, #E6C200); }
.nv-fd-motor-acc { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.nv-fd-kval { font-size: 22px; font-weight: 800; margin-top: 5px; }
.nv-fd-kdelta { position: absolute; top: 14px; right: 14px; font-size: 11px; font-weight: 700; }
.nv-fd-kdelta.up { color: var(--nv-savia-hi); }
.nv-fd-kdelta.down { color: var(--nv-critico); }
.nv-fd-spark { display: flex; align-items: flex-end; gap: 4px; height: 34px; margin-top: 10px; }
.nv-fd-spark.big { height: 60px; }
.nv-fd-sparkbar { flex: 1; background: linear-gradient(180deg, var(--nv-cosecha), var(--nv-savia)); border-radius: 2px 2px 0 0; min-height: 4px; }
.nv-fd-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; margin-bottom: 12px; }
.nv-fd-cardk { font-size: 11px; font-weight: 700; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 10px; }
.nv-fd-ret { display: flex; align-items: baseline; gap: 8px; margin-top: 12px; }
.nv-fd-retval { font-size: 22px; font-weight: 800; color: var(--nv-savia-hi); }
.nv-fd-retlb { font-size: 11.5px; color: var(--nv-tenue); }
/* tabla usuarios */
.nv-fd-table { display: flex; flex-direction: column; gap: 4px; }
.nv-fd-trow { display: grid; grid-template-columns: 2fr 1fr 0.8fr 0.8fr 1fr; gap: 8px; align-items: center; padding: 11px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); font-size: 12.5px; }
.nv-fd-trow.head { background: none; border: none; padding: 4px 12px; }
.nv-fd-th { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nv-fd-tname { display: flex; flex-direction: column; }
.nv-fd-tregion { font-size: 11px; color: var(--nv-tenue); }
.nv-fd-plan { font-size: 10.5px; font-weight: 700; padding: 2px 8px; border-radius: 999px; justify-self: start; }
.nv-fd-plan.plan-pro { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 15%, transparent); }
.nv-fd-plan.plan-free { color: var(--nv-tenue); background: color-mix(in srgb, var(--nv-tenue) 16%, transparent); }
.nv-fd-trust { color: var(--nv-dorado); }
.nv-fd-tactive { font-size: 11.5px; color: var(--nv-tenue); }
/* leads */
.nv-fd-lead { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-cielo); border-radius: var(--nv-r-md); padding: 14px; margin-bottom: 10px; }
.nv-fd-lead.stage-propuesta { border-left-color: var(--nv-savia); }
.nv-fd-lead.stage-nuevo { border-left-color: var(--nv-dorado); }
.nv-fd-leadtop { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.nv-fd-leadname { font-size: 14.5px; font-weight: 700; }
.nv-fd-leadmeta { font-size: 12px; color: var(--nv-tenue); }
.nv-fd-leadscore { font-size: 11px; font-weight: 700; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 14%, transparent); padding: 4px 10px; border-radius: 999px; }
.nv-fd-call { width: 100%; margin-top: 4px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; text-decoration: none; }
/* moderación */
.nv-fd-mod, .nv-fd-health, .nv-fd-terr, .nv-fd-data { display: flex; align-items: center; gap: 11px; padding: 12px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); margin-bottom: 8px; }
.nv-fd-modtitle { font-size: 13px; font-weight: 700; }
.nv-fd-modreason { font-size: 11.5px; color: var(--nv-tenue); }
.nv-fd-modbtn { width: auto; margin-left: auto; padding: 8px 14px; font-size: 12px; }
/* salud */
.nv-fd-hdot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.nv-fd-hdot.ok { background: var(--nv-savia-hi); }
.nv-fd-hdot.warn { background: var(--nv-dorado); }
.nv-fd-hname { flex: 1; font-size: 13px; font-weight: 600; }
.nv-fd-hlat { font-size: 11.5px; color: var(--nv-tenue); }
/* territorial + datos */
.nv-fd-terr, .nv-fd-data { justify-content: space-between; }
.nv-fd-terrtop { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; }
.nv-fd-terr { flex-direction: column; align-items: stretch; }
.nv-fd-terrname, .nv-fd-dataname { font-size: 14px; font-weight: 700; }
.nv-fd-terrkind, .nv-fd-datakind { font-size: 11.5px; color: var(--nv-savia); }
.nv-fd-terrben { font-size: 12.5px; color: var(--nv-tenue); margin-top: 8px; }
.nv-fd-terrstatus { font-size: 10.5px; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.nv-fd-terrstatus.st-activo { color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 15%, transparent); }
.nv-fd-terrstatus.st-piloto { color: var(--nv-dorado); background: color-mix(in srgb, var(--nv-dorado) 15%, transparent); }
.nv-fd-dataprice { font-size: 14px; font-weight: 800; color: var(--nv-cosecha); }
@media (max-width: 720px) {
  .nv-fd-trow { grid-template-columns: 1.6fr 1fr 0.9fr; }
  .nv-fd-trow > :nth-child(4), .nv-fd-trow > :nth-child(5), .nv-fd-th:nth-child(4), .nv-fd-th:nth-child(5) { display: none; }
}

/* ---------- Academia ---------- */
.nv-aci { display: inline-flex; align-items: center; justify-content: center; }
.nv-ac-seg { margin-bottom: 14px; align-self: flex-start; }
.nv-ac-live { display: flex; gap: 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; margin-bottom: 10px; }
.nv-ac-thumb { width: 120px; height: 82px; flex-shrink: 0; border-radius: var(--nv-r-sm); background: linear-gradient(135deg, #123a2c, #0a2620); display: grid; place-items: center; position: relative; }
.nv-ac-livebadge { position: absolute; top: 7px; left: 7px; font-size: 9.5px; font-weight: 800; color: #fff; background: var(--nv-critico); padding: 2px 7px; border-radius: 4px; letter-spacing: .05em; }
.nv-ac-livebody { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.nv-ac-title { font-size: 14.5px; font-weight: 700; line-height: 1.3; }
.nv-ac-by { font-size: 12px; color: var(--nv-tenue); margin-top: 2px; }
.nv-ac-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 8px 0; flex: 1; }
.nv-ac-when { font-size: 11.5px; color: var(--nv-tenue); }
.nv-ac-transbadge { display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; font-weight: 700; color: var(--nv-cielo); background: color-mix(in srgb, var(--nv-cielo) 14%, transparent); padding: 3px 8px; border-radius: 999px; }
.nv-ac-join { width: auto; align-self: flex-start; padding: 8px 15px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 6px; }
.nv-ac-join:disabled { opacity: .5; cursor: default; }
.nv-ac-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; }
.nv-ac-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); overflow: hidden; display: flex; flex-direction: column; }
.nv-ac-cthumb { height: 88px; background: linear-gradient(135deg, #1a4a38, #0d3327); display: grid; place-items: center; }
.nv-ac-cthumb.read { background: linear-gradient(135deg, #2a2340, #171226); }
.nv-ac-cbody { padding: 13px; display: flex; flex-direction: column; flex: 1; }
.nv-ac-rkind { align-self: flex-start; font-size: 10px; font-weight: 700; color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 13%, transparent); padding: 2px 8px; border-radius: 999px; text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px; }
.nv-ac-cmeta { display: flex; gap: 10px; font-size: 11.5px; color: var(--nv-tenue); margin: 6px 0; }
.nv-ac-rate { color: var(--nv-dorado); }
.nv-ac-bar { height: 6px; background: var(--nv-suelo); border-radius: 999px; overflow: hidden; margin: 4px 0 8px; }
.nv-ac-fill { height: 100%; background: linear-gradient(90deg, var(--nv-cosecha), var(--nv-savia)); border-radius: 999px; }
.nv-ac-cfoot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: auto; }
.nv-ac-price { font-size: 13.5px; font-weight: 800; color: var(--nv-cosecha); }
.nv-ac-cbtn { width: auto; padding: 8px 14px; font-size: 12px; display: inline-flex; align-items: center; gap: 5px; }
/* sala en vivo */
.nv-ac-rhead { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.nv-ac-rtitle { font-size: 15px; font-weight: 800; }
.nv-ac-stage { position: relative; height: 220px; border-radius: var(--nv-r-md); background: radial-gradient(circle at 50% 40%, #123a2c, #06140f); display: grid; place-items: center; border: 1px solid var(--nv-linea); overflow: hidden; }
.nv-ac-stagebadge { position: absolute; top: 12px; left: 12px; display: inline-flex; align-items: center; gap: 5px; font-size: 10.5px; font-weight: 800; color: #fff; background: var(--nv-critico); padding: 3px 9px; border-radius: 999px; }
.nv-ac-livedot { width: 7px; height: 7px; border-radius: 50%; background: #fff; }
.nv-ac-subtitle { position: absolute; bottom: 14px; left: 14px; right: 14px; display: flex; align-items: center; gap: 8px; background: rgba(4,20,14,.72); border-radius: var(--nv-r-sm); padding: 9px 12px; }
.nv-ac-subtxt { font-size: 13px; color: #EAF7F1; line-height: 1.4; }
.nv-ac-trans { background: var(--nv-superficie); border: 1px solid color-mix(in srgb, var(--nv-cielo) 30%, var(--nv-linea)); border-radius: var(--nv-r-md); padding: 15px; margin-top: 14px; }
.nv-ac-transhead { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--nv-cielo); margin-bottom: 11px; }
.nv-ac-toggles { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.nv-ac-toggle { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; padding: 7px 12px; border-radius: var(--nv-r-pill); border: 1px solid var(--nv-linea-fuerte); color: var(--nv-tenue); }
.nv-ac-toggle.on { background: color-mix(in srgb, var(--nv-savia) 15%, transparent); color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-ac-langrow { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.nv-ac-langk { font-size: 12.5px; color: var(--nv-tenue); }
.nv-ac-transnote { font-size: 12px; color: var(--nv-tenue); line-height: 1.5; }
@media (max-width: 720px) { .nv-ac-live { flex-direction: column; } .nv-ac-thumb { width: 100%; height: 120px; } }

/* ---------- Estudio Creativo ---------- */
.nv-sti { display: inline-flex; align-items: center; justify-content: center; }
.nv-st-comp { margin: 14px 0 10px; }
.nv-st-gen { width: 100%; }
.nv-st-out { margin-top: 14px; }
.nv-st-preview { display: flex; gap: 14px; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; }
.nv-st-media { width: 120px; height: 120px; flex-shrink: 0; border-radius: var(--nv-r-md); display: grid; place-items: center; background: linear-gradient(135deg, #1a4a38, #0d3327); }
.nv-st-media.video { background: linear-gradient(135deg, #2a2340, #171226); }
.nv-st-pbody { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.nv-st-ptitle { font-size: 15px; font-weight: 700; }
.nv-st-pubthis { width: auto; align-self: flex-start; margin-top: auto; padding: 9px 15px; font-size: 12.5px; display: inline-flex; align-items: center; gap: 6px; }
.nv-st-channels { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 9px; margin-bottom: 16px; }
.nv-st-chan { display: flex; align-items: center; gap: 9px; padding: 11px 12px; background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-md); cursor: pointer; color: var(--nv-texto); transition: var(--nv-dur-fast); }
.nv-st-chan.on { border-color: var(--nv-savia); background: color-mix(in srgb, var(--nv-savia) 10%, var(--nv-superficie)); }
.nv-st-chan.off { opacity: .72; }
.nv-st-chbadge { width: 30px; height: 30px; flex-shrink: 0; border-radius: 8px; color: #fff; font-weight: 800; font-size: 15px; display: grid; place-items: center; }
.nv-st-chname { flex: 1; font-size: 13px; font-weight: 600; }
.nv-st-chstate { font-size: 12px; color: var(--nv-savia); font-weight: 700; }
.nv-st-chconnect { font-size: 11px; color: var(--nv-cielo); }
.nv-st-addnet { grid-column: 1 / -1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 11px; background: none; border: 1px dashed var(--nv-linea-fuerte); border-radius: var(--nv-r-md); color: var(--nv-tenue); font-size: 12.5px; cursor: pointer; }
.nv-st-addnet:hover { border-color: var(--nv-savia); color: var(--nv-savia); }
.nv-st-when { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.nv-st-when .nv-pf-label { margin-bottom: 0; }
.nv-st-publish { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
@media (max-width: 720px) { .nv-st-preview { flex-direction: column; } .nv-st-media { width: 100%; height: 150px; } }

/* Fallo visible en la bandeja: aqui se negocia plata, no puede fallar callado. */
.nv-ib-err { margin: 8px 12px; padding: 8px 12px; border-radius: 10px; font-size: 12.5px; color: var(--nv-alerta, #E5534B); background: rgba(229,83,75,.08); border: 1px solid rgba(229,83,75,.25); }

/* ===== Tarjeta Universal: se llena conversando y se revisa antes de publicar (P1.6) ===== */
.nv-lc-mini { align-self: flex-start; max-width: 88%; margin-top: 2px; padding: 12px 14px;
  border-radius: 13px; border-bottom-left-radius: 4px; background: var(--nv-superficie);
  border: 1px solid var(--nv-linea); display: flex; flex-direction: column; gap: 8px; }
.nv-lc-mini-head { display: flex; align-items: center; gap: 8px; }
.nv-lc-mini-t { font-weight: 700; font-size: 13.5px; line-height: 1.3; }
.nv-lc-mini-datos { display: flex; flex-wrap: wrap; gap: 6px; }
.nv-lc-chip { font-size: 11.5px; padding: 3px 9px; border-radius: 999px;
  background: rgba(255,255,255,.05); border: 1px solid var(--nv-linea); font-family: var(--nv-f-mono); }
.nv-lc-falta { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--nv-dorado, #E6C200); }
.nv-lc-listo { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--nv-savia-hi, #2FA98C); }
.nv-lc-ic { display: inline-flex; }
.nv-lc-cta { margin-top: 2px; align-self: flex-start; }

.nv-lc-scrim { position: fixed; inset: 0; z-index: 120; background: rgba(4,20,14,.72);
  display: flex; align-items: flex-end; justify-content: center; padding: 0; }
@media (min-width: 720px) { .nv-lc-scrim { align-items: center; padding: 24px; } }
.nv-lc-modal { width: 100%; max-width: 560px; max-height: 92vh; display: flex; flex-direction: column;
  background: var(--nv-suelo); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-lg, 18px) var(--nv-r-lg, 18px) 0 0; overflow: hidden; }
@media (min-width: 720px) { .nv-lc-modal { border-radius: var(--nv-r-lg, 18px); } }
.nv-lc-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 15px 18px; border-bottom: 1px solid var(--nv-linea); font-size: 15px; }
.nv-lc-x { background: none; border: none; color: var(--nv-texto); font-size: 17px; cursor: pointer; padding: 4px 8px; }
.nv-lc-body { padding: 14px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 12px; }
.nv-lc-campo { display: flex; flex-direction: column; gap: 5px; }
.nv-lc-lb { font-size: 12px; color: var(--nv-texto-2, #9fb8ad); }
.nv-lc-in { width: 100%; padding: 10px 12px; border-radius: var(--nv-r-sm, 10px);
  border: 1px solid var(--nv-linea); background: var(--nv-superficie); color: var(--nv-texto);
  font: inherit; font-size: 13.5px; resize: vertical; }
.nv-lc-in:focus { outline: 2px solid var(--nv-savia); outline-offset: 1px; }
.nv-lc-req .nv-lc-in { border-color: var(--nv-dorado, #E6C200); }
.nv-lc-aviso { display: none; font-size: 12.5px; color: var(--nv-dorado, #E6C200); }
.nv-lc-aviso.on { display: block; }

/* P3 · Hilo 2 paso 6 — «¿a cuánto está hoy?», pegado a la casilla del precio.
   DELIBERADAMENTE DISCRETO: es una ayuda para decidir, no una recomendación de
   la app. Si pesara más que la casilla del precio, el productor dejaría de poner
   el suyo y pondría el nuestro — y el número de la plaza es MAYORISTA, no lo que
   le pagan a él. Por eso va en tono apagado y sin acento de color en la cifra. */
.nv-lc-ref { display: flex; flex-direction: column; gap: 4px;
  padding: 10px 12px; margin-top: -2px;
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm, 10px);
  background: var(--nv-fondo-2, rgba(255,255,255,.03)); }
.nv-lc-ref:empty { display: none; }
.nv-lc-ref-tit { font-size: 11.5px; letter-spacing: .02em; text-transform: uppercase;
  color: var(--nv-texto-2, #9fb8ad); }
.nv-lc-ref-fila { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  font-size: 13px; color: var(--nv-texto); }
.nv-lc-ref-et { color: var(--nv-texto-2, #9fb8ad); }
.nv-lc-ref-nota { font-size: 12px; color: var(--nv-texto-2, #9fb8ad); }
/* Los avisos NO son decoración: cada uno tapa una forma concreta de leer mal el
   número (mayorista vs finca, primera vs segunda, dato viejo, calidades mezcladas). */
.nv-lc-ref-aviso { font-size: 12px; line-height: 1.35; color: var(--nv-dorado, #E6C200); }
.nv-lc-pie { display: flex; gap: 10px; padding: 14px 18px; border-top: 1px solid var(--nv-linea); }
.nv-lc-pie .nv-btn { flex: 1; width: auto; }
.nv-lc-off { opacity: .5; cursor: not-allowed; }

/* ----- P1.7: foto tomada con el celular, fechas y GPS ----- */
.nv-lc-fotos { display: flex; flex-wrap: wrap; gap: 8px; }
.nv-lc-foto { position: relative; width: 84px; height: 84px; border-radius: var(--nv-r-sm, 10px);
  overflow: hidden; border: 1px solid var(--nv-linea); background: var(--nv-superficie); }
.nv-lc-foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
.nv-lc-foto-x { position: absolute; top: 3px; right: 3px; width: 22px; height: 22px; line-height: 1;
  border: none; border-radius: 50%; background: rgba(4,20,14,.78); color: #fff; cursor: pointer; font-size: 12px; }
/* Insignia de foto de banco — la celda es chica (84px), el credito completo
   va en el `title` (tooltip). Ver `.nv-fotolibre-credito`/`.nv-foto-credito`
   para el mismo credito en tamaño completo, en el buscador y en la tarjeta
   publicada. */
.nv-lc-foto-banco { position: absolute; left: 3px; bottom: 3px; padding: 1px 5px;
  font-size: 8.5px; font-weight: 700; letter-spacing: .02em; color: #fff;
  background: rgba(4,20,14,.78); border-radius: var(--nv-r-pill); cursor: help; }
.nv-lc-addfoto, .nv-lc-geo { width: auto; align-self: flex-start; display: inline-flex;
  align-items: center; gap: 8px; padding: 9px 14px; margin-top: 4px; }
.nv-lc-fotomsg, .nv-lc-geotxt { display: none; margin-top: 6px; font-size: 12px; color: var(--nv-texto-2, #9fb8ad); }
.nv-lc-fotomsg.on, .nv-lc-geotxt.on { display: block; }
.nv-lc-fotomsg.err, .nv-lc-geotxt.err { display: block; color: var(--nv-alerta, #E5534B); }
.nv-lc-mini-fotos { display: flex; gap: 6px; }
.nv-lc-mini-fotos img { width: 56px; height: 56px; object-fit: cover; border-radius: 8px;
  border: 1px solid var(--nv-linea); }

/* ----- P1.8: punto de encuentro (solo con el trato cerrado) ----- */
.nv-ib-punto { margin: 12px; padding: 13px 15px; border-radius: var(--nv-r-md, 14px);
  border: 1px solid var(--nv-savia); background: rgba(47,169,140,.08);
  display: flex; flex-direction: column; gap: 7px; }
.nv-ib-punto-t { display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: 13px; }
.nv-ib-punto-d { font-size: 13px; }
.nv-ib-punto-c { font-size: 11.5px; font-family: var(--nv-f-mono); color: var(--nv-texto-2, #9fb8ad); }
.nv-ib-punto-btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 3px; }
.nv-ib-punto-b { width: auto; display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 13px; font-size: 12.5px; text-decoration: none; }

/* ----- P1.9: composer con 5 funciones (dictar, foto, archivo, escribir, leer) ----- */
.nv-tool { position: relative; transition: color .15s ease, background .15s ease; }
.nv-tool-on { color: var(--nv-savia); background: rgba(47,169,140,.16); border-radius: 10px; }
.nv-tool-on::after { content: ''; position: absolute; top: 3px; right: 3px; width: 6px; height: 6px;
  border-radius: 50%; background: var(--nv-savia); animation: nvPulso 1.1s ease-in-out infinite; }
@keyframes nvPulso { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
.nv-tool:disabled { opacity: .45; cursor: not-allowed; }
/* Sugerencia (no bloquea) vs falta (sí bloquea) */
.nv-lc-sug { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--nv-texto-2, #9fb8ad); }
.nv-lc-aviso.sug { display: block; color: var(--nv-texto-2, #9fb8ad); }

/* ----- P1.10: modo conversación manos libres ----- */
.nv-conv-barra { display: flex; align-items: center; gap: 10px; padding: 8px 14px;
  border-top: 1px solid var(--nv-linea); }
.nv-conv-btn { display: inline-flex; align-items: center; gap: 7px; padding: 8px 14px;
  border-radius: 999px; border: 1px solid var(--nv-savia); background: transparent;
  color: var(--nv-savia); font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; }
.nv-conv-btn:hover { background: rgba(47,169,140,.10); }
.nv-conv-on { background: var(--nv-savia); color: #04140E; }
.nv-conv-estado { display: none; font-size: 12px; color: var(--nv-texto-2, #9fb8ad); }
.nv-conv-estado.on { display: inline-flex; align-items: center; gap: 6px; }
.nv-conv-estado.on::before { content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; animation: nvPulso 1.1s ease-in-out infinite; }
.nv-conv-estado.escuchando { color: var(--nv-savia); }
.nv-conv-estado.hablando { color: var(--nv-cielo, #2E86DE); }
.nv-conv-estado.pensando { color: var(--nv-dorado, #E6C200); }

/* ----- P1.11: detalle de publicación, portada, copiar ----- */
/* El texto del chat SE PUEDE seleccionar: es lo que el usuario quiere copiar. */
.nv-amsg { user-select: text; -webkit-user-select: text; position: relative; }
.nv-amsg-copy { position: absolute; top: 4px; right: 4px; width: 24px; height: 24px;
  display: none; align-items: center; justify-content: center; border: none; cursor: pointer;
  border-radius: 7px; background: rgba(255,255,255,.07); color: var(--nv-texto-2, #9fb8ad); }
.nv-amsg.ai:hover .nv-amsg-copy, .nv-amsg-copy:focus { display: inline-flex; }
.nv-amsg-copy:hover { background: rgba(255,255,255,.14); color: var(--nv-texto); }
.nv-amsg-copy.ok { display: inline-flex; color: var(--nv-savia); background: rgba(47,169,140,.18); }
@media (hover: none) { .nv-amsg.ai .nv-amsg-copy { display: inline-flex; } }

/* Portada en las tarjetas */
.nv-mn-media.con-foto { padding: 0; overflow: hidden; }
.nv-mn-media img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit; }
.nv-mk-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.nv-mk-media { position: relative; overflow: hidden; }
.nv-mn-click, .nv-mk-click { cursor: pointer; }
.nv-mn-click:hover, .nv-mk-click:hover { border-color: var(--nv-savia); }

/* Modal de detalle */
.nv-dt-scrim { position: fixed; inset: 0; z-index: 130; background: rgba(4,20,14,.74);
  display: flex; align-items: flex-end; justify-content: center; }
@media (min-width: 720px) { .nv-dt-scrim { align-items: center; padding: 24px; } }
.nv-dt-hoja { width: 100%; max-width: 560px; max-height: 92vh; display: flex; flex-direction: column;
  background: var(--nv-suelo); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-lg, 18px) var(--nv-r-lg, 18px) 0 0; overflow: hidden; }
@media (min-width: 720px) { .nv-dt-hoja { border-radius: var(--nv-r-lg, 18px); } }
.nv-dt-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 15px 18px; border-bottom: 1px solid var(--nv-linea); }
/* MEDIDO A 360px (revision UI Daniel, 2026-08-13, licitaciones detalle
   "MANIFESTACION DE INTERES"): sin `min-width:0` un flex item se niega a
   encogerse por debajo de su ancho de contenido, asi que un titulo largo se
   salia del contenedor en vez de partir linea. `flex:1` + `overflow-wrap`
   le dan el espacio que sobra y le dejan romper la palabra si hace falta;
   `.nv-dt-x` se fija con `flex:none` para que el boton de cerrar nunca
   pierda su tamano de toque cuando el titulo pelea por espacio. */
.nv-dt-title { font-size: 15px; line-height: 1.3; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.nv-dt-x { background: none; border: none; color: var(--nv-texto); font-size: 17px; cursor: pointer; padding: 4px 8px; flex: none; }
.nv-dt-body { padding: 14px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 11px; }
/* La foto se ve ENTERA (`contain`, no `cover`): en el agro la foto es la prueba
   del producto —el racimo, el tractor, el lote— y recortarla es esconder lo que
   se está comprando. Alto natural con tope de 46vh; lo que sigue se lee
   bajando, que para eso `.nv-dt-body` ya scrollea. */
/* `align-items: flex-start` NO es cosmético: el `stretch` por defecto del flex
   estiraba la foto al alto disponible del contenedor y la dejaba en una franja
   de 81 px (medido en el navegador). Con flex-start conserva su alto natural. */
/* `flex-shrink: 0` en la galería: el cuerpo del modal es una columna flex y, sin
   esto, comprimía la foto para que cupiera todo sin scroll —justo lo contrario
   de lo que se busca—. Ahora la foto manda su alto y lo que sobra se baja
   scrolleando el cuerpo. */
.nv-dt-fotos { display: flex; align-items: flex-start; flex: 0 0 auto; gap: 8px;
  overflow-x: auto; overflow-y: hidden; padding-bottom: 4px; scroll-snap-type: x mandatory; }
.nv-dt-fotos img { width: 86%; max-width: 340px; height: auto; max-height: 46vh;
  object-fit: contain; border-radius: 12px; background: rgba(0,0,0,.28);
  flex-shrink: 0; scroll-snap-align: center; border: 1px solid var(--nv-linea); }
/* Con una sola foto no hay carrusel que insinuar: ocupa todo el ancho. */
.nv-dt-fotos.una img { width: 100%; max-width: 100%; }
.nv-dt-sinfoto { display: flex; align-items: center; gap: 8px; padding: 18px; border-radius: 12px;
  border: 1px dashed var(--nv-linea); color: var(--nv-texto-2, #9fb8ad); font-size: 12.5px; }
.nv-dt-precio { font-family: var(--nv-f-mono); font-size: 21px; font-weight: 800; color: var(--nv-savia); }
.nv-dt-fila { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.nv-dt-lb { color: var(--nv-texto-2, #9fb8ad); min-width: 86px; }
.nv-dt-val { font-weight: 600; }
.nv-dt-desc { font-size: 13.5px; line-height: 1.55; white-space: pre-wrap; user-select: text; }
.nv-dt-apx { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--nv-texto-2, #9fb8ad); }
.nv-dt-aviso { display: none; font-size: 12.5px; }
.nv-dt-aviso.on { display: block; color: var(--nv-texto-2, #9fb8ad); }
.nv-dt-aviso.ok { display: block; color: var(--nv-savia); }
.nv-dt-aviso.err { display: block; color: var(--nv-alerta, #E5534B); }
.nv-dt-pie { padding: 14px 18px; border-top: 1px solid var(--nv-linea); }
.nv-dt-pie .nv-btn { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }

/* ── Formulario de nueva parcela (hito 6) ───────────────────────────────────
   VA AL FINAL DEL ARCHIVO A PROPOSITO. La leccion del hilo 8 costo el movil
   entero: un bloque escrito ANTES de la regla base pierde, porque un @media no
   suma especificidad. Aqui no hay @media, pero la regla se respeta igual —
   anadir al final es lo unico que no puede alterar la cascada de nadie.
   Reusa el overlay `.nv-ag-modal`, que ya existia: no se crea un modal nuevo. */
.nv-un-fld { display: flex; flex-direction: column; gap: 5px; }
.nv-un-fldlb { font-size: 12px; font-weight: 600; color: var(--nv-tenue); }
.nv-un-fldin { width: 100%; background: var(--nv-fondo); border: 1px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-md, 10px); padding: 10px 12px; color: var(--nv-texto); font-size: 14px; font-family: inherit; outline: none; }
.nv-un-fldin:focus { border-color: var(--nv-savia); }
.nv-un-fldin::placeholder { color: var(--nv-tenue); opacity: .75; }
.nv-un-fldmsg { font-size: 12.5px; line-height: 1.45; }
.nv-un-fldmsg.is-bad { color: var(--nv-critico, #E5534B); }

/* ============================================================================
   HILO «FINCA DEMO» — lo que el dueno fotografio con el movil (2026-08-02).

   VA AL FINAL A PROPOSITO, por la misma razon escrita en el bloque de arriba:
   anadir al final es lo unico que no puede alterar la cascada de nadie.

   Son cinco arreglos de FORMA. Ni un texto cambia aqui: los textos que el dueno
   marco como correctos siguen siendo los mismos, lo que estaba mal era como se
   veian.
   ========================================================================== */

/* ─── 1. Los chips de tipo NO TENIAN NI UN ESTILO ──────────────────────────
   `.nv-as-fam` y `.is-sel` no estaban declaradas en NINGUN sitio (esta es la
   unica hoja del proyecto). O sea que los cuatro botones de «Registrar
   novedad» —uso / mantenimiento / novedad / reparacion— se pintaban como
   botones pelados del navegador, y al tocar uno NO PASABA NADA VISIBLE.

   Y eso costaba plata: con tipo elegido el registro es gratis; sin tipo, el
   texto se va a la IA y cuesta 1 token (`routers/assets.js:449`). El productor
   no tenia forma de saber si su toque habia quedado, o sea que no podia saber
   si iba a pagar. Un estado invisible que decide el precio es un fallo mudo. */
.nv-as-fams { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.nv-as-fam { font: 600 12.5px/1 var(--nv-f-body); color: var(--nv-tenue);
  background: var(--nv-suelo); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-pill); padding: 8px 14px; cursor: pointer;
  transition: var(--nv-dur-fast); width: auto; }
.nv-as-fam:hover { border-color: var(--nv-savia); color: var(--nv-texto); }
.nv-as-fam:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: 2px; }
/* El estado elegido se ve de tres formas a la vez —fondo, color y peso— para
   que se distinga tambien con poca luz o con la pantalla al sol, que es como
   se usa esto en el campo. */
.nv-as-fam.is-sel { background: var(--nv-savia); border-color: var(--nv-savia);
  color: #04140E; font-weight: 700; }

/* ─── 2. «Proximo mantenimiento»: la nota que se decia cinco veces ─────────
   Ahora se dice UNA vez y como parte de la tarjeta, no como cinco parrafos
   sueltos colgando de cada tarea. */
.nv-as-tareasnota { display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px; margin-bottom: 8px;
  background: color-mix(in srgb, var(--nv-dorado) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--nv-dorado) 30%, transparent);
  border-radius: var(--nv-r-sm); color: var(--nv-tenue);
  font-size: 12.5px; line-height: 1.45; }
.nv-as-tareasnota .nv-asi { flex-shrink: 0; line-height: 0; margin-top: 1px; }
/* La lista de tareas en sí — reportado 2026-08-21 ("Bujía—, Filtro de
   aire—..."): el comentario de arriba describe un arreglo de FORMA que
   nunca llegó a tener CSS. `.nv-as-tareas`/`.nv-as-tarea`/`.nv-as-tk`/
   `.nv-as-tv` no tenían NI UNA regla — el nombre y el "—" salían como
   `<span>` en línea, pegados, sin fila ni tarjeta. */
.nv-as-tareas { display: flex; flex-direction: column; gap: 7px; }
.nv-as-tarea { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-linea-fuerte);
  border-radius: var(--nv-r-sm); padding: 10px 12px; }
.nv-as-tarea.st-alto, .nv-as-tarea.st-vencido { border-left-color: var(--nv-critico); }
.nv-as-tarea.st-medio { border-left-color: var(--nv-dorado); }
.nv-as-tarea.st-ok { border-left-color: var(--nv-savia); }
.nv-as-tk { font-size: 13px; font-weight: 700; }
.nv-as-tv { font-size: 13px; font-weight: 700; color: var(--nv-tenue); margin-left: auto; }
.nv-as-tarea.st-alto .nv-as-tv, .nv-as-tarea.st-vencido .nv-as-tv { color: var(--nv-critico); }
.nv-as-tsub { flex-basis: 100%; font-size: 11.5px; color: var(--nv-tenue); line-height: 1.4; }

/* ─── 3. «Compras que parecen equipos»: era un muro ────────────────────────
   Cada candidata pasa a ser una tarjeta con su icono arriba y sus dos acciones
   en su propia fila al pie, en vez de ocho filas de texto y botones a la misma
   altura sin nada que separara una compra de la siguiente. */
.nv-as-candlist { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.nv-as-propit { display: flex; flex-direction: column; gap: 10px;
  padding: 12px; background: var(--nv-superficie);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-as-candtop { display: flex; align-items: flex-start; gap: 10px; }
.nv-as-candic { width: 30px; height: 30px; flex-shrink: 0; border-radius: 8px;
  background: color-mix(in srgb, var(--nv-savia) 12%, transparent);
  display: grid; place-items: center; }
.nv-as-cname { font-size: 13.5px; font-weight: 600; line-height: 1.35;
  overflow-wrap: anywhere; }
.nv-as-ctype { font-size: 11.5px; color: var(--nv-tenue); margin-top: 2px; }
/* Los dos botones reparten el ancho: en un telefono quedan del mismo tamano y
   ninguno se sale. */
.nv-as-candacts { display: flex; gap: 8px; }
.nv-as-candacts .nv-btn { flex: 1; padding: 10px 12px; font-size: 13px; }

/* ─── 4. Pecuaria: los registros AHORA SE ABREN ────────────────────────────
   `.nv-lv-ev` pasa de `div` a `button`, asi que necesita que le quiten lo que
   el navegador le pone por su cuenta. Se conserva su aspecto de fila: lo unico
   nuevo es que se ve que se puede tocar. */
button.nv-lv-ev { width: 100%; text-align: left; font: inherit; color: inherit;
  cursor: pointer; }
button.nv-lv-ev:hover { border-color: var(--nv-savia);
  background: color-mix(in srgb, var(--nv-savia) 6%, var(--nv-superficie)); }
button.nv-lv-ev:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: 2px; }
/* La flecha mira hacia la derecha: `NVIcon.get('back')` apunta a la izquierda y
   aqui significa «entrar», no «volver». */
.nv-lv-evgo { flex-shrink: 0; line-height: 0; color: var(--nv-fantasma);
  transform: rotate(180deg); transition: var(--nv-dur-fast); }
button.nv-lv-ev:hover .nv-lv-evgo { color: var(--nv-savia); }

/* ─── 5. Monitoreo del cultivo: evidencia y observacion ────────────────────
   El aviso de que la evidencia todavia no se guarda. Sale DENTRO de la
   pantalla y pegado al boton que lo dispara — nunca una ventana del navegador,
   que es lo que habia aqui. */
.nv-cr-visual { display: flex; flex-direction: column; gap: 8px; }
.nv-cr-evaviso { font-size: 12.5px; line-height: 1.45; color: var(--nv-tenue);
  min-height: 0; }
.nv-cr-evaviso.on { padding: 10px 12px;
  background: color-mix(in srgb, var(--nv-dorado) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--nv-dorado) 30%, transparent);
  border-radius: var(--nv-r-sm); }

/* La caja de observacion: la unica entrada que tiene esta pantalla. */
.nv-cr-obs { margin-top: 18px; display: flex; flex-direction: column; gap: 8px; }
.nv-cr-obscta { width: 100%; }
.nv-cr-obstok { font-size: 11.5px; color: var(--nv-fantasma); margin: 0; text-align: center; }
.nv-cr-obsout:empty { display: none; }
.nv-cr-obsok { padding: 12px; border-radius: var(--nv-r-md);
  background: var(--nv-superficie);
  border: 1px solid color-mix(in srgb, var(--nv-savia) 35%, transparent);
  border-left: 3px solid var(--nv-savia);
  display: flex; flex-direction: column; gap: 6px; }
.nv-cr-obsoktitle { font-size: 13.5px; font-weight: 700; }
.nv-cr-obssum { font-size: 12.5px; line-height: 1.5; color: var(--nv-tenue); margin: 0; }
.nv-cr-obsnote { font-size: 12px; color: var(--nv-tenue); }
.nv-cr-obsrever { width: 100%; margin-top: 2px; }

/* ─── 6. Insumos: dictar el nombre ─────────────────────────────────────────
   El micro va en su propia fila con la nota al lado, para que quede claro que
   dicta el NOMBRE y que la cantidad y la unidad van a mano. */
.nv-ag-inmic { display: flex; align-items: center; gap: 10px; margin: 4px 0 8px; }
.nv-ag-inmicb { flex-shrink: 0; }
.nv-ag-inmicnote { font-size: 11.5px; line-height: 1.4; color: var(--nv-fantasma); }

/* ══════════════════════════════════════════════════════════════════════════
   2026-08-02 — Los cuatro cableados de hoy. Nada de esto es decoración: cada
   bloque existe porque un dato que el productor necesita ver no tenía dónde
   verse. `[hidden]` (línea 6) manda sobre estos `display`, así que los campos
   que se ocultan por catálogo se ocultan de verdad.
   ══════════════════════════════════════════════════════════════════════════ */

/* ─── Pecuaria: hembras en ordeño ──────────────────────────────────────────
   El campo que convierte 2,54 L/animal (todo el censo, rojo) en 5,7 L/vaca
   (las que de verdad ordeñan, bien). Sólo se ve si el catálogo dice que esta
   orientación calcula `lv.ind.litrosVacaDia`. */
.nv-lv-ordeno { display: flex; flex-direction: column; gap: 5px; }
.nv-lv-ordeno .nv-lv-areanote { margin-bottom: 2px; }
/* El error del formulario vive DENTRO del formulario y no se borra solo: hay
   un campo a la vista que corregir. En rojo, porque es un choque de datos. */
.nv-lv-formerr:empty { display: none; }
.nv-lv-formerr { font-size: 12px; line-height: 1.4; color: var(--nv-critico);
  background: color-mix(in srgb, var(--nv-critico) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--nv-critico) 35%, transparent);
  border-radius: var(--nv-r-sm); padding: 8px 10px; }
/* Editar la ficha del lote. Discreto y arriba a la derecha: la tarjeta entera
   ya sirve para ELEGIR el lote, y este botón no puede competir con eso. */
.nv-lv-herded { flex-shrink: 0; align-self: flex-start; line-height: 0;
  background: transparent; border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-sm); padding: 5px; cursor: pointer; color: var(--nv-tenue);
  transition: var(--nv-dur-fast); }
.nv-lv-herded:hover { border-color: var(--nv-savia); color: var(--nv-savia); }
.nv-lv-herded:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: 2px; }
/* El botón que lleva del motivo en gris al sitio donde se declara el dato. */
.nv-lv-indcta { margin-left: auto; }

/* ─── Agro: las ACCIONES, separadas de la lista de compras ─────────────────
   Iban revueltas con los insumos y recortadas a 60 caracteres, así que «no
   subir la dosis de nitrógeno» se leía como un producto que había que ir a
   comprar. El borde punteado dice a la vista que esto NO se compra. */
.nv-ag-acciones { margin-top: 14px; }
.nv-ag-accion { border-style: dashed; }
.nv-ag-accion .nv-ag-inic { color: var(--nv-tenue); }
.nv-ag-accion .nv-ag-inname { font-weight: 600; line-height: 1.4; }

/* ─── Activos: qué tareas cubrió el mantenimiento ──────────────────────── */
.nv-as-logtareas { display: flex; flex-direction: column; gap: 7px; margin: 4px 0 8px; }
/* «Hubo un mantenimiento que no dijo qué se hizo». En ámbar y no en rojo: no
   es una avería, es un registro incompleto que el productor puede corregir. */
.nv-as-tsindetalle { display: flex; align-items: flex-start; gap: 7px; width: 100%;
  margin-top: 6px; font-size: 11.5px; line-height: 1.4; color: var(--nv-alerta); }
.nv-as-tsindetalle .nv-asi { flex-shrink: 0; line-height: 0; margin-top: 1px; }

/* ════════════════════════════════════════════════════════════════════════════
   MERCADO · EL CLUSTER Y EL MUNICIPIO (hito 34)

   Lo que se pinta cuando el boletín del DANE no conoce la palabra pero
   Tuxani sí tiene algo: tractores, fletes, asesorías, bueyes.

   VA AL FINAL DEL ARCHIVO Y CON NOMBRES NUEVOS, a propósito. La lección que ya
   costó el menú cayéndose al fondo está escrita arriba: `@media` NO suma
   especificidad, así que una regla base escrita DESPUÉS de un bloque `@media`
   lo pisa. Aquí no hay nada que pisar —ningún selector de abajo existe en otra
   parte del archivo— y el único `@media` propio va inmediatamente después de
   sus reglas base, que es el orden que el resto del archivo respeta.
   ════════════════════════════════════════════════════════════════════════════ */

/* La cabecera del cluster: por qué no hay precio de plaza. Comparte el tono
   ámbar de los avisos porque es lo mismo — una advertencia, no un error. */
.nv-mk-clhead { display: flex; gap: 9px; align-items: flex-start; padding: 12px 13px; margin: 4px 0 10px;
  font-size: 13px; line-height: 1.45; color: var(--nv-texto);
  background: color-mix(in srgb, var(--nv-dorado) 9%, var(--nv-superficie));
  border: 1px solid color-mix(in srgb, var(--nv-dorado) 28%, transparent); border-radius: var(--nv-r-sm); }
.nv-mk-clhead .nv-mii { color: var(--nv-dorado); flex-shrink: 0; margin-top: 1px; }
.nv-mk-clhead p { margin: 0; min-width: 0; }
.nv-mk-clcorr { font-size: 12px; color: var(--nv-dorado); margin: 0 0 8px; }
.nv-mk-clrazon { font-size: 12px; color: var(--nv-tenue); margin: 0 0 10px; }

/* El aviso pasó de [icono + texto] a [icono + bloque], porque ahora debajo del
   texto puede ir su CIFRA y, en el de «no sé dónde estás», el buscador entero.
   `min-width:0` es obligatorio: sin él un municipio largo desborda el flex y en
   un teléfono la caja se sale de la pantalla. */
.nv-mk-avisotx { min-width: 0; flex: 1; }
.nv-mk-avisonum { margin-top: 5px; font-size: 12px; font-weight: 700; color: var(--nv-texto); }

.nv-mk-clorden { font-size: 12px; color: var(--nv-tenue); margin-bottom: 10px; }
.nv-mk-cltotal { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
/* «De tu municipio» — el rótulo que hace comprobable el orden. En verde savia
   y no en gris: es el dato que este hito vino a poner delante. */
.nv-mk-clcerca { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: var(--nv-r-pill);
  color: var(--nv-savia-hi); background: color-mix(in srgb, var(--nv-savia) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--nv-savia) 34%, transparent); }

.nv-mk-clbloque { margin-bottom: 18px; }
.nv-mk-clcab { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 8px;
  padding-bottom: 6px; border-bottom: 1px solid var(--nv-linea); }
.nv-mk-clkey { font-size: 13px; font-weight: 800; }
.nv-mk-cln { font-size: 11.5px; color: var(--nv-tenue); font-variant-numeric: tabular-nums; }
.nv-mk-clmas { font-size: 11.5px; color: var(--nv-tenue); margin-top: 6px; }

.nv-mk-item { margin-bottom: 8px; }
.nv-mk-idesc { margin: 8px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--nv-tenue); }
.nv-mk-ipie { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
/* Por qué entró y si compra o vende. Gris por defecto: son matices, no titulares. */
.nv-mk-ichip { font-size: 10.5px; padding: 2px 7px; border-radius: var(--nv-r-pill);
  color: var(--nv-tenue); background: var(--nv-suelo); border: 1px solid var(--nv-linea); }
.nv-mk-ichip.is-cerca { color: var(--nv-savia-hi);
  background: color-mix(in srgb, var(--nv-savia) 12%, transparent);
  border-color: color-mix(in srgb, var(--nv-savia) 30%, transparent); }
/* «Está buscando» sí es un titular: con 27 ofertas y 1 demanda en toda la base,
   el que busca es justo el que el productor vino a encontrar. */
.nv-mk-ichip.is-demanda { color: var(--nv-cosecha); font-weight: 700;
  background: color-mix(in srgb, var(--nv-cosecha) 12%, transparent);
  border-color: color-mix(in srgb, var(--nv-cosecha) 32%, transparent); }

/* ─── El buscador de municipios ────────────────────────────────────────────
   Vive DENTRO del aviso «no sé dónde estás», que es donde hace falta. */
.nv-mk-mun { margin-top: 10px; }
.nv-mk-muntit { font-size: 12px; font-weight: 700; margin-bottom: 6px; }
.nv-mk-munq { width: 100%; }
.nv-mk-munout { margin-top: 6px; }
.nv-mk-munmot { font-size: 11.5px; color: var(--nv-tenue); padding: 4px 0; }
.nv-mk-munmot.is-estado { color: var(--nv-dorado); }
.nv-mk-munmot.is-ok { color: var(--nv-savia-hi); font-weight: 700; }
.nv-mk-munlista { display: flex; flex-direction: column; gap: 4px; margin-top: 4px;
  max-height: 240px; overflow-y: auto; }
/* Cada opción es un botón de ancho completo: en un teléfono se toca con el
   pulgar, y 44 px de alto es el mínimo que se acierta sin ampliar. */
.nv-mk-munop { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; width: 100%;
  min-height: 44px; padding: 9px 11px; text-align: left; cursor: pointer;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-mk-munop:hover, .nv-mk-munop:focus-visible { border-color: var(--nv-savia); }
.nv-mk-munnom { font-size: 13px; font-weight: 700; }
/* EL DEPARTAMENTO NUNCA SE OCULTA. Hay tres «Argelia» en tres departamentos:
   sin esta línea, una de cada tres personas elige el pueblo equivocado y la app
   le sigue diciendo «cerca de ti» sobre sitios a un día de camino. */
.nv-mk-mundep { font-size: 11.5px; color: var(--nv-tenue); }
.nv-mk-munrep { font-size: 10.5px; color: var(--nv-dorado); }

@media (max-width: 720px) {
  /* El precio y el nombre dejan de pelearse por el ancho: en 320 px un título
     de 140 caracteres empujaba el precio fuera de la tarjeta. */
  .nv-mk-item .nv-mi-ctop { flex-wrap: wrap; }
  .nv-mk-munlista { max-height: 200px; }
}

/* ============================================================================
   HITO 33 — PUBLICAR. La pantalla que faltaba (`modules/publish.js`).

   Hereda el armazón de la tarjeta de revisión (`.nv-lc-scrim/.nv-lc-modal/
   .nv-lc-head/.nv-lc-body/.nv-lc-pie`) a propósito: publicar y revisar son dos
   pasos del MISMO acto, y darles dos maquetas distintas haría que el segundo
   pareciera otra pantalla.

   LO ÚNICO QUE ES DELIBERADAMENTE GRANDE son las dos opciones de «compro /
   vendo» y el micrófono. Es lo que se toca con el pulgar, de pie, en una finca,
   con sol. Un radio button de 14 px ahí dentro sería inservible.
   ========================================================================== */
.nv-pb-preg { font-size: 15px; font-weight: 700; line-height: 1.35; margin: 2px 0 4px; }
.nv-pb-nota { font-size: 12.5px; line-height: 1.4; color: var(--nv-texto-2, #9fb8ad); margin: 0; }
.nv-pb-nota:empty { display: none; }

/* Las dos puertas. Sin ninguna marcada: el estado inicial NO puede parecer una
   respuesta, porque entonces se pulsa «siguiente» sin leer y volvemos al
   problema que este hito vino a cerrar. */
.nv-pb-opt { display: flex; align-items: center; gap: 13px; width: 100%; min-height: 76px;
  padding: 15px 16px; text-align: left; cursor: pointer;
  background: var(--nv-superficie); color: var(--nv-texto);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md, 14px); }
.nv-pb-opt:hover, .nv-pb-opt:focus-visible { border-color: var(--nv-savia); background: rgba(47,169,140,.07); }
.nv-pb-opt-ic { display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: 0 0 44px; border-radius: 50%;
  background: rgba(47,169,140,.12); color: var(--nv-savia); }
.nv-pb-opt-txt { display: flex; flex-direction: column; gap: 3px; }
.nv-pb-opt-txt b { font-size: 15px; }
.nv-pb-opt-d { font-size: 12.5px; line-height: 1.35; color: var(--nv-texto-2, #9fb8ad); }

.nv-pb-volver { align-self: flex-start; width: auto; padding: 4px 0; font-size: 12.5px;
  background: none; border: none; color: var(--nv-texto-2, #9fb8ad); cursor: pointer; }
.nv-pb-volver:hover { color: var(--nv-savia); }

/* La conversación. `min-height` para que la caja no salte de tamaño con el
   primer mensaje: un salto de maqueta se lee como que algo se rompió. */
.nv-pb-conv { display: flex; flex-direction: column; gap: 8px; min-height: 60px;
  max-height: 46vh; overflow-y: auto; padding: 2px; }
.nv-pb-msg { max-width: 88%; padding: 9px 12px; font-size: 13.5px; line-height: 1.4;
  border-radius: var(--nv-r-md, 14px); }
.nv-pb-msg.yo { align-self: flex-end; background: var(--nv-savia); color: #04140E; }
.nv-pb-msg.ai { align-self: flex-start; background: var(--nv-superficie);
  border: 1px solid var(--nv-linea); }
.nv-pb-pensando { opacity: .72; font-style: italic; }
.nv-pb-err { align-self: stretch; }

.nv-pb-estado { font-size: 12.5px; color: var(--nv-savia); min-height: 1em; }
.nv-pb-estado:empty { min-height: 0; }

.nv-pb-entrada { display: flex; align-items: center; gap: 8px; }
.nv-pb-in { flex: 1; }
.nv-pb-env { width: auto; padding: 10px 14px; }
/* 52 px: el micrófono es lo que más se toca de esta pantalla. */
.nv-pb-mic { display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; flex: 0 0 52px; border-radius: 50%; cursor: pointer;
  background: var(--nv-superficie); color: var(--nv-savia);
  border: 1px solid var(--nv-linea-fuerte, var(--nv-linea)); }
.nv-pb-mic:hover { border-color: var(--nv-savia); }
.nv-pb-mic.on { background: var(--nv-savia); color: #04140E; border-color: var(--nv-savia); }
/* Deshabilitado se VE deshabilitado. Un botón apagado que parece encendido hace
   que la persona lo golpee tres veces y crea que la app se colgó. */
.nv-pb-mic.nv-pb-off, .nv-pb-mic:disabled { opacity: .45; cursor: not-allowed; }

/* La salida cuando la IA no responde. Discreta a propósito: es la escalera de
   incendios, no la puerta principal. Pero SIEMPRE visible, porque el día que la
   necesites no puede estar escondida en un menú. */
.nv-pb-manual { align-self: center; width: auto; padding: 6px 10px; font-size: 12.5px;
  background: none; border: none; text-decoration: underline;
  color: var(--nv-texto-2, #9fb8ad); cursor: pointer; }
.nv-pb-manual:hover { color: var(--nv-savia); }
.nv-pb-manual:disabled { opacity: .5; cursor: progress; }

@media (max-width: 720px) {
  .nv-pb-conv { max-height: 38vh; }
  /* En 320 px el botón «Enviar» empujaba la casilla de texto a 60 px de ancho.
     Con el micrófono ahí al lado, escribir era imposible. */
  .nv-pb-entrada { flex-wrap: wrap; }
  .nv-pb-in { min-width: 55%; }
}

/* ══════════════════════════════════════════════════════════════════════════
   TARJETAS CON FOTO Y BUSCADOR — Industria y Ganado (encargo del dueño)
   ══════════════════════════════════════════════════════════════════════════

   «En el mercado Industria y ganado, es importante que se vean como si fuera
   el marketplace: como tarjetas con imagenes, y que tengan buscador.»

   LO QUE SE MIDIO ANTES DE ESCRIBIR ESTE BLOQUE, y explica el encargo entero:

     grep "nv-in-|nv-sb-" app.css  ->  CERO coincidencias.

   Las dos pantallas no estaban «con otro estilo»: estaban SIN ESTILO. Eran
   divs crudos apilados con el aspecto por defecto del navegador. El
   marketplace parecia un marketplace y estas dos parecian un volcado de texto
   porque, literalmente, nadie les habia escrito una sola regla.

   MEDIDO A 360 px, que es el ancho del telefono del dueño. Todo lo de aqui es
   mobile-first: la rejilla arranca en UNA columna y crece con el sitio, en vez
   de arrancar en tres y encogerse. La version «arranca ancha» es la que deja
   los importes desbordados en un telefono — un fallo que ya se pago hoy en
   otra pantalla.
   ────────────────────────────────────────────────────────────────────────── */

/* ---- el recuadro de la foto (compartido: industria, ganado) ---- */
.nv-foto { position: relative; overflow: hidden; height: 132px;
  background: linear-gradient(135deg, #1a4a38, #0d3327); }
.nv-foto-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
/* Cuantas fotos mas hay. Sin esto, una publicacion con seis fotos se ve igual
   que una con una, y el comprador no sabe que hay mas que mirar. */
.nv-foto-n { position: absolute; right: 8px; bottom: 8px;
  font-family: var(--nv-f-mono); font-size: 11px; color: var(--nv-texto);
  background: rgba(4,20,14,.66); border: 1px solid var(--nv-linea);
  padding: 2px 8px; border-radius: var(--nv-r-pill); }
/* Cuando ademas hay credito de foto de banco, el contador sube para no
   pisarse con la faja del credito (abajo del todo, ancho completo). */
.nv-foto-n-alto { bottom: 22px; }

/* Credito de una foto de banco (Pexels), 2026-08-24 — SIEMPRE visible, nunca
   solo al pasar el mouse: `.nv-foto` tiene alto fijo y `overflow:hidden`
   (arriba), asi que va de faja sobre el borde inferior de la foto, igual de
   permanente que `.nv-foto-n`. Es la regla de `routers/fotos_stock.js`
   ("el credito se ve"): sin esto la foto de ejemplo se lee como el producto
   real de quien publico. */
.nv-foto-credito { position: absolute; left: 0; right: 0; bottom: 0;
  padding: 3px 8px; font-size: 9.5px; line-height: 1.3; color: #fff;
  background: rgba(4,20,14,.66); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* EL MARCADOR HONESTO. La mitad de la plaza (20 de 40 publicaciones vivas) no
   tiene foto, y la de subasta comparte una de stock entre cuatro lotes. Aqui
   NO va una imagen de archivo: va el hueco, dicho. Se pinta apagado a
   proposito — tiene que leerse como «falta un dato», no como decoracion. */
.nv-foto-sin { position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 6px;
  background: var(--nv-suelo); color: var(--nv-fantasma); }
.nv-foto-sin-ic { opacity: .55; line-height: 0; }
.nv-foto-sin-t { font-family: var(--nv-f-mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; }

/* ---- el buscador ---- */
.nv-tj-search { width: 100%; min-width: 0; margin: 10px 0 2px; }
.nv-tj-search input[type="search"]::-webkit-search-cancel-button { filter: invert(.6); cursor: pointer; }

/* ══ INDUSTRIA ═══════════════════════════════════════════════════════════ */
.nv-in-bar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.nv-in-bar .nv-seg { flex: 1 1 100%; }
.nv-in-linea { font-family: var(--nv-f-mono); font-size: 11.5px; color: var(--nv-tenue); margin: 12px 0 6px; }
.nv-in-aviso { font-size: 12.5px; color: var(--nv-tenue); background: var(--nv-suelo);
  border: 1px solid var(--nv-linea); border-left: 3px solid var(--nv-dorado, #E6C200);
  border-radius: var(--nv-r-sm); padding: 10px 12px; margin: 0 0 12px; }
.nv-in-nota { font-size: 11.5px; color: var(--nv-fantasma); margin-top: 16px; line-height: 1.5; }

/* UNA COLUMNA A 360 px. minmax(0, 1fr) y no minmax(210px, 1fr): con un minimo
   de 210 px, una tarjeta con un titulo largo sin espacios desborda la pantalla
   en horizontal — el mismo fallo que el dueño vio hoy con los importes. El
   auto-fill sigue dando dos y tres columnas cuando de verdad hay sitio. */
.nv-in-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
@media (min-width: 560px) { .nv-in-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); } }

.nv-in-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); overflow: hidden; box-shadow: var(--nv-sh-1);
  transition: var(--nv-dur-fast); display: flex; flex-direction: column; min-width: 0; }
.nv-in-card:hover { border-color: var(--nv-linea-fuerte); transform: translateY(-2px); }
.nv-in-cuerpo2 { padding: 12px 13px 13px; display: flex; flex-direction: column; gap: 7px; min-width: 0; }
/* overflow-wrap y min-width:0 en TODO lo que lleva texto del usuario. Un titulo
   como «Comercializadora-exporta-aguacate-Hass-12-toneladas» no tiene un solo
   espacio donde partir, y sin esto empuja la rejilla entera. */
.nv-in-title { font-size: 14.5px; font-weight: 700; line-height: 1.3; overflow-wrap: anywhere; }
.nv-in-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 2px;
  font-size: 11.5px; color: var(--nv-tenue); overflow-wrap: anywhere; }
.nv-in-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.nv-in-chip { font-size: 11px; padding: 4px 9px; border-radius: var(--nv-r-pill);
  border: 1px solid var(--nv-linea-fuerte); color: var(--nv-tenue); white-space: nowrap; }
.nv-in-chip.on { background: color-mix(in srgb, var(--nv-savia) 14%, transparent);
  color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-in-cifras { font-size: 13px; color: var(--nv-cosecha); overflow-wrap: anywhere; }
/* «No dijo: recurrencia, plazo de pago». Es la mitad del hito 30 y tiene que
   verse como una advertencia, no como una linea mas de la ficha. */
.nv-in-falta { font-size: 11.5px; color: var(--nv-tenue); overflow-wrap: anywhere; }
.nv-in-falta-t { color: var(--nv-dorado, #E6C200); font-weight: 700; }
.nv-in-desc { font-size: 12px; color: var(--nv-tenue); line-height: 1.45; margin: 0; overflow-wrap: anywhere; }
.nv-in-mio, .nv-in-coin { font-size: 11.5px; color: var(--nv-tenue); overflow-wrap: anywhere; }
.nv-in-mio-t { color: var(--nv-savia); font-weight: 700; }
.nv-in-acc { margin-top: auto; padding-top: 4px; }
.nv-in-acc .nv-btn { padding: 10px 14px; font-size: 13px; }
.nv-in-vacio { text-align: center; color: var(--nv-tenue); padding: 34px 18px;
  display: flex; flex-direction: column; align-items: center; gap: 12px; }
.nv-in-vacio .nv-btn { width: auto; }
.nv-in-ic { line-height: 0; display: inline-flex; vertical-align: -2px; }

/* el formulario de «yo compro» */
.nv-in-form { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); padding: 14px; margin-bottom: 14px; }
.nv-in-grupo { margin: 12px 0; }
.nv-in-lab { display: block; font-size: 12px; color: var(--nv-tenue); margin-bottom: 7px; }
.nv-in-ops { display: flex; flex-wrap: wrap; gap: 7px; }
.nv-in-op { font-size: 12px; padding: 8px 12px; border-radius: var(--nv-r-pill);
  border: 1px solid var(--nv-linea-fuerte); background: none; color: var(--nv-texto);
  cursor: pointer; width: auto; transition: var(--nv-dur-fast); }
.nv-in-op.on { background: color-mix(in srgb, var(--nv-savia) 16%, transparent);
  color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-in-estado { font-size: 12px; color: var(--nv-savia); min-height: 16px; margin: 8px 0; }

/* ══ GANADO / SUBASTA ════════════════════════════════════════════════════ */
.nv-sb-body { margin-top: 12px; }
.nv-sb-orden { font-size: 12px; color: var(--nv-tenue); background: var(--nv-suelo);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm);
  padding: 10px 12px; margin-bottom: 12px; }
.nv-sb-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
@media (min-width: 620px) { .nv-sb-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); } }
/* El formulario de "Guardar la fecha" del permiso — reportado 2026-08-22:
   "le falta aire entre el text field y el botón". `.nv-sb-form` no tenía NI
   UNA regla: select, dos labels, dos campos y el botón, todos pegados uno
   contra otro sin gap. Y `.nv-lb` (la etiqueta genérica de campo, la usan 6
   módulos) tampoco tenía ninguna — por eso "Número del documento..." salía
   pegada a su campo también. */
.nv-sb-form { display: flex; flex-direction: column; gap: 10px; background: var(--nv-superficie);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 15px; }
.nv-lb { display: block; font-size: 12px; color: var(--nv-tenue); }

.nv-sb-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); overflow: hidden; box-shadow: var(--nv-sh-1);
  display: flex; flex-direction: column; min-width: 0; transition: var(--nv-dur-fast); }
.nv-sb-card:hover { border-color: var(--nv-linea-fuerte); }
.nv-sb-cuerpo { padding: 12px 13px 13px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.nv-sb-top { display: flex; gap: 8px; align-items: flex-start; justify-content: space-between; min-width: 0; }
.nv-sb-title { font-size: 14.5px; font-weight: 700; line-height: 1.3; overflow-wrap: anywhere; min-width: 0; }
.nv-sb-est { font-family: var(--nv-f-mono); font-size: 10px; text-transform: uppercase;
  letter-spacing: .08em; padding: 3px 8px; border-radius: var(--nv-r-pill);
  border: 1px solid var(--nv-linea-fuerte); color: var(--nv-tenue); white-space: nowrap; flex-shrink: 0; }
.nv-sb-est-abierta { color: var(--nv-savia); border-color: var(--nv-savia); }
.nv-sb-est-adjudicada { color: var(--nv-cosecha); border-color: var(--nv-cosecha); }
.nv-sb-est-desierta, .nv-sb-est-cancelada { color: var(--nv-fantasma); }
.nv-sb-meta { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 11.5px; color: var(--nv-tenue); }
.nv-sb-k { font-family: var(--nv-f-mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--nv-fantasma); margin: 16px 0 8px; }
.nv-sb-k:first-child { margin-top: 0; }
.nv-sb-v { font-size: 13px; overflow-wrap: anywhere; }

/* PERMISOS — Ganado y subasta (rediseño 2026-08-21, reportado "se ve feo").
   `.nv-sb-permisos`, `.nv-sb-lista` y sus `<li>` no tenían NI UNA regla en
   toda la hoja — salía la viñeta y el margen por defecto del navegador, que
   es justo el aspecto "tirado" que se reportó. Cada requisito pasa a ser su
   propia tarjeta con franja de color por severidad, igual que las alertas
   agronómicas (`.nv-ag-card`): roja = vencido, dorada = por vencer, verde =
   al día, neutra = falta. */
.nv-sb-permisos { display: flex; flex-direction: column; }
.nv-sb-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.nv-sb-lista li { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-left: 3px solid var(--nv-linea-fuerte); border-radius: var(--nv-r-sm);
  padding: 11px 13px; font-size: 13px; font-weight: 600; line-height: 1.35; }
.nv-sb-lista.nv-mal li { border-left-color: var(--nv-critico); }
.nv-sb-lista.nv-ojo li { border-left-color: var(--nv-dorado); }
.nv-sb-lista li .nv-sb-nota { font-weight: 400; margin-top: 4px; }

/* EL NUMERO GRANDE ES EL COSTO POR KILO RECIBIDO, no el precio del aviso. Esa
   es toda la diferencia entre esta pantalla y una lista de anuncios, asi que
   es lo unico que se pinta grande. clamp() y no un tamaño fijo: «$12.400 –
   $13.900» a 26 px se sale de un telefono de 360 px. */
.nv-sb-big { background: var(--nv-suelo); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-sm); padding: 10px 12px; }
.nv-sb-big-n { font-family: var(--nv-f-mono); font-variant-numeric: tabular-nums;
  font-size: clamp(15px, 4.6vw, 21px); color: var(--nv-cosecha); overflow-wrap: anywhere; }
.nv-sb-big-u { font-size: 11px; color: var(--nv-tenue); margin-top: 2px; }
.nv-sb-det, .nv-sb-pie { display: flex; flex-wrap: wrap; gap: 4px 12px;
  font-size: 11.5px; color: var(--nv-tenue); }
.nv-sb-merma, .nv-sb-flete { border-top: 1px dashed var(--nv-linea); padding-top: 8px; }
.nv-sb-aviso { font-size: 11.5px; color: var(--nv-dorado, #E6C200); line-height: 1.45; margin: 4px 0 0; }
.nv-sb-nota { font-size: 11.5px; color: var(--nv-tenue); line-height: 1.45; margin: 4px 0 0; overflow-wrap: anywhere; }
.nv-sb-puja { border-top: 1px solid var(--nv-linea); padding-top: 10px; display: flex;
  flex-direction: column; gap: 7px; }
.nv-sb-puja input { width: 100%; background: var(--nv-suelo);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm);
  padding: 10px 12px; color: var(--nv-texto); font-size: 14px; }

/* ══════════════════════════════════════════════════════════════════════════
   LA BANDA DE TRANSPORTE DE LA PORTADA
   ══════════════════════════════════════════════════════════════════════════
   «Me gustaria que en los botones, cuando se abre la app, se vea
    inmediatamente lo del transporte, puesto que la logistica es demasiado
    coyuntural para el negocio y el sector agropecuario, y es bueno que tanto
    el que ofrece el servicio como el que lo requiere sea facil de acceder.»

   MEDIDO A 360 px. Los dos botones van UNO DEBAJO DE OTRO en el telefono y
   lado a lado en cuanto hay sitio. En una fila, a 360 px, «Necesito
   transporte» y «Ofrezco transporte» quedan a 150 px cada uno y el texto se
   parte en tres lineas: dos botones ilegibles no son mas accesibles que uno.
   ────────────────────────────────────────────────────────────────────────── */
.nv-tp-banda { margin-bottom: 18px; }
.nv-tp-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); padding: 14px; box-shadow: var(--nv-sh-1);
  /* El borde de color es lo que hace que se vea PRIMERO sin gritar. La banda
     compite con la fila de indicadores, que es lo que el productor ya viene
     buscando; sin una marca, se lee como un bloque mas y se salta.
     DORADO desde el 2026-08-09, por encargo del dueño («aqui todo es
     protagonista» — y cuando todo pesa igual, nada destaca). Va de LINEA, no de
     relleno: el fondo dorado convertiria la banda en un aviso publicitario y se
     aprende a ignorar. En esta banda el dorado solo lo llevan DOS cosas —esta
     linea y el contador— porque un tercer sitio y deja de mandar. */
  border-left: 3px solid var(--nv-dorado); }
.nv-tp-cab { display: flex; gap: 11px; align-items: flex-start; min-width: 0; }
.nv-tp-ic { line-height: 0; color: var(--nv-savia); flex-shrink: 0; margin-top: 2px; }
.nv-tp-cab b { font-family: var(--nv-f-display); font-size: 15px; }
.nv-tp-d { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; margin: 3px 0 0; }
.nv-tp-acc { display: flex; flex-direction: column; gap: 9px; margin-top: 13px; }
/* `.nv-tp-ofr` tambien reparte: «Ofrezco» dejo de ser hijo directo de `.nv-tp-acc`
   cuando se le pego el «?», y sin esta linea el par se quedaria a su ancho
   natural mientras «Necesito» se comia la fila. */
@media (min-width: 480px) { .nv-tp-acc { flex-direction: row; } .nv-tp-b, .nv-tp-ofr { flex: 1; } }
.nv-tp-b { padding: 12px 14px; font-size: 13.5px; }
/* «Ofrezco transporte» + su «?». Van en la misma fila a cualquier ancho: el «?»
   habla de ESE boton y separarlos lo dejaria huerfano. */
.nv-tp-ofr { display: flex; gap: 8px; align-items: stretch; }
.nv-tp-ofr .nv-tp-b { flex: 1; min-width: 0; }
/* 44 px de LADO, que es el minimo tactil de verdad. Un «?» de 14 px con el area
   de un alfiler es un boton que solo aciertan los ratones. */
.nv-tp-ayuda { flex: 0 0 44px; width: 44px; min-height: 44px; padding: 0;
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm, 8px);
  background: transparent; color: var(--nv-tenue); cursor: pointer;
  font-family: var(--nv-f-display); font-size: 16px; line-height: 1; }
.nv-tp-ayuda:hover { border-color: var(--nv-linea-fuerte); color: var(--nv-texto); }
.nv-tp-ayuda:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: 2px; }
/* El renglon de encomiendas. UN PUNTO menos que el titulo (15px -> 13px) y sin
   caja, sin fondo y sin borde: lleva marco y deja de leerse como «y ademas,
   esto» para leerse como «otra cosa distinta», que es justo lo contrario. */
.nv-tp-enc { font-size: 13px; color: var(--nv-texto); line-height: 1.45; margin: 13px 0 0; }
/* La escalera de ocho. Una fila, sin envolver. A 360 px la tarjeta deja ~304 px
   utiles: 8 x 24 px de icono = 192 px y sobran ~16 px por hueco. `space-between`
   y no `gap` fijo para que los extremos queden alineados con el texto. */
.nv-tp-esc { display: flex; justify-content: space-between; align-items: flex-end;
  gap: 4px; list-style: none; margin: 10px 0 0; padding: 0; }
.nv-tp-modo { flex: 0 0 auto; line-height: 0; color: var(--nv-tenue); }
.nv-tp-modo-ic { display: block; line-height: 0; }
/* El censo medido. DORADO: es el unico numero vivo de la portada y esta casa ya
   reserva ese color para «dinero y destacados» (tokens.css). El cero no cambia
   de color —seria un tercer dorado compitiendo— sino de peso: sigue leyendose
   como advertencia, porque quien pulse «necesito transporte» no va a encontrar
   a nadie. */
.nv-tp-censo { font-family: var(--nv-f-mono); font-size: 11px; color: var(--nv-dorado);
  margin: 7px 0 0; line-height: 1.45; }
.nv-tp-censo.nv-tp-cero { font-weight: 600; }
/* Vacio no ocupa nada: este parrafo solo existe como hueco de error. Sin esto,
   su `margin-top: 11px` abriria un agujero permanente bajo la escalera. */
.nv-tp-nota { font-size: 11.5px; color: var(--nv-fantasma); line-height: 1.5; margin: 11px 0 0; }
.nv-tp-nota:empty { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   INTERESES Y SU BANNER
   ══════════════════════════════════════════════════════════════════════════
   «Es importante que en algun lugar haya o existan intereses, para que cuando
    alguien monte algo de interes de el le salga en un banner.»

   MEDIDO A 360 px. El hueco del aviso NO LLEVA margen ni alto propios: cuando
   esta vacio tiene que ocupar CERO, o el transporte —el otro encargo del
   dueño— se va fuera de la primera pantalla en el 95% de las aperturas, que
   son las que no traen aviso. El margen vive en `.nv-in-banner`, que solo
   existe cuando hay algo que decir.
   ────────────────────────────────────────────────────────────────────────── */
.nv-in-slot:empty { display: none; }

.nv-in-banner { display: flex; gap: 10px; align-items: flex-start;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-left: 3px solid var(--nv-dorado, #E6C200);
  border-radius: var(--nv-r-md); padding: 12px; margin-bottom: 14px;
  box-shadow: var(--nv-sh-1); }
/* `min-width: 0` es lo que deja que el titulo se PARTA en vez de estirar el
   flex y sacar la X fuera de la pantalla. Un titulo de publicacion es texto de
   usuario: puede ser una sola palabra de 40 letras. */
.nv-in-bcuerpo { display: flex; flex-direction: column; gap: 2px; flex: 1;
  min-width: 0; overflow-wrap: anywhere; }
.nv-in-motivo { font-size: 11px; color: var(--nv-dorado, #E6C200);
  font-family: var(--nv-f-mono); }
.nv-in-btit { font-size: 14px; line-height: 1.3; }
.nv-in-bmeta { font-size: 11.5px; color: var(--nv-tenue); }
.nv-in-bacc { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.nv-in-bver { padding: 8px 11px; font-size: 12px; white-space: nowrap; }
/* 34 px de lado: por debajo de ~32 px un objetivo tactil se falla con el dedo,
   y este boton es el que APAGA el aviso. Si cerrar cuesta tres intentos, el
   aviso se vuelve exactamente el estorbo que no queremos. */
.nv-in-bx { width: 34px; height: 34px; flex-shrink: 0; border-radius: var(--nv-r-sm);
  background: transparent; border: 1px solid var(--nv-linea);
  color: var(--nv-tenue); font-size: 13px; cursor: pointer; line-height: 1; }
.nv-in-bx:hover { color: var(--nv-texto); border-color: var(--nv-tenue); }
/* A 360 px «Ver la publicacion» + la X + el titulo no caben en una fila. Las
   acciones bajan y quedan a lo ancho, que ademas las hace mas faciles de dar. */
@media (max-width: 420px) {
  .nv-in-banner { flex-direction: column; }
  .nv-in-bacc { width: 100%; }
  .nv-in-bver { flex: 1; }
}

/* ─── El editor ───────────────────────────────────────────────────────────
   ⚠️ `nv-int-*` DESDE EL 2026-08-08, Y NO SE VUELVE ATRAS. Cinco de estos
   nombres —card, chips, chip, nota, vacio— estaban definidos DOS VECES en
   este mismo fichero: una vez arriba, en el bloque de Industria, y otra aqui.
   No es un duplicado inocente: gana el que va mas abajo, asi que cada pantalla
   se llevaba trozos del estilo de la otra. Se veia en dos sitios —el hueco de
   «no has guardado ninguno» heredaba el `padding: 34px 18px` de la rejilla de
   Industria, y esta tarjeta saltaba con el `:hover` de las tarjetas de
   producto— y no se veia en un tercero: las tarjetas de Industria las estaba
   pintando ESTE bloque por ser el ultimo.
   Antes de reutilizar un nombre `nv-*`, mirar si ya tiene dueño. Aqui costo
   una pantalla. */
.nv-in-ed { margin-top: 22px; }
.nv-int-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); padding: 14px; }
.nv-int-card b { font-family: var(--nv-f-display); font-size: 15px; }
.nv-in-d { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.45; margin: 4px 0 0; }
.nv-int-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 11px; }
.nv-int-vacio { font-size: 12px; color: var(--nv-fantasma); }
/* El interes se pinta con «✕» PEGADO detras via ::after y no como texto, para
   que lo que se copia y lo que leen los buscadores siga siendo la palabra
   limpia. El rotulo accesible («Quitar aguacate») lo pone `aria-label`: un
   lector de pantalla diciendo solo «aguacate» no avisa en ningun momento de
   que pulsarlo lo BORRA. */
.nv-int-chip { display: inline-flex; align-items: center; gap: 6px;
  background: var(--nv-suelo); border: 1px solid var(--nv-linea);
  border-radius: 999px; padding: 7px 12px; font-size: 12.5px;
  color: var(--nv-texto); cursor: pointer; max-width: 100%;
  overflow-wrap: anywhere; text-align: left; }
.nv-int-chip::after { content: '✕'; font-size: 10px; color: var(--nv-tenue); flex-shrink: 0; }
.nv-int-chip:hover { border-color: var(--nv-rojo, #E05555); }
.nv-int-chip:hover::after { color: var(--nv-rojo, #E05555); }

/* EL BOTON VA DEBAJO DEL CAMPO, NO AL LADO.
   «Deberia estar el boton de guardar en la parte de abajo o debajo del text
   field, para que ese textfield tenga espacio y aire de escribir y que se vea
   que se esta escribiendo, como en el WhatsApp» (dueño, 2026-08-08).

   MEDIDO a 360 px antes de tocar nada: el campo salia de 25 px de ancho y el
   boton de 261 px, los dos en la misma fila. No se podia ver lo que uno
   escribia — cabia una letra.

   Y NO ERA QUE FALTARA `flex: 1`: el campo YA lo tenia, con `min-width: 0` y
   todo. Lo que manda es que `.nv-btn` lleva `width: 100%` en su regla base
   (linea 22), asi que dentro de un flex el boton pide el ancho ENTERO del
   contenedor y el campo, que si sabe encogerse, se queda con las sobras. Poner
   `flex: 1` mas fuerte en el campo no arregla nada mientras el boton siga
   pidiendo el 100%.

   Apilarlos resuelve las dos cosas de una: el campo ocupa el ancho completo y
   el boton cae debajo, que es exactamente lo que se pidio. Se apila SIEMPRE y
   no solo en movil — un campo de escribir a media fila tampoco es comodo en
   una pantalla grande, y una regla sin punto de corte es una regla menos que
   se puede romper al redimensionar. */
.nv-in-fila { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
/* `min-width: 0` otra vez: sin el, un input dentro de un flex NO baja de su
   ancho por defecto (~180 px) y a 360 px empuja el boton fuera de la tarjeta. */
.nv-in-inp { width: 100%; min-width: 0; background: var(--nv-suelo);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm);
  padding: 13px 14px; color: var(--nv-texto);
  /* 16 px EXACTOS y no 14: por debajo de 16, Safari de iOS hace ZOOM al tocar
     el campo y deja la portada descuadrada y al usuario sin saber volver. */
  font-size: 16px; }
.nv-in-inp:disabled { opacity: .5; }
.nv-in-add { padding: 13px 15px; font-size: 14px; white-space: nowrap; }
.nv-int-nota { font-size: 11.5px; color: var(--nv-dorado, #E6C200); margin: 9px 0 0; line-height: 1.45; }

/* ═══ LA FICHA DEL POSIBLE CLIENTE — «a quién le vendo», tarjeta de afuera ════
 *
 * Estas reglas NO son cosmética. Hasta ahora la ficha se apoyaba en que los
 * `div` saltan de línea solos, y con eso bastaba mientras eran tres campos. Ya
 * son diez, y dos `span` hermanos SÍ se pegan: sin `.nv-mk-rot` se leía
 * «Su fuerteexporta a Europacompruébalo».
 *
 * OJO CON EL NOMBRE `.nv-mk-fila`: la ficha usaba `.nv-mk-dato`, que YA EXISTE
 * arriba (línea ~1352) para las filas de dos columnas de `market_precios.js`
 * —`flex-direction: row; justify-content: space-between`— y le repartía el
 * rótulo, el valor y el enlace a lo ancho de la tarjeta. Era una colisión de
 * nombres, no un estilo: se renombró la de la ficha en vez de ganarle por
 * especificidad, porque una guerra de selectores entre dos módulos la pierde
 * siempre el que se toque después. `market_precios.js` no se tocó.
 */
.nv-mk-fila { padding: 6px 0; line-height: 1.5; font-size: 13.5px; }
.nv-mk-rot { color: var(--nv-tenue); font-weight: 700; font-size: 12.5px; }
.nv-mk-val { color: var(--nv-texto); }
/* El « · » va en el TEXTO (lo escribe la pantalla) y aquí solo se le da aire.
   No se esconde con `display:none`: si esta hoja no carga —o carga a medias—
   ese carácter es lo único que separa el valor del enlace, y una pantalla que
   se lee mal es mejor que una que se lee al revés. */
.nv-mk-sep { color: var(--nv-fantasma); padding: 0 2px; }
.nv-mk-fuente { color: var(--nv-savia); text-decoration: none; font-size: 12px;
  border-bottom: 1px dotted color-mix(in srgb, var(--nv-savia) 50%, transparent); }
.nv-mk-fuente:hover { border-bottom-style: solid; }
.nv-mk-fuentes { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px;
  padding-top: 9px; border-top: 1px solid var(--nv-linea); }

/* CUÁNTO MUEVEN AL AÑO — se le da peso porque es lo primero que se busca al
   abrir la ficha, y porque el backend ya garantizó que si está pintado, tiene
   una URL que se puede abrir. Destacar un dato sin respaldo sería subrayar una
   mentira; destacar uno respaldado es solo ahorrarle la búsqueda al ojo. */
.nv-mk-anual .nv-mk-val { font-weight: 700; font-size: 15px; }

/* LAS DOS LÍNEAS QUE IMPIDEN QUE ESTA PANTALLA MIENTA.
 *
 * `.nv-mk-supuesto` («lo supongo yo») y `.nv-mk-estimado` («es un cálculo, no
 * un dato»). Van en dorado, más pequeñas y con una barra al lado IZQUIERDO
 * para que NO se lean como una línea más de la ficha: el texto que acompañan se
 * ve idéntico venga de un informe o de una deducción, y lo único que separa las
 * dos cosas es que esto se note. Si algún día alguien las baja a gris tenue
 * «por ruido visual», habrá devuelto la ficha al sitio del que vino. */
.nv-mk-supuesto { color: var(--nv-dorado, #E6C200); font-size: 11.5px;
  line-height: 1.45; margin-top: 5px; padding-left: 9px;
  border-left: 2px solid color-mix(in srgb, var(--nv-dorado, #E6C200) 55%, transparent); }

/* El resto de la ficha: los párrafos sueltos necesitaban aire, no color. */
.nv-mk-que { color: var(--nv-tenue); font-size: 13px; margin: 4px 0 8px; line-height: 1.5; }
.nv-mk-porque { font-size: 13px; margin: 6px 0; line-height: 1.5; }
.nv-mk-como { padding: 9px 0 4px; font-size: 13.5px; line-height: 1.55; }
.nv-mk-conf { font-size: 11px; font-weight: 700; padding: 3px 8px;
  border-radius: 999px; white-space: nowrap; }
.nv-mk-prio { font-size: 11px; padding: 3px 8px; border-radius: 999px;
  white-space: nowrap; color: var(--nv-tenue); border: 1px solid var(--nv-linea); }

/* ══════════════════════════════════════════════════════════════════════════
   CAMPOS DE ENTRADA — EL BLANCO QUE DESLUMBRA
   ══════════════════════════════════════════════════════════════════════════

   «Ese blanco se ve muy impactante.» «Y eso asi blanco se ve feo.»
   (dueño, 2026-08-08, sobre «Flete y merma» y «Permisos» de Ganado.)

   LO QUE SE MIDIO EN PRODUCCION ANTES DE ESCRIBIR ESTO, a 360 px, que es el
   ancho de su telefono, y con `tokens.css` cargado de verdad — sin los
   estilos la fuente cae a serif y TODAS las medidas mienten:

     .nv-panel input.nv-in  ->  background-color: rgb(255, 255, 255)
                                color: rgb(0, 0, 0)
                                border-color: rgb(118, 118, 118)
                                height: 21px

   Cinco seguidos en «Flete y merma». No estaban mal pintados: NO TENIAN
   REGLA. `grep "\.nv-in([^-]|$)" app.css` -> cero. Sale el aspecto por
   defecto del navegador, que en una pagina de tema oscuro es un foco.

   Y NO ERA SOLO ESA CLASE. Enumerando las clases que los modulos ponen en
   `input`/`textarea`/`select` y cruzandolas con la hoja salieron ONCE sin
   una sola regla: .nv-in .nv-inp .nv-emb-altainp .nv-lv-met .nv-bf-inp
   .nv-bf-ta .nv-as-horas .nv-ag-inf-grado .nv-ag-inf-kgenv .nv-ag-inf-marca
   .nv-ag-inf-precio .nv-lv-met. Por eso esto se escribe contra el ELEMENTO y
   no contra una lista de clases: arreglar `.nv-in` sola habria dejado diez
   focos encendidos y el proximo campo que alguien añada nace blanco otra vez.

   POR QUE NO ROMPE LO QUE YA ESTABA ESTILADO: un selector de elemento pesa
   (0,0,1) y cualquier regla de clase (0,1,0) le gana. `.nv-ib-input`,
   `.nv-un-input`, `.nv-composer textarea` y compañia siguen mandando en su
   propio fondo. Comprobado midiendo el valor CALCULADO despues del cambio,
   no leyendo el fichero: que la regla exista no es que se aplique.

   LAS EXCLUSIONES NO SON OPCIONALES. Un `background`/`padding`/`border`
   sobre `checkbox`, `radio`, `file` o `range` los desfigura o los borra.
   Hay casillas reales en `transport.js` y en `agro_inputs.js`, y selectores
   de archivo en seis modulos. Si alguien quita el `:not()`, esas casillas
   dejan de verse.
   ────────────────────────────────────────────────────────────────────────── */
.nv-panel input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="submit"]):not([type="button"]):not([type="image"]):not([type="reset"]),
.nv-panel textarea,
.nv-panel select,
.nv-modal input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="submit"]):not([type="button"]):not([type="image"]):not([type="reset"]),
.nv-modal textarea,
.nv-modal select {
  width: 100%;
  min-width: 0;
  /* 44 px es el minimo tactil. Los 21 px que salian por defecto no se pueden
     acertar con el pulgar en un potrero, y menos con guantes. */
  min-height: 44px;
  background: var(--nv-campo);
  color: var(--nv-texto);
  border: 1px solid var(--nv-campo-borde);
  border-radius: var(--nv-r-input);
  padding: 11px 13px;
  font: 400 15px/1.4 var(--nv-f-body);
  outline: none;
  transition: border-color var(--nv-dur-fast) var(--nv-ease),
              box-shadow var(--nv-dur-fast) var(--nv-ease);
}
/* 15 px y no 14: por debajo de 16 px Safari de iOS hace ZOOM al enfocar un
   campo y deja la pantalla descuadrada. 15 px con `min-height` holgado es el
   punto donde el texto se lee sin que el navegador se meta. */
.nv-panel textarea, .nv-modal textarea { line-height: 1.5; resize: vertical; }

.nv-panel input::placeholder, .nv-panel textarea::placeholder,
.nv-modal input::placeholder, .nv-modal textarea::placeholder {
  color: var(--nv-campo-ph); opacity: 1;
}

/* EL FOCO TIENE QUE VERSE. Sin esto el campo enfocado es identico al de al
   lado y en un formulario de cinco no se sabe donde se esta escribiendo —
   que es justo lo que el dueño pidio para el campo de intereses. */
.nv-panel input:focus, .nv-panel textarea:focus, .nv-panel select:focus,
.nv-modal input:focus, .nv-modal textarea:focus, .nv-modal select:focus {
  border-color: var(--nv-savia);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--nv-savia) 22%, transparent);
}
.nv-panel select, .nv-modal select { cursor: pointer; }

/* ══════════════════════════════════════════════════════════════════════════
   EL COMPOSITOR RECUPERA SU ALTURA — y esto es un arreglo de una regresión
   ══════════════════════════════════════════════════════════════════════════

   MEDIDO en un navegador a 360 px el 2026-08-09, en Pecuaria: el campo del
   compositor tenía **46 px de alto y su contenido 67**. El ejemplo que le
   sugiere al productor —«Ej: ordeño de hoy 92 litros… o vacuné el lote
   leche»— se partía por la mitad y la segunda línea quedaba cortada por
   abajo. Lo reportó el dueño.

   LA CAUSA: `.nv-composer textarea` (línea 282) pide `min-height: 105px`,
   pensado para que quepan varias líneas dictadas. Pero el bloque genérico de
   `.nv-panel textarea` de aquí arriba —el que arregló los campos blancos sobre
   el tema oscuro— pide `min-height: 44px`, **tiene la MISMA especificidad y va
   DESPUÉS en la hoja**. Empate, gana el último: 44 px.

   O sea que arreglar los campos blancos encogió, sin que nadie lo notara, los
   DIEZ compositores de la app. Es la clase de regresión que no da error: solo
   deja de caber el texto.

   POR QUÉ ASÍ Y NO SUBIENDO EL DE ARRIBA: `.nv-panel .nv-composer` gana por
   especificidad de verdad (dos clases), no por orden. Un arreglo que depende
   de quién va antes en el archivo se rompe la próxima vez que alguien mueva un
   bloque — que es exactamente lo que pasó aquí.

   Los 44 px de la regla genérica siguen valiendo para todos los demás campos:
   es el mínimo táctil, y el motivo está escrito arriba. */
.nv-panel .nv-composer textarea, .nv-modal .nv-composer textarea,
.nv-panel .nv-composer input, .nv-modal .nv-composer input {
  min-height: 105px;
  max-height: 190px;
  line-height: 1.45;
}

/* ══════════════════════════════════════════════════════════════════════════
   EL ICONO DE ENVIAR, QUE ERA VERDE SOBRE VERDE
   ══════════════════════════════════════════════════════════════════════════

   El dueño: «el icono casi invisible, se adivina más que se ve».

   LA CAUSA, medida: los iconos de `assets/js/icons.js` traen el color ESCRITO
   DENTRO del SVG — `stroke="url(#nvIG)"`, un degradado verde. No usan
   `currentColor`. Así que `.nv-send { color: #04140E }` y la regla
   `.nv-send svg { color: inherit }` de la línea 296 **no alcanzan a los
   trazos**: el icono se dibuja en verde encima de un botón verde.

   Se comprobó en el navegador: `getComputedStyle(path).stroke` valía
   literalmente `url("#nvIG")`.

   EL ARREGLO NO LLEVA `!important`: un atributo de presentación de SVG pierde
   contra CUALQUIER regla CSS, así que basta con seleccionarlo. Y va con
   `currentColor` para que siga al `color` del botón — si mañana el botón
   cambia de fondo, el icono lo sigue solo.

   SOLO PARA `.nv-send`. Los iconos de las herramientas (micrófono, cámara,
   documento) van sobre fondo oscuro y ahí el verde SE VE bien — comprobado en
   la misma captura. Tocarlos también sería arreglar lo que no está roto. */
.nv-composer .nv-send svg [stroke],
.nv-composer .nv-send svg[stroke] { stroke: currentColor; }

/* ══════════════════════════════════════════════════════════════════════════
   EL MENU DE PESTAÑAS SE QUEDA FIJO
   ══════════════════════════════════════════════════════════════════════════

   «Este menu deberia ser fijo y que se scrollee lo de abajo, no que se pierda
   el menu.» (dueño, 2026-08-08, sobre la fila de Ganado: Lotes en subasta ·
   Mis lotes · Flete y merma · Permisos · Donde parar.)

   POR QUE `sticky` AQUI SI FUNCIONA, que es lo que hay que entender antes de
   tocar esto: el contenedor de scroll NO es la pagina, es `.nv-panel`, que
   tiene `overflow-y:auto` y una altura ACOTADA por `height:100dvh` en
   `.nv-app`. Eso ya esta razonado arriba en el bloque del APP SHELL, y es la
   misma razon por la que el composer pudo anclarse abajo. Si algun dia
   alguien le quita la altura al shell, esto deja de pegarse — no se rompe,
   se queda como estaba.

   LO SEGUNDO NO ES COSMETICA. Medido a 360 px: las cinco pestañas no caben,
   asi que el navegador partia el texto en dos y tres lineas («Lotes / en /
   subasta») y la fila crecia de 36 px a 61 px, con «Donde parar» saliendose
   4 px por la derecha. Una barra pegada que ademas ocupa 61 px de una
   pantalla de 740 px es peor que no pegarla. Por eso pasa a deslizarse en
   horizontal: cada pestaña en UNA linea, y se arrastra para ver el resto.

   `scrollbar-width:none` y el `::-webkit-scrollbar` en cero: es una fila de
   pestañas, no una lista; una barra de scroll gris cruzada por debajo de las
   pildoras se lee como un fallo de pintado.

   EL FONDO ES OBLIGATORIO. Sin `background` la barra pegada deja ver el
   contenido pasando POR DEBAJO y se vuelve ilegible a los dos segundos de
   arrastrar. Va con el fondo del panel, no transparente.
   ────────────────────────────────────────────────────────────────────────── */
.nv-panel .nv-seg {
  position: sticky;
  top: 0;
  z-index: 5;
  /* De `inline-flex` a `flex`: una caja de nivel de linea con `sticky` se
     ancla de forma inconsistente entre navegadores, y ademas no puede ocupar
     el ancho para deslizarse. */
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* DEJA DE SER UNA PILDORA Y PASA A SER UNA FRANJA. La pildora funcionaba
     centrada y suelta en medio del contenido; pegada arriba no, porque una
     caja con borde y radio deja ver el contenido colandose por las esquinas
     redondeadas mientras se desplaza, y eso se lee como un fallo de pintado.
     La franja se extiende de borde a borde con los 18 px de padding del panel
     en negativo, apoya en una linea inferior y tapa lo que pasa por debajo.
     La pildora NO se pierde: sigue siendo la pestaña activa (.nv-seg-b.on),
     que es donde de verdad hacia falta. */
  margin: 0 calc(var(--nv-panel-pad) * -1);
  width: calc(100% + var(--nv-panel-pad) * 2);
  padding: 8px var(--nv-panel-pad);
  background: var(--nv-abismo);
  border: none;
  border-bottom: 1px solid var(--nv-linea);
  border-radius: 0;
  scroll-snap-type: x proximity;
  /* LA FRANJA DE 18 px QUE SE COLABA POR ENCIMA. `sticky` con `top:0` apoya en
     el borde del area de contenido, o sea 18 px por debajo del borde visible
     del panel — el padding. Medido: al desplazar, por esa rendija se veia
     pasar la foto del lote por encima de las pestañas.
     Se tapa con una sombra SOLIDA hacia arriba (sin desenfoque) del color del
     fondo. Sombra y no un `::before`: el pseudo-elemento lo recortaria el
     `overflow-x:auto` de esta misma caja; la sombra se pinta fuera de la caja
     y el overflow no la toca.
     Sin desplazar, esa sombra cae sobre el hueco que hay hasta la tarjeta de
     arriba — mismo color que el fondo, invisible. Por eso el hueco de
     `.nv-panel` y este valor son LA MISMA VARIABLE: si alguien reduce el
     hueco por debajo del padding, la sombra empieza a comerse la tarjeta. */
  box-shadow: 0 calc(var(--nv-panel-pad) * -1) 0 0 var(--nv-abismo);
}
.nv-panel .nv-seg::-webkit-scrollbar { height: 0; width: 0; }
.nv-panel .nv-seg-b {
  flex: 0 0 auto;          /* que no se encojan: encogerse es lo que partia el texto */
  white-space: nowrap;     /* «Flete y merma» en UNA linea */
  scroll-snap-align: start;
}
/* La sombra solo aparece cuando la barra ya esta pegada y hay algo pasando por
   debajo, para que no parezca un borde suelto cuando la pantalla no scrollea.
   `animation-timeline: scroll()` degrada solo: donde no se entienda, la barra
   sigue fija y sin sombra. */
@supports (animation-timeline: scroll()) {
  .nv-panel .nv-seg {
    animation: nv-seg-pegada linear both;
    animation-timeline: scroll(nearest block);
    animation-range: 0 40px;
  }
  @keyframes nv-seg-pegada {
    from { box-shadow: 0 calc(var(--nv-panel-pad) * -1) 0 0 var(--nv-abismo); }
    to   { box-shadow: 0 calc(var(--nv-panel-pad) * -1) 0 0 var(--nv-abismo),
                       0 8px 16px -8px rgba(0,0,0,.55); }
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   AIRE ENTRE CONTENEDORES
   ══════════════════════════════════════════════════════════════════════════

   «Hay que darle aire entre contenedores... es importante que no se vea todo
   saturado.» (dueño, 2026-08-08, sobre Ganado, Precios, Industria, Inicio IA
   y Prospeccion.)

   LA CAUSA NO ERA QUE FALTARA UN MARGEN AQUI Y ALLA. `.nv-panel` YA reparte
   16 px entre sus hijos. Lo que pasa es que casi ningun modulo tiene varios
   hijos: mete TODO dentro de un unico `div` envoltorio, asi que el panel
   reparte 16 px entre un solo elemento — o sea, entre nada — y dentro del
   envoltorio, que es un `display:block` pelado, los bloques se apilan
   tocandose.

   MEDIDO a 360 px en produccion, envoltorio por envoltorio:

     subasta .nv-sb    display:block  gap:normal  4 hijos
     precios .nv-mi    display:block  gap:normal  4 hijos
     prospect .nv-mi   display:block  gap:normal  4 hijos
     library .nv-lb    display:block  gap:normal  6 hijos
     assets  .nv-as    display:block  gap:normal  8 hijos
     ...y asi 12 de los 15 modulos mirados.

   Y el sintoma exacto que fotografio el dueño, medido: en Ganado, el borde de
   abajo de `.nv-ag-intro` y el de arriba de `.nv-seg` estaban en la MISMA
   coordenada. Hueco de 0 px. Una tarjeta empezaba donde acababa la otra.

   POR QUE UNA LISTA Y NO `.nv-panel > div:only-child`, que seria mas corto y
   valdria para los modulos que vengan: porque `:only-child` tambien cogeria a
   `.nv-ib` (Inbox), que es una rejilla de dos paneles con su propio
   `gap:14px`, y ponerle `flex-direction:column` le desmonta la vista de
   bandeja. La lista es fea pero no rompe nada; el atajo si.

   OJO CON `.nv-lb`: ese nombre lo usan TRES cosas distintas — el envoltorio de
   Biblioteca, la etiqueta de los KPI (`.nv-kpi .nv-lb`) y las etiquetas de
   formulario de Ganado. Por eso aqui va con `>` desde `.nv-panel`: asi solo
   alcanza al envoltorio y no toca las otras dos.
   ────────────────────────────────────────────────────────────────────────── */
.nv-panel > .nv-sb,
.nv-panel > .nv-mi,
.nv-panel > .nv-lb,
.nv-panel > .nv-ac,
.nv-panel > .nv-sv,
.nv-panel > .nv-mn,
.nv-panel > .nv-tr,
.nv-panel > .nv-bf,
.nv-panel > .nv-un,
.nv-panel > .nv-ec,
.nv-panel > .nv-as,
/* Sumados 2026-08-21 (reportado por Daniel: "sin aire" en Agricultura
   protegida, IA Agronómica, Monitoreo del cultivo y compañía) — la misma
   auditoría de arriba, hecha el 2026-08-08, sólo miró 15 módulos y estos se
   quedaron afuera. `.nv-ib` y `.nv-dl` SIGUEN sin entrar aquí a propósito:
   ambos ya traen su propio `display:grid` de dos paneles (ver `.nv-dl` más
   arriba en esta hoja), y ponerles `flex-direction:column` desmontaría esa
   vista partida — el mismo motivo que ya excluía a `.nv-ib`. */
.nv-panel > .nv-ag,
.nv-panel > .nv-cr,
.nv-panel > .nv-pr,
.nv-panel > .nv-lv,
.nv-panel > .nv-fd,
.nv-panel > .nv-co,
.nv-panel > .nv-lic,
.nv-panel > .nv-kn,
.nv-panel > .nv-pf,
.nv-panel > .nv-emb,
.nv-panel > .nv-st {
  display: flex;
  flex-direction: column;
  gap: var(--nv-aire);
  min-width: 0;   /* si no, un hijo ancho estira el envoltorio y desborda */
}

/* UN NIVEL MAS ADENTRO (parte 2, 2026-08-21) — mismo problema que
   `.nv-sb-body` más arriba, en los módulos sumados hoy: el envoltorio de
   arriba ya reparte aire entre SUS hijos directos, pero cuando el módulo
   mete todo dentro de un SEGUNDO `div` (`.nv-pr-dash`, `.nv-cr-body`,
   `.nv-bf-body`), ese segundo nivel sigue siendo `display:block` pelado y
   el aire se pierde ahí. Medido: ninguna de las tres tenía una sola regla
   en toda la hoja.
   Sumados 2026-08-22 (QA de Daniel, IA Agronómica clase por clase): mismo
   fallo exacto en `.nv-ag-hist` (agro_lab.js), `.nv-ag-inbody`
   (agro_inputs.js), `.nv-ag-result` (agro_lab.js) y `.nv-ag-satbody`
   (agro_sat.js) — los cuatro son el SEGUNDO `div` dentro de `.nv-ag`, cero
   reglas en toda la hoja, confirmado con grep. */
.nv-pr-dash,
.nv-cr-body,
.nv-bf-body,
.nv-ag-hist,
.nv-ag-inbody,
.nv-ag-result,
.nv-ag-satbody {
  display: flex;
  flex-direction: column;
  gap: var(--nv-aire);
  min-width: 0;
}

/* ══════════════════════════════════════════════════════════════════════════
   UN NIVEL MAS ADENTRO
   ══════════════════════════════════════════════════════════════════════════

   La regla de arriba arregla los ENVOLTORIOS de modulo, y FUNCIONA: medido hoy
   a 360 px, el envoltorio de Ganado (`.nv-sb`) es `flex` con 16 px de verdad.
   Pero dentro del envoltorio hay un SEGUNDO `display:block` pelado, y ahi se
   volvia a perder todo el aire otra vez.

   MEDIDO con `getBoundingClientRect()` a 360 px, ANTES de esta regla:

     Ganado > Mis lotes       entre `.nv-sb-card`         0 px  (y `margin: 0px`)
     Ganado > Mis lotes       lista → «Publicar un lote»  0 px
     Ganado > Flete y merma   ultimo campo → «Calcular»   0 px
     Pecuaria                 «Diagnosticar» → «Plan»     0 px

   POR QUE LOS DOS, `.nv-sb-body` Y SU `> div`: `subasta.js` cuelga dentro de
   `.nv-sb-body` un contenedor ANONIMO (`var host = el('div')`) y mete ahi la
   lista; en «Mis lotes» ademas deja el boton «Publicar un lote» como hermano de
   ese contenedor. O sea que habia dos huecos de 0 px, uno en cada nivel, y
   arreglar solo uno dejaba el otro pegado. `.nv-sb-body` lo comparten LAS CINCO
   pestañas de Ganado — se abrieron las cinco.

   `.nv-lv-formrow` NO TENIA UNA SOLA REGLA en toda la hoja: por eso los dos
   botones de Pecuaria se tocaban. Lo usan tambien `assets.js` (3 sitios) y
   otras 4 filas de `livestock.js`; se miraron las 8 y todas son filas de
   `.nv-btn`, que ya trae `width:100%`, asi que el `stretch` del flex no cambia
   nada de ancho — solo separa.
   ────────────────────────────────────────────────────────────────────────── */
.nv-sb-body,
.nv-sb-body > div,
.nv-lv-formrow {
  display: flex;
  flex-direction: column;
  gap: var(--nv-aire);
  min-width: 0;
}

/* ETIQUETA Y CAMPO SON UNA PAREJA, Y TIENEN QUE LEERSE COMO TAL.
   En «Flete y merma» las cinco etiquetas eran `<label>` sin una sola regla, o
   sea elementos EN LINEA: el texto se enredaba alrededor de los campos y salia
   «Cuantos animales[campo]Peso promedio por animal (kg)[campo]Lo que te
   ofrecen por...». Se leia como un parrafo roto, no como un formulario.
   El hueco de ARRIBA es mayor que el de abajo a proposito: es lo unico que
   dice que la etiqueta pertenece al campo que tiene debajo y no al de encima.
   `label.nv-lb` y no `.nv-lb`: ver el aviso de los tres dueños de ese nombre. */
label.nv-lb {
  display: block;
  margin: var(--nv-sp-5, 24px) 0 var(--nv-sp-2, 8px);
  font-size: 13px;
  color: var(--nv-tenue);
  line-height: 1.35;
}
label.nv-lb:first-child { margin-top: 0; }

/* Precios no usa `.nv-seg`: se pinta sus propias pestañas (`.nv-pr-tabs`, en
   `precios.css`). Le hace falta lo mismo, y por el mismo motivo — es otra de
   las pantallas que fotografio el dueño.

   DOS DIFERENCIAS CON LAS DE ARRIBA, medidas antes de escribir esto:
   ya tenia `overflow-x: auto`, asi que la mitad de «no partir el texto» estaba
   resuelta y aqui solo falta fijarla; y hoy NO se pierde al bajar porque la
   pantalla entera cabe sin scroll (`scrollHeight` 655 = `clientHeight` 655).
   O sea: hoy no se ve el fallo, pero esta ahi — en cuanto la lista de precios
   crezca lo suficiente para desplazar, la barra se va con ella. Se arregla
   ahora que es una linea y no cuando vuelva a costar una captura.

   VA AQUI Y NO EN `precios.css` a proposito: `.nv-panel .nv-pr-tabs` pesa
   (0,2,0) y le gana a `.nv-pr-tabs` (0,1,0) sin depender de que hoja carga
   antes, y asi este arreglo no toca un fichero de otro modulo. */
.nv-panel .nv-pr-tabs {
  position: sticky;
  top: 0;
  z-index: 5;
  margin: 0 calc(var(--nv-panel-pad) * -1);
  width: calc(100% + var(--nv-panel-pad) * 2);
  padding: 8px var(--nv-panel-pad);
  background: var(--nv-abismo);
  border-bottom: 1px solid var(--nv-linea);
  box-shadow: 0 calc(var(--nv-panel-pad) * -1) 0 0 var(--nv-abismo);
}

/* ══ EMBAJADORES · LA FILA DE CADA NIVEL — ARREGLO DEL FALLO F.2 ════════════
   Estas cuatro clases NO TENIAN NI UNA REGLA en toda la hoja. `.nv-emb-nivel`
   caia en `display:block` sin `gap`, y `ambassadors.js` le mete dos `span`
   pegados sin un solo nodo de texto en medio. Resultado medido en el navegador,
   no supuesto — las cinco filas se pintaban asi:

       Nivel 110%   Nivel 25%   Nivel 33%   Nivel 42%   Nivel 51%

   El numero del nivel fundido con el porcentaje. El dueno las leyo como «10,
   25, 33, 42, 51» y pregunto por que suben. No suben: son 10, 5, 3, 2 y 1, que
   BAJAN, que es exactamente lo que manda `ML_PCTS` en `routers/multilevel.js`.
   Las cinco filas cuadran una a una con esa lectura, y por eso esto es un fallo
   de pantalla y no de backend.

   `space-between` empuja el porcentaje al borde derecho, que ademas es donde se
   busca una cifra en una lista. El separador `·` va aparte, en el DOM: si esta
   regla se poda o la hoja no carga, lo peor que sale es una fila fea — no una
   fila que promete un 51% que nadie va a pagar.
   ═══════════════════════════════════════════════════════════════════════════ */
.nv-emb-niveles { display: flex; flex-direction: column; margin: 10px 0; }
.nv-emb-nivel {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 0;
  border-bottom: 1px solid var(--nv-linea);
}
.nv-emb-nivel:last-child { border-bottom: none; }
.nv-emb-nivellb { font-size: 13px; color: var(--nv-texto); }
/* El `margin-left:auto` va en el SEPARADOR y no en el porcentaje: asi los dos
   viajan juntos al borde derecho y se leen como una sola cosa («· 10%»). Puesto
   en el porcentaje, el `·` se quedaba solo pegado a la etiqueta y parecia un
   resto de algo. */
.nv-emb-nivelsep { margin-left: auto; color: var(--nv-tenue); font-size: 12px; }
.nv-emb-nivelpct { font-size: 14px; font-weight: 600; color: var(--nv-texto); }
/* La frase que presenta la lista. Un poco mas separada que las notas de abajo
   porque es lo primero que hay que leer, no un pie. */
.nv-emb-reglaque { margin-bottom: 4px; }

/* PERFIL · la linea que separa la foto del sello de verificado. Tenue y pegada
   a la cabecera: es una aclaracion, no un aviso de error — pintarla en rojo
   diria que algo se rompio. */
.nv-pf-fotonota { font-size: 12px; line-height: 1.45; color: var(--nv-tenue); margin: -4px 0 14px; }

/* ══════════════════════════════════════════════════════════════════════════
   NVModal — LA VENTANA REUSABLE (2026-08-09)
   ══════════════════════════════════════════════════════════════════════════
   Se estrena con el «?» de la banda de transporte, pero NO es suya: se
   construyo generica porque en cuanto exista la van a querer en Comunidad y en
   los porcentajes de embajadores, y una segunda ventana copiada a mano llega
   con los mismos cuatro bugs de accesibilidad.

   ⚠️ NO es `.nv-modal`. Esa clase ya existia en este archivo con reglas de
   formulario (`.nv-modal input`, `.nv-modal textarea`…) que NINGUN JS usaba;
   reusar el nombre habria hecho que esas reglas huerfanas se le cayeran encima
   a cualquier campo que se meta aqui dentro. Prefijo propio: `.nv-md-*`.

   MEDIDO A 360 px: `max-width` con `calc(100vw - 32px)` y el cuerpo con scroll
   propio, para que un texto largo no empuje la ventana fuera de la pantalla ni
   obligue a la pagina a desplazarse en horizontal.
   ────────────────────────────────────────────────────────────────────────── */
.nv-md-velo { position: fixed; inset: 0; z-index: 140; background: rgba(4,20,14,.72);
  display: flex; align-items: center; justify-content: center; padding: 16px;
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.nv-md { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); box-shadow: var(--nv-sh-2);
  width: 100%; max-width: min(460px, calc(100vw - 32px));
  max-height: calc(100vh - 32px); display: flex; flex-direction: column; }
.nv-md-cab { display: flex; align-items: center; gap: 10px; padding: 12px 12px 12px 16px;
  border-bottom: 1px solid var(--nv-linea); }
.nv-md-t { font-family: var(--nv-f-display); font-size: 15px; margin: 0; flex: 1; min-width: 0; }
/* 44 px de lado. El glifo es pequeño; el AREA no. */
.nv-md-x { flex: 0 0 44px; width: 44px; height: 44px; padding: 0; border: 0;
  background: transparent; color: var(--nv-tenue); font-size: 16px; line-height: 1;
  cursor: pointer; border-radius: var(--nv-r-sm); }
.nv-md-x:hover { background: var(--nv-suelo); color: var(--nv-texto); }
.nv-md-x:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: -2px; }
.nv-md-cuerpo { padding: 14px 16px 18px; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.nv-md-cuerpo p { font-size: 13.5px; line-height: 1.6; color: var(--nv-tenue); margin: 0; }

/* Texto solo para lector de pantalla. Sin `display:none` ni `visibility:hidden`
   —esos lo esconden TAMBIEN del lector, que es lo contrario de lo que se
   busca— y sin `width:0`, que en algunos motores lo colapsa a nada. */
.nv-sr { position: absolute !important; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0; }

/* ============================================================================
   B.1/B.3/B.4 (2026-08-10, agente tarjetas-fase2) — clases NUEVAS únicamente.
   No se toca ninguna regla existente de este archivo.
   ========================================================================== */

/* ---------- B.3: Biofábrica — tarjetas del catálogo con foto ----------------
   Antes de esto NINGUNA clase `.nv-bf-*` tenía CSS (la pantalla se pintaba con
   divs sueltos, sin borde ni grilla): de ahí la queja del dueño de que «se ve
   muy feo». Solo se estilan las clases de la tarjeta del catálogo
   (`tarjetaReceta`/`pintarFicha` en `biofabrica.js`); el resto de la pantalla
   (pasos, ingredientes) no es parte de este encargo. */
.nv-bf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; margin: 10px 0; }
.nv-bf-card { display: flex; gap: 12px; text-align: left; background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); padding: 13px; cursor: pointer; color: var(--nv-texto); box-shadow: var(--nv-sh-1); transition: var(--nv-dur-fast); }
.nv-bf-card:hover, .nv-bf-card:focus-visible { border-color: var(--nv-linea-fuerte); outline: none; }
.nv-bf-media { width: 84px; flex-shrink: 0; border-radius: var(--nv-r-sm); overflow: hidden; }
.nv-bf-media img { width: 84px; height: 84px; object-fit: cover; display: block; }
.nv-bf-cbody { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.nv-bf-cname { font-size: 14px; font-weight: 700; margin: 0; }
.nv-bf-cdesc { font-size: 12.5px; line-height: 1.4; color: var(--nv-tenue); margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.nv-bf-meta { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.nv-bf-chip { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--nv-linea-fuerte); background: var(--nv-suelo); color: var(--nv-tenue); }
.nv-bf-card .nv-btn { margin-top: 4px; align-self: flex-start; }
/* El crédito de Pexels viaja PEGADO a la foto, siempre — regla de la casa. */
.nv-bf-credito { font-size: 10.5px; line-height: 1.3; color: var(--nv-tenue); padding: 3px 6px; }
.nv-bf-credito a { color: inherit; text-decoration: underline; }
/* La misma foto, más grande, en la ficha completa (B.3, `pintarFicha`). */
.nv-bf-fmedia { width: 100%; border-radius: var(--nv-r-md); overflow: hidden; margin: 4px 0 10px; }
.nv-bf-fmedia img { width: 100%; height: 200px; object-fit: cover; display: block; }
@media (max-width: 480px) { .nv-bf-grid { grid-template-columns: 1fr; } }

/* ---------- Biofábrica — la ficha completa (rediseño 2026-08-21) -----------
   El bloque de arriba (B.3, 2026-08-10) decía explícitamente que "pasos,
   ingredientes" quedaban FUERA de ese encargo. Daniel los reportó ahora:
   "El paso a paso" salía como un `<ol>` de `<span>` pegados sin una sola
   regla — el día, el texto y la frecuencia todos en la misma línea, sin
   espacio. Mismo criterio que el resto de la hoja: tarjeta por paso, franja
   de color cuando hay peligro asociado. */
.nv-bf-fname { font-size: 18px; margin: 4px 0 2px; }
.nv-bf-fdesc { font-size: 13px; color: var(--nv-tenue); line-height: 1.5; margin: 0 0 4px; }
.nv-bf-sub { font-size: 13.5px; font-weight: 700; margin: 4px 0 0; }
.nv-bf-peligro { background: color-mix(in srgb, var(--nv-dorado) 9%, var(--nv-superficie));
  border: 1px solid color-mix(in srgb, var(--nv-dorado) 35%, transparent);
  border-radius: var(--nv-r-md); padding: 12px 14px; display: flex; flex-direction: column; gap: 5px; }
.nv-bf-peligro h5 { margin: 0 0 2px; font-size: 12.5px; color: var(--nv-dorado); }
.nv-bf-peligro.nv-bf-grave { background: color-mix(in srgb, var(--nv-critico) 10%, var(--nv-superficie));
  border-color: color-mix(in srgb, var(--nv-critico) 40%, transparent); }
.nv-bf-peligro.nv-bf-grave h5 { color: var(--nv-critico); }
.nv-bf-pline { font-size: 12.5px; line-height: 1.45; margin: 0; color: var(--nv-texto); }

.nv-bf-form { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); padding: 14px; display: flex; flex-direction: column; gap: 8px; }
.nv-bf-lb { font-size: 12px; color: var(--nv-tenue); }
.nv-bf-fila { display: flex; align-items: center; gap: 8px; }
.nv-bf-inp { flex: 1 1 auto; min-width: 0; }
.nv-bf-u { font-size: 12.5px; color: var(--nv-tenue); flex-shrink: 0; }
.nv-bf-hint { font-size: 11.5px; color: var(--nv-tenue); line-height: 1.4; margin: 0; }
.nv-bf-calc { display: flex; flex-direction: column; gap: 10px; }

.nv-bf-pasos { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.nv-bf-paso { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-left: 3px solid var(--nv-savia); border-radius: var(--nv-r-sm);
  padding: 11px 13px; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
.nv-bf-pdia { font: 700 11px/1 var(--nv-f-mono); color: var(--nv-savia);
  background: color-mix(in srgb, var(--nv-savia) 14%, transparent); border-radius: var(--nv-r-pill);
  padding: 4px 9px; flex-shrink: 0; }
.nv-bf-ptxt { font-size: 13px; line-height: 1.4; flex: 1 1 200px; }
.nv-bf-pcada { font-size: 11px; color: var(--nv-tenue); flex-basis: 100%; }
.nv-bf-pav { flex-basis: 100%; margin: 4px 0 0; font-size: 11.5px; color: var(--nv-dorado); line-height: 1.4; }

.nv-bf-senal, .nv-bf-extra, .nv-bf-fuente { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); padding: 13px 14px; }
.nv-bf-senal h5, .nv-bf-fuente h5 { margin: 0 0 4px; font-size: 12.5px; color: var(--nv-tenue); }
.nv-bf-senal p, .nv-bf-cita, .nv-bf-citaq { font-size: 12.5px; line-height: 1.5; margin: 0; }
.nv-bf-citaq { color: var(--nv-tenue); margin-top: 3px; }
.nv-bf-rango, .nv-bf-dura { font-size: 12.5px; line-height: 1.5; margin: 0 0 4px; color: var(--nv-tenue); }
.nv-bf-rango:last-child, .nv-bf-dura:last-child { margin-bottom: 0; }

.nv-bf-ings { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.nv-bf-ing { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm);
  padding: 9px 12px; font-size: 13px; }
.nv-bf-ing.tiene { border-color: color-mix(in srgb, var(--nv-savia) 35%, transparent); }
.nv-bf-ingn { font-weight: 700; }
.nv-bf-ingq { color: var(--nv-tenue); font-family: var(--nv-f-mono); font-size: 12px; }
.nv-bf-ingt { margin-left: auto; font-size: 11px; font-weight: 700; color: var(--nv-critico); }
.nv-bf-ing.tiene .nv-bf-ingt { color: var(--nv-savia); }
.nv-bf-ingo { font-size: 10.5px; color: var(--nv-tenue); font-style: italic; }
.nv-bf-rinde { background: color-mix(in srgb, var(--nv-savia) 8%, var(--nv-superficie));
  border: 1px solid color-mix(in srgb, var(--nv-savia) 30%, transparent);
  border-radius: var(--nv-r-md); padding: 13px 14px; }
.nv-bf-rindeq { font-size: 15px; font-weight: 800; color: var(--nv-savia-hi); margin: 0; }
.nv-bf-rinded { font-size: 11.5px; color: var(--nv-tenue); margin: 3px 0 0; }
.nv-bf-agenda { font-size: 12.5px; color: var(--nv-tenue); line-height: 1.5; }
.nv-bf-acc { display: flex; }

/* ---------- B.4: Academia — portada de curso (miniatura firmada de Bunny) ---
   `.nv-ac-cthumb`/`.nv-ac-thumb` ya existían (con degradado + ícono); aquí
   solo se agrega el caso CON foto, igual al patrón ya usado en
   `.nv-mn-media.con-foto` (Mis publicaciones). */
.nv-ac-cthumb.con-foto, .nv-ac-thumb.con-foto { background: none; }
.nv-ac-cthumb img, .nv-ac-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit; }

/* ---------- Plan maestro (rediseño 2026-08-21) ---------------------------
   Reportado por Daniel: la etiqueta "Región, departamento o provincia
   (opcional)" se montaba sobre su propio campo. `.nv-pm-campos`/
   `.nv-pm-campo` no tenían NI UNA regla en toda la hoja — cero resultados de
   grep. `.nv-pm-campo` va en columna (label ARRIBA, campo ABAJO): con eso,
   una etiqueta de dos líneas simplemente empuja el campo hacia abajo, nunca
   se le monta encima — a diferencia de una grilla de una sola fila con alto
   fijo, que es lo que produce el "montaje" que se reportó. */
.nv-pm-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-lg); padding: 18px; box-shadow: var(--nv-sh-1);
  display: flex; flex-direction: column; gap: var(--nv-aire); }
.nv-pm-h { font-size: 16px; margin: 0; }
.nv-pm-campos { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 16px 14px; }
.nv-pm-campo { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.nv-pm-campo label { font-size: 12px; color: var(--nv-tenue); line-height: 1.35; }
.nv-pm-campo input, .nv-pm-campo select { width: 100%; }

/* ── Labores culturales (2026-08-22) ────────────────────────────────────────
   Clases PROPIAS `.nv-lc-*` y no las `.nv-pr-*` de Agricultura protegida,
   aunque el checklist se vea parecido. El motivo no es estético: son dos
   dominios distintos (invernadero por SISTEMA · lote abierto por ETAPA) y van
   a divergir. Compartir la clase obliga a que un retoque en una se le aplique
   a la otra sin que nadie lo haya decidido. Lo que SÍ se reusa es
   `.nv-ag-select`, porque ahí sí es literalmente el mismo control. */
.nv-lc { display: flex; flex-direction: column; gap: 14px; }
.nv-lci { display: inline-flex; align-items: center; }
.nv-lc-tabs { display: flex; gap: 8px; flex-wrap: wrap; }
.nv-lc-tab { font: 700 13px/1 var(--nv-f-body); padding: 9px 15px; border-radius: var(--nv-r-pill);
  background: var(--nv-superficie); border: 1px solid var(--nv-linea); color: var(--nv-tenue); cursor: pointer;
  transition: var(--nv-dur-fast); }
.nv-lc-tab:hover { border-color: var(--nv-linea-fuerte); color: var(--nv-texto); }
.nv-lc-tab.is-on { background: var(--nv-savia); border-color: var(--nv-savia); color: #04140E; }
.nv-lc-body { display: flex; flex-direction: column; gap: 12px; }

/* El encabezado del día: plantilla y etapa a la izquierda, pendientes a la
   derecha. El contador NO se pinta en rojo cuando quedan tareas — quedan
   porque el día empieza, no porque algo esté mal. */
.nv-lc-hoy { display: flex; flex-direction: column; gap: 12px; }
.nv-lc-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 13px 15px; background: var(--nv-superficie); border: 1px solid var(--nv-linea-fuerte);
  border-radius: var(--nv-r-md); }
.nv-lc-headinfo { min-width: 0; }
.nv-lc-headtpl { font-size: 13.5px; font-weight: 700; color: var(--nv-texto); }
.nv-lc-headstage { font-size: 11.5px; color: var(--nv-tenue); margin-top: 3px; text-transform: capitalize; }
.nv-lc-pend { font-size: 12px; font-weight: 700; color: var(--nv-tenue); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-pill); padding: 6px 12px; flex-shrink: 0; }
.nv-lc-pend.is-done { color: #04140E; background: var(--nv-savia); border-color: var(--nv-savia); }

.nv-lc-tasks { display: flex; flex-direction: column; gap: 7px; }
.nv-lc-task { display: flex; align-items: flex-start; gap: 11px; text-align: left; width: 100%;
  padding: 11px 13px; background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-sm); cursor: pointer; color: var(--nv-texto); transition: var(--nv-dur-fast); }
.nv-lc-task:hover { border-color: var(--nv-linea-fuerte); }
.nv-lc-task[disabled] { opacity: .55; cursor: progress; }
.nv-lc-taskbox { width: 24px; height: 24px; flex-shrink: 0; border-radius: 7px;
  border: 1.5px solid var(--nv-linea-fuerte); display: grid; place-items: center; margin-top: 1px; }
.nv-lc-task.done .nv-lc-taskbox { background: var(--nv-savia); border-color: var(--nv-savia); }
.nv-lc-task.done .nv-lc-taskbox svg { color: #04140E; }
.nv-lc-tasktext { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.nv-lc-tasklabel { font-size: 13px; line-height: 1.45; }
/* De qué etapa sale cada tarea. Sin esto la lista cambia sola de un día para
   otro y el productor no tiene forma de saber por qué. */
.nv-lc-taskstage { font-family: var(--nv-f-mono); font-size: 9.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--nv-fantasma); }
.nv-lc-task.done .nv-lc-tasklabel { color: var(--nv-tenue); text-decoration: line-through; }
.nv-lc-nota { font-size: 12px; color: var(--nv-tenue); line-height: 1.5; }

.nv-lc-asignar { display: flex; flex-direction: column; gap: 10px; padding: 14px;
  background: var(--nv-superficie); border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md); }
.nv-lc-asignarow { display: flex; gap: 9px; flex-wrap: wrap; align-items: center; }
.nv-lc-asignarow .nv-ag-select { flex: 1 1 200px; min-width: 0; }

.nv-lc-nueva { align-self: flex-start; display: inline-flex; align-items: center; gap: 8px; }
.nv-lc-tpls { display: flex; flex-direction: column; gap: 8px; }
.nv-lc-tpl { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-sm); }
.nv-lc-tplinfo { min-width: 0; flex: 1 1 200px; }
.nv-lc-tplname { font-size: 13.5px; font-weight: 700; }
.nv-lc-tplmeta { font-size: 11.5px; color: var(--nv-tenue); margin-top: 3px; }
.nv-lc-tplacc { display: flex; gap: 7px; flex-shrink: 0; }
.nv-lc-tplacc .nv-btn { padding: 8px 13px; font-size: 12.5px; }
.nv-lc-tplacc .is-bad { color: #E4572E; border-color: color-mix(in srgb, #E4572E 45%, transparent); }

.nv-lc-edhead { display: flex; align-items: center; gap: 10px; }
.nv-lc-edtitle { font-size: 15px; font-weight: 700; }
.nv-lc-form { display: flex; flex-direction: column; gap: 10px; }
.nv-lc-lab { font-size: 12px; color: var(--nv-tenue); }
.nv-lc-in { width: 100%; font: 400 13.5px/1.4 var(--nv-f-body); padding: 11px 13px;
  background: var(--nv-superficie); color: var(--nv-texto);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-lc-in:focus { outline: none; border-color: var(--nv-savia); }
.nv-lc-edtasks { display: flex; flex-direction: column; gap: 7px; }
.nv-lc-edrow { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; }
.nv-lc-edstage { flex: 0 1 170px; min-width: 130px; font: 400 12.5px/1 var(--nv-f-body); padding: 10px;
  background: var(--nv-superficie); color: var(--nv-texto);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-sm); }
.nv-lc-edrow .nv-lc-in { flex: 1 1 220px; width: auto; }
.nv-lc-edx { flex-shrink: 0; width: 34px; height: 34px; border-radius: var(--nv-r-sm);
  background: transparent; border: 1px solid var(--nv-linea); color: var(--nv-tenue);
  font-size: 17px; line-height: 1; cursor: pointer; }
.nv-lc-edx:hover { border-color: var(--nv-linea-fuerte); color: var(--nv-texto); }
.nv-lc-edpie { display: flex; gap: 9px; flex-wrap: wrap; margin-top: 6px; }

/* ══════════════════════════════════════════════════════════════════════════
   `.nvx-*` — LA TARJETA QUE SE DESPLIEGA. COMPONENTE REUSABLE DE TODA LA APP.
   ══════════════════════════════════════════════════════════════════════════

   DE DONDE SALE: del artefacto que Daniel APROBO el 2026-08-22 («Rediseño de
   tarjetas de TUXANI», 3ra vuelta). No es un diseño nuevo inventado aqui:
   los valores de abajo —46 px de miniatura, 13.5 px de titulo, 16.5 px en el
   detalle, 10 px 12 px de relleno en la fila, la rotacion de 180° del chevron—
   son los del artefacto, copiados, no reinterpretados.

   EL PROBLEMA QUE VIENE A APAGAR, en palabras del dueño mirando produccion:
   «Estas tarjetas estan horribles no llevan el modelo que habiamos seleccionado
   que era que fueran desplegables y que se vieran bonitas como en el artefakto».
   Lo que habia era la tarjeta SIEMPRE ABIERTA: en Industria, nueve pastillas
   sueltas + tres lineas de cifras + descripcion, todo a la vez, por tarjeta.

   ─── LA REGLA CENTRAL DEL DISEÑO APROBADO ─────────────────────────────────
   EL TITULO OCUPA SOLO SU FRANJA SUPERIOR (`.nvx-dtitle`), y el precio y el
   estado van DEBAJO, en su propia fila (`.nvx-dmeta`). Nunca compitiendo por el
   mismo renglon — fue la correccion explicita que pidio el dueño en la vuelta
   anterior del artefacto, y es lo primero que se pierde si alguien «compacta»
   esto mas adelante.

   ─── EL ESQUELETO, ESCRITO AQUI PARA QUE LA PROXIMA OLEADA LO COPIE ───────
   Esta es la OLEADA 1 (Industria + Ganado). Quedan ~26 familias de tarjetas en
   la app con el mismo problema. El DOM exacto que esperan estas reglas es:

     <article class="nvx-item">                    <- la superficie (borde, sombra)
       <button class="nvx-listrow" aria-expanded="false" aria-controls="ID">
         <span class="nvx-thumb">…icono…</span>    <- 46x46, icono o miniatura
         <div class="nvx-listmain">
           <div class="nvx-listtitle">…</div>      <- UNA linea, con ellipsis
           <div class="nvx-listmeta">
             <span class="nvx-listprice">…</span>
             <span class="nvx-chip ok">…</span>
           </div>
         </div>
         <span class="nvx-listend"><span class="nvx-chevron">…svg…</span></span>
       </button>
       <div class="nvx-detailwrap" id="ID"><div class="nvx-detailinner">
         <div class="nvx-detail">
           …media opcional…
           <div class="nvx-dhead">
             <div class="nvx-dtitle">…</div>
             <div class="nvx-dmeta"><div class="nvx-dprice">…<small>…</small></div>
               <span class="nvx-chip ok">…</span></div>
           </div>
           <div class="nvx-dgrid"><div class="nvx-dkv"><span>ROTULO</span><b>dato</b></div>…</div>
           <p class="nvx-ddesc">…texto corrido…</p>
           <div class="nvx-dfoot"><button class="nvx-cta">…</button></div>
         </div>
       </div></div>
     </article>

   Y el JS son cuatro lineas, en el `click` de `.nvx-listrow`:
     var abierto = fila.getAttribute('aria-expanded') === 'true';
     fila.setAttribute('aria-expanded', String(!abierto));
     item.classList.toggle('is-open', !abierto);

   ─── POR QUE `--nv-*` Y NO LOS TOKENS DEL ARTEFACTO ───────────────────────
   El artefacto declara `--savia`, `--dorado`, `--linea`… en su propio `:root`.
   Copiarlos aqui seria declarar una SEGUNDA fuente de la paleta: se
   desincroniza en cuanto alguien afine un color en `tokens.css`, y —peor— el
   artefacto trae su propio interruptor claro/oscuro y `tokens.css` YA tiene el
   suyo. Dos interruptores para el mismo tema es como se consigue una tarjeta
   clara sobre fondo oscuro. Comprobado uno a uno: los valores son LOS MISMOS
   con otro nombre (`--savia:#178069` es `--nv-savia:#178069` del bloque claro).
   Asi que el modo oscuro sale gratis y no hay nada que mantener por duplicado.

   ─── LAS TRES DESVIACIONES DEL ARTEFACTO, Y SU MOTIVO ─────────────────────
   1. `.nvx-detail` NO lleva `max-width:340px`. En el artefacto es una vitrina
      de un solo ejemplar; en la app va dentro de una lista y tiene que ocupar
      el ancho de su fila.
   2. `.nvx-dprice` usa `clamp(15px,4.4vw,19px)` y no 19 px fijos. El dato real
      de Ganado es un RANGO («$12.400 – $13.900»), no un precio suelto: a 19 px
      fijos se sale de un telefono de 360 px. Es la misma leccion que ya
      pagamos en `.nv-sb-big-n` (ver su comentario).
   3. El CTA lleva texto `#04140E` y no `#fff`. Sobre `--nv-savia` en modo
      oscuro (#2FA98C) el blanco se queda en 2,4:1. `#04140E` es lo que ya usa
      `.nv-btn-primary` en toda la casa.
   ────────────────────────────────────────────────────────────────────────── */

/* ---- el contenedor de la lista ----
   La fila desplegable quiere UNA columna: en una rejilla de 2-3 columnas, abrir
   una tarjeta estira a sus vecinas de la misma fila. `.nv-in-grid`/`.nv-sb-grid`
   se quedan puestas en el mismo nodo porque `verify_marketplace` §2.3/§4.2
   cuenta la rejilla por esa clase; el doble selector es para ganarle en
   especificidad a su `@media (min-width:560px)`, no por manía. */
.nvx-list { display: flex; flex-direction: column; gap: 10px; }
.nv-in-grid.nvx-list, .nv-sb-grid.nvx-list { display: flex; grid-template-columns: none; }

/* ---- la superficie: una sola caja que crece al abrirse ---- */
.nvx-item { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); box-shadow: var(--nv-sh-1); overflow: hidden;
  display: flex; flex-direction: column; min-width: 0;
  transition: border-color var(--nv-dur) var(--nv-ease), box-shadow var(--nv-dur) var(--nv-ease); }
.nvx-item:hover { border-color: var(--nv-linea-fuerte); }
/* `.nv-in-card`/`.nv-sb-card` viven en ESTE MISMO nodo (las cuenta el arnes de
   QA). Su `transform: translateY(-2px)` de hover era para una tarjeta muerta:
   en una que se despliega, el salto al pasar el raton pelea con la animacion. */
.nv-in-card.nvx-item:hover, .nv-sb-card.nvx-item:hover { transform: none; }
.nvx-item.is-open { border-color: var(--nv-linea-fuerte); box-shadow: var(--nv-sh-2); }

/* ══ ESTADO 1 · COLAPSADA ════════════════════════════════════════════════
   Es la fila de «Mis Publicaciones», que es la que el dueño señalo como la que
   SI le gusta. Foto/icono, titulo, precio, estado, y ya. Todo lo demas —las
   nueve pastillas de Industria incluidas— se va al detalle. */
.nvx-listrow { display: flex; align-items: center; gap: 12px; width: 100%;
  background: none; border: 0; padding: 10px 12px; margin: 0; text-align: left;
  color: inherit; font: inherit; cursor: pointer; min-width: 0; }
.nvx-listrow:hover .nvx-listtitle { color: var(--nv-savia); }
.nvx-thumb { position: relative; width: 46px; height: 46px; flex-shrink: 0;
  border-radius: 10px; overflow: hidden; background: var(--nv-suelo);
  display: grid; place-items: center; color: var(--nv-savia); line-height: 0; }
.nvx-listmain { flex: 1 1 128px; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
/* UNA sola linea con puntos suspensivos. Los titulos reales de produccion son
   de 90 caracteres («Comercializadora exporta aguacate Hass, 12 toneladas
   semanales»); a dos y tres lineas la fila deja de ser una fila. El titulo
   entero se lee al abrir, en `.nvx-dtitle`, que si respira. */
.nvx-listtitle { font-weight: 700; font-size: 13.5px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; transition: color var(--nv-dur-fast); }
/* `flex-wrap: wrap` y NO un recorte con puntos suspensivos. Se probó el recorte
   primero: con el dato real de produccion —«Comercializadora o exportador» al
   lado de «COP $7.500/tonelada»— la suma no cabe en los 236 px utiles de una
   columna de 372, asi que la fila envolvia IGUAL y lo unico que conseguia el
   recorte era mutilar el rotulo. A los anchos reales del panel de la app las
   dos cosas caben de sobra en un renglon; a 360 px envuelve, que es lo correcto
   —dos lineas se leen; un precio cortado por la mitad, no—. */
.nvx-listmeta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.nvx-listprice { color: var(--nv-savia); font-weight: 700; font-family: var(--nv-f-mono);
  font-size: 12.5px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.nvx-listend { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.nvx-chevron { display: inline-flex; color: var(--nv-fantasma); line-height: 0;
  transition: transform var(--nv-dur) var(--nv-ease); flex-shrink: 0; }
.nvx-listrow[aria-expanded="true"] .nvx-chevron { transform: rotate(180deg); color: var(--nv-savia); }

/* ---- el chip de estado ----
   Mismos valores que `.nvx-feed .nvx-chip` de `nvx-pecuario-alimento.css`: esa
   hoja llego antes con el mismo artefacto y su regla, al ser mas especifica,
   sigue ganando dentro de `.nvx-feed`. No se toca. */
.nvx-chip { display: inline-flex; align-items: center; gap: 5px;
  font: 600 10.5px/1 var(--nv-f-body); color: var(--nv-tenue);
  background: var(--nv-suelo); border: 1px solid var(--nv-linea-fuerte);
  border-radius: var(--nv-r-pill); padding: 5px 9px; white-space: nowrap; }
.nvx-chip.ok { color: var(--nv-savia); }
.nvx-chip.mid { color: var(--nv-dorado); }
.nvx-chip.high { color: var(--nv-critico); }
/* `.off` NO esta en el artefacto: lo pide el dato real de Ganado, que tiene
   cuatro estados y dos de ellos (desierta, cancelada) son «esto ya no esta
   vivo». `app.css` ya los pintaba con `--nv-fantasma` en `.nv-sb-est-desierta`;
   se conserva ese criterio para no tener dos apagados distintos. */
.nvx-chip.off { color: var(--nv-fantasma); }

/* ══ ESTADO 2 · EXPANDIDA ════════════════════════════════════════════════
   `grid-template-rows: 0fr -> 1fr` es la unica forma de animar «hasta el alto
   que tenga el contenido» sin medirlo en JS. `visibility` va atada al mismo
   interruptor —y no solo el alto— porque una caja de 0 px de alto SIGUE siendo
   navegable con el tabulador y la sigue leyendo un lector de pantalla: sin esto,
   el teclado se mete en un detalle que el ojo no ve. El `transition` de
   `visibility` a 0s CON RETRASO al cerrar deja que la animacion termine antes
   de esconderlo; al abrir no hay retraso. */
.nvx-detailwrap { display: grid; grid-template-rows: 0fr; visibility: hidden;
  transition: grid-template-rows var(--nv-dur) var(--nv-ease),
              visibility 0s linear var(--nv-dur); }
.nvx-item.is-open > .nvx-detailwrap { grid-template-rows: 1fr; visibility: visible;
  transition: grid-template-rows var(--nv-dur) var(--nv-ease), visibility 0s; }
.nvx-detailinner { overflow: hidden; min-height: 0; }
.nvx-detail { min-width: 0; border-top: 1px solid var(--nv-linea); }
/* La foto la sigue pintando `NVTarjeta.media()` (`.nv-foto`), que es la pieza
   honesta: descarta la foto de stock repetida y pinta «Sin foto» cuando no hay.
   Aqui solo se le pone la linea de abajo para que no flote contra la ficha. */
.nvx-detail .nv-foto { border-bottom: 1px solid var(--nv-linea); }

.nvx-dhead { padding: 15px 16px 12px; }
.nvx-dtitle { font-family: var(--nv-f-display); font-weight: 700; font-size: 16.5px;
  line-height: 1.3; text-wrap: balance; overflow-wrap: anywhere; }
.nvx-dmeta { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.nvx-dprice { font-family: var(--nv-f-mono); font-weight: 700; color: var(--nv-savia);
  font-size: clamp(15px, 4.4vw, 19px); font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere; min-width: 0; }
.nvx-dprice small { font-family: var(--nv-f-body); font-weight: 600; font-size: 11.5px;
  color: var(--nv-tenue); margin-left: 3px; }
/* La coletilla larga va DEBAJO del numero, no pegada a su derecha. Medido en el
   preview con el dato de Ganado: «$12.400 – $13.900» + «por kilo que TE LLEGA»
   en el mismo renglon partia la frase en «…que TE / LLEGA» y empujaba el chip a
   una tercera linea. Y esa coletilla no es una unidad, es LA frase que explica
   por que este numero no es el precio del aviso: partida a la mitad no explica
   nada. */
.nvx-dprice small.nvx-dnota { display: block; margin: 3px 0 0; }

/* La grilla de datos: `minmax(0,1fr)` y no `1fr` — un valor largo sin espacios
   («Silvania, Cundinamarca») desborda la columna con `1fr` a secas. */
.nvx-dgrid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 10px 16px; padding: 13px 16px; border-top: 1px solid var(--nv-linea); }
.nvx-dkv { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.nvx-dkv span { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase; letter-spacing: .04em; }
.nvx-dkv b { font-size: 12.5px; font-weight: 600; overflow-wrap: anywhere; }

/* Texto corrido: aqui es donde caen las nueve pastillas sueltas de Industria
   («Factura · RUT · Buenas Practicas Agricolas · …»). Leerlas en una frase
   cuesta menos que nueve cajas peleando por el ancho. */
.nvx-ddesc { padding: 0 16px 13px; margin: 0; font-size: 12.5px;
  color: var(--nv-tenue); line-height: 1.5; overflow-wrap: anywhere; }
.nvx-ddesc:empty { display: none; }
/* Bloques que ya tienen su propio estilo (merma, flete, la subasta desierta) y
   solo necesitan el relleno de la ficha. */
.nvx-dextra { padding: 0 16px 13px; }
/* Columna con aire: en «Mis lotes» el pie lleva DOS botones (cerrar y cancelar)
   mas el hueco de mensajes. Sin `gap` salen pegados uno contra otro — es
   exactamente el «se ve apeñuscado» que ya se reporto una vez en esta pantalla. */
.nvx-dfoot { padding: 13px 16px 16px; display: flex; flex-direction: column; gap: 8px; }
.nvx-cta { width: 100%; font: 700 13px/1 var(--nv-f-body); color: #04140E;
  background: var(--nv-savia); border: none; border-radius: var(--nv-r-sm);
  padding: 12px; cursor: pointer; text-align: center; transition: var(--nv-dur-fast); }
.nvx-cta:hover { background: var(--nv-savia-hi); }
.nvx-cta:disabled { opacity: .5; cursor: not-allowed; }

/* ══════════════════════════════════════════════════════════════════════════
   `.nvx-grid` / `.nvx-card` — LA OTRA MITAD DEL MISMO ARTEFACTO: LA TARJETA
   SUELTA DE REJILLA, CON FOTO.  (OLEADA 2 · Marketplace, 2026-08-22)
   ══════════════════════════════════════════════════════════════════════════

   MISMO artefacto aprobado, otra pieza. El de arriba (`.nvx-item`/`.nvx-listrow`)
   es la FILA QUE SE DESPLIEGA de una lista de texto. Este es la tarjeta suelta
   de la seccion «Las 28 familias» del artefacto, que es la que lleva
   `.nvx-media` arriba y la regla de siempre: TITULO SOLO EN SU FRANJA, meta
   debajo (`.nvx-metarow`). El propio artefacto lo dice con todas las letras:
   «Las 28 familias de abajo usan la MISMA idea de "titulo arriba, meta debajo"
   en su version compacta de tarjeta suelta».

   ─── POR QUE MARKETPLACE NO LLEVA `.nvx-listrow` ───────────────────────────
   1. EL MARKETPLACE ES LA PANTALLA DE REFERENCIA, no una a corregir. El
      encargo que creo `tarjeta.js` fue, literal: «en el mercado Industria y
      ganado, es importante que se vean COMO SI FUERA EL MARKETPLACE: como
      tarjetas con imagenes». Convertir el marketplace en una lista de texto
      seria borrar el modelo al que se acaba de parecer las otras dos.
   2. `.nvx-listrow` obliga a `.nvx-list`, que es UNA columna (abrir un acordeon
      en una rejilla estira a sus vecinas de fila). Medido en produccion: 20
      publicaciones. En una columna, la plaza pasa de una pantalla a cinco.
   3. La miniatura de `.nvx-listrow` es 46x46 con un ICONO, no la foto. 8 de las
      20 publicaciones vivas traen foto propia; esconderlas en un cuadradito de
      46 px tira lo unico que hace que una plaza se lea de un vistazo.

   ─── POR QUE NO LLEVA ACORDEON (`.nvx-toggle` / `.nvx-detailwrap`) ─────────
   Porque el detalle YA EXISTE y no es este: tocar la tarjeta abre
   `NutriversoDetalle.abrir(l, {propia:false})` — fotos, descripcion, fechas y
   el hilo con la otra parte. Un acordeon aqui seria un SEGUNDO detalle, mas
   pobre, del mismo objeto: dos sitios contando lo mismo, que es como se
   consigue que uno de los dos mienta.
   Y hay un motivo de uso, no solo de arquitectura: hoy la tarjeta ENTERA es el
   boton. Meterle un chevron dejaria dos blancos a 8 px uno del otro que
   contestan cosas distintas a la misma pregunta («enseñame esto»).
   El acordeon del artefacto es para las familias que NO tienen pantalla de
   detalle (un lote, una alerta, un curso). Esta si la tiene.

   ─── POR QUE VA TODO BAJO `.nvx-grid` Y NO SUELTO ──────────────────────────
   `nvx-pecuario-alimento.css` YA usa estos nombres (`.nvx-card`, `.nvx-head`,
   `.nvx-title`, `.nvx-metarow`, `.nvx-sub`, `.nvx-stat`) y `livestock.js` los
   pinta — siempre dentro de `.nvx-feed`, y esa hoja los escribe TODOS scoped.
   Si estas reglas fueran sueltas, cada propiedad que aquella hoja no declare
   (el recorte a dos lineas del titulo, por ejemplo) se le colaria a las
   tarjetas de alimentacion por cascada. Scoped a `.nvx-grid` no hay cruce
   posible, y es el mismo contrato que ya tiene `.nvx-list` con `.nvx-item`.

   ─── LAS TRES DESVIACIONES DEL ARTEFACTO, Y SU MOTIVO ──────────────────────
   1. `.nvx-title` va RECORTADO A DOS LINEAS. En el artefacto los titulos son de
      galeria («Lote El Roble»); los REALES de esta pantalla llegan a 67
      caracteres («Comercializadora exporta aguacate Hass, 12 toneladas
      semanales DEMO»), que en una columna de 250 px son cuatro renglones y una
      rejilla que baila. Es la misma leccion de `.nvx-listtitle` (ahi, una
      linea), con el limite que aguanta una tarjeta en vez de una fila. El
      titulo entero se lee al tocar, en `NutriversoDetalle`.
   2. `.nvx-stat` NO lleva color de acento: hereda `--nv-texto`, que es lo que
      hace el artefacto (su unico `.nvx-stat` coloreado es una METRICA —el NDVI
      «0.472»—; el que lleva dinero, «$45.000», va en color de texto).
      SE PROBO PRIMERO EN `--nv-savia`, por parecerse a `.nvx-listprice` y
      `.nvx-dprice` de la oleada 1, y se descarto MIDIENDO: `#2FA98C` sobre
      `--nv-superficie` en modo oscuro da 2,93:1 — el precio que hay hoy
      (`--nv-cosecha`, `#B7CC00`) da 9,65:1. Habria sido cambiar un numero
      legible por uno que no llega ni a AA grande, en nombre de la coherencia.
      El color de texto da 15,7:1 en claro y sigue separandose del resto por lo
      que ya lo separaba: mono, negrita y cifras tabulares.
   3. NO se escribe `:hover` para `.nvx-card`. La tarjeta del marketplace ya
      tiene el suyo (`.nv-mk-card:hover` levanta, `.nv-mk-click:hover` pinta el
      borde en savia porque la tarjeta entera es un boton) y no era lo que
      estaba mal. Una regla mia aqui solo serviria para ganarle por
      especificidad a un hover que si funciona.
   ────────────────────────────────────────────────────────────────────────── */

/* El contenedor. Los valores son los del artefacto: `clamp(210px,36vw,250px)`
   arranca en 210 en un telefono de 360 —igual que la rejilla que habia— y en
   pantalla ancha para en 250 en vez de seguir metiendo columnas de 210, que es
   lo que dejaba los titulos reales en tres y cuatro renglones. */
.nvx-grid { display: grid; gap: clamp(10px, 2vw, 16px);
  grid-template-columns: repeat(auto-fill, minmax(clamp(210px, 36vw, 250px), 1fr)); }

/* `flex` en columna para que el pie de acciones se pegue abajo: en una fila de
   la rejilla las tarjetas se estiran a la mas alta, y sin esto los botones
   quedan flotando a media altura, cada uno a la suya. */
.nvx-grid .nvx-card { background: var(--nv-superficie); border: 1px solid var(--nv-linea);
  border-radius: var(--nv-r-md); box-shadow: var(--nv-sh-1); overflow: hidden;
  display: flex; flex-direction: column; min-width: 0; }
/* La foto la pinta `NVTarjeta.media()` (`.nv-foto`), la misma pieza honesta que
   usan Industria y Ganado. Aqui solo se le pone la linea de abajo. */
.nvx-grid .nvx-card > .nv-foto { border-bottom: 1px solid var(--nv-linea); }

.nvx-grid .nvx-head { display: flex; flex-direction: column; gap: 7px; padding: 13px 14px; }
.nvx-grid .nvx-title { font-family: var(--nv-f-display); font-weight: 700; font-size: 13.5px;
  line-height: 1.28; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.nvx-grid .nvx-metarow { display: flex; align-items: center; justify-content: space-between;
  gap: 8px; flex-wrap: wrap; min-width: 0; }
.nvx-grid .nvx-sub { font-size: 11px; color: var(--nv-tenue); min-width: 0; }
.nvx-grid .nvx-stat { font-family: var(--nv-f-mono); font-weight: 700; font-size: 14.5px;
  font-variant-numeric: tabular-nums; overflow-wrap: anywhere; min-width: 0; }
.nvx-grid .nvx-stat small { font-family: var(--nv-f-body); font-weight: 600; font-size: 10.5px;
  color: var(--nv-tenue); margin-left: 2px; }

/* El pie de acciones. `margin-top: auto` es lo que lo pega abajo; el relleno se
   lo pone aqui porque el nodo vive FUERA de `.nvx-head` (si entrara dentro,
   los botones heredarian el `gap` de la cabecera y se pegarian al dato de
   arriba, que es el «se ve apeñuscado» que ya costo una vuelta en `.nvx-dfoot`). */
.nvx-grid .nvx-card > .nv-mk-act { margin-top: auto; padding: 0 14px 14px; }

/* El chip de DEMANDA. No se usa `.mid` (dorado): en toda esta app el dorado
   significa «falta un dato / atencion» (`.nv-in-falta-t`, el «Sin perfil» de
   Industria), y que alguien COMPRE en vez de vender no es un aviso, es la otra
   mitad de la plaza — medido hoy: 10 de 20. `--nv-cosecha` es el color con el
   que la casa YA distingue una demanda (`.nv-mk-ichip.is-demanda`, en la ficha
   de prospeccion); se reusa ese criterio en vez de inventar un segundo. */
.nvx-chip.dem { color: var(--nv-cosecha); }

/* ══════════════════════════════════════════════════════════════════════════
   OLEADA 3 DEL PATRON `.nvx-*` — Academia, Biofabrica, Biogas, Cultivo y
   Deal Rooms.  (2026-08-22)
   ══════════════════════════════════════════════════════════════════════════

   NO HAY NI UNA REGLA NUEVA DE DISEÑO AQUI. Todo lo de abajo es el mismo
   trabajo de siempre: las cinco pantallas traian clases propias —de cuando
   cada tarjeta se pintaba a mano— que siguen puestas en el MISMO nodo que
   `.nvx-item`/`.nvx-card`, y algunas de ellas declaran `padding`, `gap` o
   `margin` que el patron no declara y que por lo tanto SOBREVIVEN a la
   cascada. Un `padding: 13px` heredado en la caja de fuera deja la foto sin
   sangrar y el detalle flotando; un `margin-bottom: 8px` se suma al `gap` de
   `.nvx-list` y descuadra la columna entera.

   POR QUE NO SE BORRAN ESAS CLASES DE LOS MODULOS Y YA: porque varias llevan
   informacion que el patron no tiene —la franja de severidad de una alerta, el
   borde rojo del filtro de H2S, el borde punteado de la mejora propuesta— y
   porque el `@media` de una columna en telefono vive en ellas. Se conservan y
   se les apaga SOLO lo que estorba, que es una linea por caso.
   ────────────────────────────────────────────────────────────────────────── */

/* ---- Academia: la tarjeta de curso y la de lectura entran en `.nvx-grid` ----
   El pie va en COLUMNA y no en fila: antes era `justify-content: space-between`
   porque compartia renglon con el precio, y el precio ya no vive ahi — subio a
   `.nvx-metarow`, que es la regla del artefacto (titulo solo en su franja, la
   meta debajo). Lo que queda en el pie es el boton y el hueco de errores de
   `botonCurso()`, y esos dos se leen uno encima del otro. */
.nvx-grid .nvx-card > .nv-ac-cfoot { margin-top: auto; padding: 0 14px 14px;
  flex-direction: column; align-items: stretch; }
/* `.nv-ac-cmeta` traia `margin: 6px 0` de cuando era el unico separador dentro
   del cuerpo. Dentro de `.nvx-head`, que ya tiene `gap: 7px`, eso son 13 px. */
.nvx-grid .nvx-head > .nv-ac-cmeta { margin: 0; }

/* ---- Biofabrica: la foto de banco pasa de miniatura lateral a banda ----
   `.nv-bf-card` era una fila (`display:flex; gap:12px; padding:13px`) con la
   foto de 84x84 a la izquierda. `.nvx-card` la vuelve columna, pero el relleno
   y el hueco NO los declara el patron y se quedarian puestos: la banda de foto
   saldria con 13 px de margen por dentro, que es justo lo que hace que una
   tarjeta con imagen se vea pegada. */
.nvx-grid .nv-bf-card { padding: 0; gap: 0; }
.nvx-grid .nvx-card > .nv-bf-media { width: 100%; border-radius: 0;
  border-bottom: 1px solid var(--nv-linea); }
/* 132 px es la altura de `.nv-foto`, la banda que usan Marketplace, Industria
   y Ganado. Se copia el numero para que las cinco pantallas tengan la foto del
   mismo alto y las rejillas no bailen al pasar de una a otra. */
.nvx-grid .nvx-card > .nv-bf-media img { width: 100%; height: 132px; }
.nvx-grid .nvx-card > .nv-bf-cfoot { margin-top: auto; padding: 0 14px 14px; }

/* ---- Cultivo: la alerta se despliega, y su severidad sigue viendose ----
   La franja izquierda de 3 px es el unico sitio donde el nivel se lee sin
   mirar el chip, y `.nvx-item` la borra sin querer: su `border: 1px solid` es
   una propiedad ABREVIADA, o sea que reescribe `border-left` entero. Se vuelve
   a poner aqui, ya dentro del patron. El `padding` de `.nv-cr-alert` si se
   apaga: el relleno lo pone la fila (`.nvx-listrow`), y uno en la caja de
   fuera dejaria el detalle flotando dentro de un marco. */
.nvx-item.nv-cr-alert { padding: 0; border-left-width: 3px; border-left-color: var(--nv-tenue); }
.nvx-item.nv-cr-alert.lvl-alta { border-left-color: var(--nv-critico); }
.nvx-item.nv-cr-alert.lvl-media { border-left-color: var(--nv-dorado); }
.nvx-item.nv-cr-alert.lvl-ok { border-left-color: var(--nv-savia-hi); }
/* CAZADO AL MIRAR LA PANTALLA MONTADA EN EL NAVEGADOR, no leyendo el codigo, y
   no es de la tarjeta: es la de al lado. `.nv-cr-phenometa` reparte sus dos
   hijos con `justify-content: space-between` y SIN `gap`, asi que con el dato
   real de produccion —«Cultivo perenne: 3 año(s) y 2 mes(es) desde la
   siembra.», que ocupa dos renglones— el rotulo y la nota se quedan pegados y
   se lee «FenologiaCultivo perenne…». Una declaracion. */
.nv-cr-phenometa { gap: 10px; }

/* ---- Deal Rooms: el prestador se despliega ----
   Mismo caso: `.nv-dl-prov` era una fila con su propio relleno y su propio
   `margin-bottom: 8px`. El margen se apaga porque la separacion ya la pone el
   `gap` de `.nvx-list`; sumados daban 18 px entre prestadores y 10 entre
   categorias, o sea la jerarquia al reves. */
.nvx-item.nv-dl-prov { padding: 0; gap: 0; margin-bottom: 0; }
/* La columna de prestadores va DENTRO de la caja de servicios, que ya tiene su
   relleno: solo hace falta separarla del rotulo de su categoria. */
.nv-dl-provs { margin: 8px 0 12px; }

/* ---- Biogas: cada seccion del informe es una fila que se despliega ----
   El contenido de las secciones son los mismos `.nv-bg-p`, `.nv-bg-fila` y
   `.nv-bg-cifras` de siempre, que no traen relleno propio porque antes vivian
   dentro de un `.nv-bg-card` con `padding: 14px`. Ahora viven en `.nvx-dextra`,
   que pone el relleno de los lados y el de abajo pero no el de arriba —en
   Industria y Ganado ese bloque va precedido de la cabecera de la ficha, y
   aqui es lo primero que hay debajo de la linea. */
.nv-bg .nvx-detail > .nvx-dextra { padding-top: 13px; }
/* 🔴 EL BORDE ROJO DEL FILTRO DE H2S Y EL AMBAR DE «LO QUE NO SE SABE», QUE SE
   PERDIAN. Los pone el propio modulo (`.nv-bg-h2s`, `.nv-bg-sin`), y esas dos
   reglas son de UNA clase; `.nvx-item.is-open` son DOS, asi que le gana por
   especificidad aunque la hoja del modulo se inyecte despues. Y como estas dos
   secciones arrancan ABIERTAS —que es justo lo que exigen las reglas 2 y 4 de
   la cabecera de `biogas.js`—, el borde que se veia siempre era el verde de
   «abierta»: el filtro de H2S, que es el aviso de corrosion, salia sin su
   marca. CAZADO MIDIENDO CON `getComputedStyle` EN EL NAVEGADOR, no leyendo.

   Se usan `--nv-critico` y `--nv-dorado` y no los respaldos que escribe el
   modulo (`#e8695b`, `#e8b84b`): son los mismos colores con los que `.nvx-chip`
   pinta «alto» y «atencion» tres reglas mas arriba, y dos rojos parecidos en la
   misma pantalla se leen como dos cosas distintas. */
.nvx-item.nv-bg-h2s, .nvx-item.nv-bg-h2s.is-open { border-color: var(--nv-critico); }
.nvx-item.nv-bg-sin, .nvx-item.nv-bg-sin.is-open { border-color: var(--nv-dorado); }
/* La miniatura de la fila. Mismo trato que `.nv-cri`/`.nv-ibi` en las otras
   pantallas: el `<span>` que envuelve el SVG tiene que ser una caja flex o el
   icono no se centra en los 46 px. */
.nvx-thumb-ic { display: inline-flex; align-items: center; justify-content: center; }
/* El veredicto se queda en su tarjeta, arriba y solo — es LA respuesta, no una
   seccion del informe. Esto es lo unico que lo separa de la lista. */
.nv-bg-secciones { margin-top: 14px; }

/* ══════════════════════════════════════════════════════════════════════════
   OLEADA 4 DEL PATRON `.nvx-*` — Comando del Fundador, Fincas, Conocimiento,
   Biblioteca, Licitaciones y las tres pantallas de Mercado.  (2026-08-22)
   ══════════════════════════════════════════════════════════════════════════

   COMO EN LA OLEADA 3: NO HAY NI UNA REGLA NUEVA DE DISEÑO AQUI. Todo lo de
   abajo apaga lo que SOBREVIVE a la cascada: cada una de estas pantallas traia
   clases propias —de cuando la tarjeta se pintaba a mano— que siguen puestas en
   el MISMO nodo que `.nvx-item`/`.nvx-card` y que declaran `padding`, `gap` o
   `margin` que el patron no declara. Un `padding` heredado en la caja de fuera
   deja el detalle flotando dentro de un marco; un `margin-bottom` se suma al
   `gap` de `.nvx-list` y descuadra la columna entera.

   ─── LA ESPECIFICIDAD, QUE AQUI DECIDE MAS QUE EL ORDEN ────────────────────
   `licitaciones.css` se enlaza DESPUES de `app.css` en `web/app/index.html`,
   asi que `.nv-lic-card` —una clase— le ganaba a `.nvx-item` —una clase— por
   orden de hoja. Por eso sus reglas van escritas con DOS clases
   (`.nvx-item.nv-lic-card`): dos le ganan a una venga la hoja donde venga. Es
   la misma leccion que la oleada 3 pago con el borde rojo del filtro de H2S en
   Biogas, y la unica forma de que esto no dependa del orden de los `<link>`.

   ─── LO QUE ESTA OLEADA MIDIO Y NO SE ARREGLA AQUI ─────────────────────────
   `.nv-mk-conf-alta`, `.nv-mk-conf-media`, `.nv-mk-conf-baja`,
   `.nv-mk-prio-1/2/3` y `.nv-mk-fuera` NO EXISTEN en ninguna hoja: cero reglas.
   Medido con `getComputedStyle` en produccion, las tres confianzas de la ficha
   de prospeccion daban EXACTAMENTE el mismo color, sin fondo y sin borde. No se
   arregla escribiendo tres colores nuevos —seria un cuarto juego para la misma
   escala—: `market_prospect.js` pasa a usar `.nvx-chip.ok/.mid/.high`, que ya
   estan medidos. Estas clases se quedan escritas en el nodo por si alguien las
   quiere estilar algun dia, pero HOY no pintan nada y conviene saberlo.
   ────────────────────────────────────────────────────────────────────────── */

/* ---- Comando del Fundador: las tres colas se despliegan ----
   `.nv-fd-card` traia `padding: 15px` y `margin-bottom: 12px` de cuando era una
   tarjeta muerta. Escrito con DOS clases a proposito: la MISMA `.nv-fd-card` se
   sigue usando —bien usada— como caja del parrafo que escribe el modelo en la
   pestaña de auditoria, y esa SI tiene que conservar su relleno. */
.nvx-item.nv-fd-card { padding: 0; margin-bottom: 0; }

/* ---- Fincas: la rejilla de expedientes ----
   El `padding` y el `margin-top` de `.nv-fn-item` se apagan en el propio
   modulo (`modules/fincas.js`, que inyecta su hoja), no aqui: esa hoja es suya
   y tiene dueño. Lo unico que hace falta desde `app.css` es que la tarjeta siga
   alineando su texto a la izquierda dentro de `.nvx-card` — es un `<button>`, y
   un boton centra por defecto. */
.nvx-grid .nv-fn-item { text-align: left; }

/* ---- Conocimiento y Biblioteca: dos rejillas con el mismo problema ----
   Las dos tarjetas eran `display: flex; flex-direction: column` CON su propio
   `padding` (15 px y 14 px). `.nvx-card` repone el flex, pero el relleno no lo
   declara: sin esto, `.nvx-head` —que ya trae 13px 14px— sumaria el suyo y el
   titulo saldria a 28 px del borde. */
.nvx-grid .nv-kn-card, .nvx-grid .nv-lb-card { padding: 0; }
/* Los dos pies, pegados abajo. `margin-top: auto` es lo que iguala la altura de
   los botones en una fila de la rejilla; el relleno va aqui porque el nodo vive
   FUERA de `.nvx-head`. El de Conocimiento ademas pasa a COLUMNA: era
   `space-between` porque compartia renglon con el precio, y el precio subio a
   `.nvx-metarow`. Lo que queda —boton, hueco de errores de pago, confirmacion y
   «ir a la bandeja»— se lee uno encima de otro. */
.nvx-grid .nvx-card > .nv-kn-foot { margin-top: auto; padding: 0 14px 14px;
  flex-direction: column; align-items: stretch; }
.nvx-grid .nvx-card > .nv-lb-foot { margin-top: auto; padding: 0 14px 14px; }
/* `.nv-lb-summary` traia `margin: 6px 0 12px` y `flex: 1` de cuando era el
   unico separador del cuerpo. Dentro de `.nvx-head`, que ya tiene `gap: 7px`,
   ese margen son 19 px por abajo; y el `flex: 1` lo estiraba para empujar el
   pie, trabajo que ahora hace el `margin-top: auto` del pie. */
.nvx-grid .nvx-head > .nv-lb-summary { margin: 0; flex: none; }

/* ---- Licitaciones: 30 titulos de 200 caracteres, en una columna ----
   `.nv-lic-grid` es una rejilla de varias columnas por encima de 620 px (y su
   `@media` tambien es de una clase). La fila que se despliega quiere UNA
   columna: abrir un acordeon en una celda estira a sus vecinas de fila — que es
   justo lo que hacia el formulario de propuesta. Dos clases para ganarle a la
   hoja de despues Y a su `@media`. */
.nv-lic-grid.nvx-list { display: flex; flex-direction: column; grid-template-columns: none; }
/* `.nv-lic-card` traia `padding: 12px 13px 13px`, `gap: 8px` y un
   `border-radius` distinto (`--nv-r-sm`). El relleno lo pone ahora la fila
   (`.nvx-listrow`) y el hueco lo ponen los bloques del detalle. */
.nvx-item.nv-lic-card { padding: 0; gap: 0; border-radius: var(--nv-r-md); }

/* ---- Mercado: la misma tarjeta en tres pantallas ----
   `.nv-mi-card` traia `padding: 15px` y `margin-bottom: 10px`, y `.nv-mk-item`
   otro `margin-bottom: 8px` encima. Sumados al `gap: 10px` de `.nvx-list`
   daban 18 y 25 px entre filas — o sea, tres separaciones distintas en la misma
   columna segun de que pantalla viniera la tarjeta. */
.nvx-item.nv-mi-card { padding: 0; margin-bottom: 0; }
.nvx-item.nv-mk-item { margin-bottom: 0; }
/* La ficha de prospeccion mete DIEZ bloques `.nv-mk-fila` seguidos dentro de
   `.nvx-dextra`, y el primero llega pegado a la linea del detalle:
   `.nvx-dextra` pone el relleno de los lados y el de abajo, pero no el de
   arriba —en Industria ese bloque va precedido de la cabecera de la ficha—.
   Mismo arreglo que la oleada 3 escribio para las secciones de Biogas. */
.nv-mk-fuera .nvx-detail > .nvx-dextra { padding-top: 13px; }
/* `.nv-mk-comps` es `display: flex; flex-direction: column` SIN `gap`: la
   separacion la ponia el `margin-bottom` de cada tarjeta, que acaba de
   apagarse. `.nvx-list` la repone, pero las dos son de una clase y
   `.nv-mk-comps` esta ANTES en la hoja, asi que gana `.nvx-list` — se escribe
   con las dos igualmente para que no dependa de que nadie mueva el bloque. */
.nv-mk-comps.nvx-list { display: flex; flex-direction: column; gap: 10px; }

/* ══════════════════════════════════════════════════════════════════════════
   OLEADA 5 DEL PATRON `.nvx-*` — Caja de tokens, Plan Maestro, Servicios,
   Transporte, Unidad Operativa, IA Agronomica, la portada, Biofabrica y las
   clases en vivo de Academia.  (2026-08-22)
   ══════════════════════════════════════════════════════════════════════════

   COMO EN LAS OLEADAS 3 Y 4: NO HAY NI UNA REGLA NUEVA DE DISEÑO AQUI. Todo lo
   de abajo apaga lo que SOBREVIVE a la cascada. Cada una de estas pantallas
   traia clases propias —de cuando la tarjeta se pintaba a mano— que siguen
   puestas en el MISMO nodo que `.nvx-item`/`.nvx-card` y que declaran
   `padding`, `gap`, `margin` o `flex-direction` que el patron NO declara y que
   por lo tanto no se caen solas. Un `padding: 15px` heredado en la caja de
   fuera deja el detalle flotando dentro de un marco; un `margin-bottom: 9px`
   se suma al `gap` de `.nvx-list` y descuadra la columna entera.

   LAS CLASES SE CONSERVAN EN EL NODO, no se borran de los modulos: varias
   llevan informacion que el patron no tiene —el lima del precio de un flete,
   el fondo solido del sello «te alcanza con este», el apagado de un
   transportador bloqueado— y en varios casos su `@media` de una columna en
   telefono vive en ellas.

   ─── LO QUE ESTA OLEADA MIDIO Y NO SE ARREGLA AQUI ─────────────────────────
   1. `.nv-bf-lote`, `.nv-bf-lhead`, `.nv-bf-lname`, `.nv-bf-lest`,
      `.nv-bf-lcant`, `.nv-bf-lven`, `.nv-bf-lprox`, `.nv-bf-lacc` y las demas
      de la vista «Mis lotes» de Biofabrica NO EXISTIAN en ninguna hoja: cero
      reglas, medido con grep sobre las doce hojas del proyecto. Esa vista eran
      `<div>` crudos con el estilo por defecto del navegador — un `<h5>` de
      serie en una app que no usa `h5` en ningun otro sitio. Por eso al final de
      este bloque, en vez de apagarle cosas, se le ESCRIBEN las que faltaban.
      Es el mismo hallazgo que `tarjeta.js` documento en su dia para Industria
      y Ganado.
   2. Los tokens rotos de Plan Maestro (`--nv-surface`, `--nv-border`,
      `--nv-accent`, `--nv-ok`, `--nv-warn`, `--nv-bad`: ninguno de los seis
      existe) NO se arreglan aqui sino en `modules/plan_maestro.js`, que inyecta
      su propia hoja y tiene dueño. Queda razonado en la cabecera de ese bloque.
   ────────────────────────────────────────────────────────────────────────── */

/* ---- Caja de tokens: los tres paquetes, en rejilla ----
   `.nv-paq-lista` era una COLUMNA (`display:flex; flex-direction:column`) y es
   de una clase, igual que `.nvx-grid`; pero `caja_tokens.css` se enlaza
   DESPUES de `app.css` en `web/app/index.html` (linea 109 contra 98), asi que
   ganaba la columna y la rejilla no se veria. Dos clases le ganan a una venga
   la hoja de donde venga — misma leccion que la oleada 4 pago con
   `licitaciones.css`. */
.nv-paq-lista.nvx-grid { display: grid; grid-template-columns:
  repeat(auto-fill, minmax(clamp(210px, 36vw, 250px), 1fr)); }
/* `.nv-paq-card` traia `padding: 10px 12px` y `gap: 2px` de cuando ella misma
   era el boton. El relleno lo pone ahora `.nvx-head`; sumados, el titulo
   saldria a 24 px del borde. Dos clases por el mismo motivo de orden de hoja. */
.nvx-grid .nv-paq-card { padding: 0; gap: 0; }
/* El pie del paquete, pegado abajo: en una fila de tres, «45 tokens» y «240
   tokens» no ocupan lo mismo y sin esto los tres botones de comprar quedan
   cada uno a su altura. */
.nvx-grid .nvx-card > .nv-paq-foot { margin-top: auto; padding: 0 14px 14px; }
/* «Te alcanza con este» conserva su fondo savia solido con texto `#04140E` —el
   chip del patron es hueco y lo dejaria indistinguible de los demas— pero
   pierde el `align-self: flex-start` y el `margin-bottom` de cuando vivia
   suelto encima del titulo: dentro de `.nvx-metarow` esos dos descuadran la
   fila. Es el mismo arreglo que la oleada 4 le hizo a `.nv-kn-live`. */
.nvx-metarow > .nv-paq-tag { align-self: auto; margin-bottom: 0; }
/* La linea de equivalencias vive DENTRO de `.nvx-head`, que ya tiene `gap:7px`:
   su `margin-top: 2px` se sumaria. */
.nvx-head > .nv-paq-eq { margin-top: 0; }

/* ---- Servicios: la rejilla de prestadores y la columna de reservas ----
   `.nv-sv-card` traia `padding: 15px`. Y su rejilla propia (`.nv-sv-grid`,
   `minmax(240px,1fr)`) pasa a la del patron para que Servicios, Transporte,
   Academia y Marketplace tengan las celdas del MISMO ancho: cuatro rejillas
   parecidas pero distintas se notan al saltar de una pantalla a otra. */
.nvx-grid .nv-sv-card { padding: 0; }
.nv-sv-grid.nvx-grid { grid-template-columns:
  repeat(auto-fill, minmax(clamp(210px, 36vw, 250px), 1fr)); gap: clamp(10px, 2vw, 16px); }
/* El pie pasa a COLUMNA y se pega abajo. Era `space-between` en fila porque
   compartia renglon con el precio, y el precio subio a `.nvx-metarow` — que es
   la regla del artefacto. Lo que queda en el pie —el boton, el hueco de
   errores, la confirmacion y «ir a la bandeja»— se lee uno encima de otro. */
.nvx-grid .nvx-card > .nv-sv-cfoot { margin-top: auto; padding: 0 14px 14px;
  flex-direction: column; align-items: stretch; }
/* `.nv-sv-cnote` traia `margin: 10px 0` y `flex: 1` de cuando era el unico
   separador del cuerpo y ademas empujaba el pie. Dentro de `.nvx-head`, que ya
   tiene `gap: 7px`, ese margen son 17 px; y el trabajo de empujar el pie lo
   hace ahora el `margin-top: auto` de `.nv-sv-cfoot`. */
.nvx-grid .nvx-head > .nv-sv-cnote { margin: 0; flex: none; }
/* `.nv-work-card` traia `padding: 14px`. El relleno lo pone la fila
   (`.nvx-listrow`), y uno en la caja de fuera dejaria el detalle flotando
   dentro de un marco. */
.nvx-item.nv-work-card { padding: 0; }
/* `.nv-work-meta` traia `margin-bottom: 6px` de cuando era una fila mas de la
   tarjeta abierta; dentro de `.nvx-dextra`, que ya pone el relleno de abajo,
   son 6 px de mas antes del pie. */
.nvx-dextra > .nv-work-meta { margin-bottom: 0; }
/* `NVConf.acciones()` mete su fila de botones Y el selector de cinco estrellas
   dentro de un `.nv-work`; a ancho completo dentro del pie para que no se
   apeñuque contra «ir a la bandeja» a 360 px. */
.nvx-dfoot > .nv-work { width: 100%; }

/* ---- Transporte: la oferta de flete, misma rejilla que Servicios ----
   `.nv-tr-card` era una FILA (`display:flex; align-items:center; gap:12px`) con
   `padding: 13px` y `margin-bottom: 9px`. `.nvx-card` la vuelve columna, pero
   ninguno de esos tres los declara el patron: el relleno se sumaria al de
   `.nvx-head` y el margen al `gap` de la rejilla, dejando las columnas con
   distinta separacion horizontal que vertical. */
.nvx-grid .nv-tr-card { padding: 0; gap: 0; margin-bottom: 0; align-items: stretch; }
/* El pie: `.nv-tr-right` alineaba a la DERECHA (`align-items: flex-end`) porque
   era la columna derecha de una fila. Ahora es el pie de una tarjeta y va a
   ancho completo — con `flex-end`, el motivo de un bloqueo por KYC (una frase
   entera) saldria en una tira estrecha pegada al borde derecho, que es lo
   contrario de «apagado CON SU MOTIVO». */
.nvx-grid .nvx-card > .nv-tr-right { margin-top: auto; padding: 0 14px 14px;
  align-items: stretch; }
.nvx-grid .nv-tr-card .nv-tr-req { width: 100%; justify-content: center; }
/* La cobertura vive dentro de `.nvx-head`, que ya tiene su `gap`: su margen
   propio se sumaria. */
.nvx-head > .nv-tr-cov { margin: 0; }

/* ---- Unidad Operativa: la rejilla de parcelas ----
   `.nv-un-card` traia `padding: 15px` y —por ser un `<button>`— el texto
   centrado del navegador. Las dos cosas, apagadas: el relleno lo ponen ahora
   `.nvx-head` y el pie. El `:hover` con `transform: translateY(-2px)` se queda
   como esta: aqui la tarjeta NO se despliega —abre otra vista— asi que el
   saltito no pelea con ninguna animacion, al reves que en `.nvx-item`. */
.nvx-grid .nv-un-card { padding: 0; text-align: left; }
.nv-un-grid.nvx-grid { grid-template-columns:
  repeat(auto-fill, minmax(clamp(210px, 36vw, 250px), 1fr)); gap: clamp(10px, 2vw, 16px); }
/* La barra de fenologia va dentro de `.nvx-head` (que ya tiene `gap: 7px`) y el
   pie va fuera, pegado abajo: `margin-top: auto` es lo que iguala la altura del
   «dia 12» en una fila de parcelas con nombres de distinto largo. */
.nvx-grid .nvx-head > .nv-un-stage { margin: 0; }
.nvx-grid .nvx-card > .nv-un-cfoot { margin-top: auto; padding: 0 14px 14px; }

/* ---- IA Agronomica: el plan se despliega por secciones ----
   Mismo caso que las secciones de Biogas en la oleada 3: el contenido de cada
   seccion (`.nv-ag-covchips`, `.nv-ag-prio`, `.nv-ag-refs`) no trae relleno
   propio porque antes vivia dentro de un `.nv-ag-card` con `padding: 16px`.
   Ahora vive en `.nvx-dextra`, que pone el de los lados y el de abajo pero no
   el de arriba — en Industria y Ganado ese bloque va precedido de la cabecera
   de la ficha, y aqui es lo primero que hay debajo de la linea. */
.nv-ag-secciones .nvx-detail > .nvx-dextra { padding-top: 13px; }
/* La columna de secciones, separada de la tarjeta del veredicto. Ese veredicto
   se queda arriba y solo — es LA respuesta que costo 20 tokens, no una seccion
   del informe. Mismo criterio que `.nv-bg-secciones`. */
.nv-ag-secciones { margin-top: 14px; }
/* `.nv-ag-card` sigue siendo la tarjeta del veredicto y conserva su franja
   izquierda, pero pierde el `margin-bottom: 16px` que ahora duplicaria la
   separacion con la columna de secciones. OJO: se escribe con DOS clases
   porque `.nv-ag-card` la usa TAMBIEN `agro_lab.js` —con sus franjas de
   severidad `sev-medio`/`sev-alto`— y alli sigue siendo una tarjeta suelta que
   necesita su margen. */
.nv-ag-recoout > .nv-ag-card { margin-bottom: 0; }

/* ---- La portada: las dos tarjetas sueltas ----
   Las dos ARRANCAN ABIERTAS (`abierta: true` en `app.js`) y alli esta razonado
   por que. Lo que hace falta aqui es apagarles el relleno propio: las dos
   traian `padding: 14px` de cuando eran cajas muertas, y ahora el relleno lo
   ponen la fila, la cabecera y `.nvx-dextra`.
   🔴 EL BORDE DORADO DE LA BANDA DE TRANSPORTE NO SE PIERDE. Lo pone
   `.nv-tp-card` —una clase— y `.nvx-item.is-open` son DOS, asi que al abrirse
   la banda se habria quedado con el verde de «abierta» y habria perdido la
   unica marca que la hace verse antes que la fila de indicadores. Se repone
   aqui con dos clases, en los dos estados. Es EXACTAMENTE el fallo que la
   oleada 3 cazo con el filtro de H2S de Biogas. */
.nvx-item.nv-tp-card, .nvx-item.nv-int-card { padding: 0; }
.nvx-item.nv-tp-card, .nvx-item.nv-tp-card.is-open { border-color: var(--nv-dorado); }
/* `.nv-tp-cab` era una FILA de dos columnas (icono a la izquierda, texto a la
   derecha). El icono subio a la miniatura de la fila y el nodo pasa a ser el
   cuerpo entero de la ficha, asi que vuelve a ser un bloque normal. */
.nvx-dextra.nv-tp-cab { display: block; }
.nvx-dextra > .nv-tp-d { margin: 0; }
/* EL CENSO, AHORA EN LA FILA COLAPSADA. Sigue siendo `.nv-tp-censo` —la mono y
   el dorado que ya tenia, ver su regla ~1.000 lineas mas arriba— pero como
   `<p>` dentro de una fila flex hay que apagarle el margen de bloque. VACIO no
   ocupa nada: nace sin texto y solo se llena si el servidor contesta, «ni un
   cero: no se pudo preguntar no es lo mismo que no haya nadie». Mismo criterio
   que `.nv-tp-nota:empty` y `.nvx-ddesc:empty`. */
.nvx-listmeta > .nv-tp-censo { margin: 0; }
.nv-tp-censo:empty { display: none; }
.nvx-chip:empty { display: none; }

/* ---- Biofabrica: el lote se despliega ----
   Aqui NO hay nada que apagar —ninguna clase de esta tarjeta tenia una sola
   regla, ver el punto 1 de la cabecera de este bloque—, asi que lo unico que se
   escribe es lo que FALTABA: los dos rotulos de seccion, que eran `<h5>` con el
   tamaño y el margen de serie del navegador, y las filas de pasos. Los valores
   son los de `.nvx-dkv span`, que es como el patron pinta un rotulo de seccion
   en el resto de la app: no se inventa una tipografia nueva. */
.nv-bf-lsec { font-size: 10.5px; color: var(--nv-tenue); text-transform: uppercase;
  letter-spacing: .04em; margin: 0 0 6px; }
.nv-bf-lven, .nv-bf-lprox { margin-bottom: 11px; }
.nv-bf-lven:last-child, .nv-bf-lprox:last-child { margin-bottom: 0; }
.nv-bf-lvf { display: flex; align-items: center; justify-content: space-between;
  gap: 9px; flex-wrap: wrap; font-size: 12.5px; padding: 5px 0;
  border-bottom: 1px solid var(--nv-linea); }
.nv-bf-lvf:last-child { border-bottom: 0; }
.nv-bf-lpf, .nv-bf-lmas { font-size: 12.5px; color: var(--nv-tenue); margin: 0 0 4px; }
.nv-bf-lok { font-size: 12.5px; color: var(--nv-savia-hi); margin: 0; }
/* El aviso legal de vender bioinsumos, en el pie y PEGADO al boton que actua.
   Ambar y no rojo: no es una averia, es una condicion legal que hay que leer
   antes de publicar — y el rojo de esta casa significa «esto se rompio». */
.nv-bf-lav { font-size: 11.5px; color: var(--nv-dorado); line-height: 1.4; margin: 0; }

/* ============================================================================
   LAS PUERTAS — portada, bandeja de «Más» y rejilla de módulos (2026-08-25)
   ============================================================================
   Rediseño de menú aprobado por Daniel («queda aprobado el cambio de ui-ux»).
   Lo pinta `app/assets/js/app.js`: `renderHome()` (barra de asistente + seis
   puertas), `abrirMas()` (la bandeja) y `rejillaModulos()` (la rejilla que
   comparten el explorador y cada puerta). La medición que lo justifica está en
   `docs/hilos/MEDICION_menu_rediseno_2026-08-25.md`.

   ─── POR QUÉ HAY TAN POCAS CLASES NUEVAS ───────────────────────────────────
   Porque casi todo ya estaba resuelto en este archivo y se REUSA en vez de
   duplicarse:
     · La bandeja NO trae overlay propio: se monta sobre `.nv-md-velo`/`.nv-md`
       (NVModal), que ya tiene scrim, z-index, blur y el foco atrapado. Aquí
       solo se le cambia la POSICIÓN en el teléfono.
     · La rejilla ancha de módulos es `.nv-qa`/`.nv-qa-tile` de siempre, con dos
       modificadores. Esos valores estaban ESCRITOS EN LÍNEA desde JS con una
       nota que decía que esta hoja «es de todos y no se toca desde aquí» —
       cierto cuando varios agentes la editaban a la vez; hoy tiene un dueño, y
       el estilo vuelve a donde vive el estilo.
     · La insignia `.nv-pronto` copia el lenguaje de `.nv-soon` (mono, ámbar,
       píldora) sin heredarla: `.nv-soon` lleva `margin-top:8px`, que dentro de
       una fila flex la descoloca.

   ─── LA TRAMPA QUE COSTÓ EL BUG, ESCRITA PARA QUE NO VUELVA ────────────────
   🔴 UN `<button>` NO HEREDA `color` DEL BODY. Trae el color propio del
   navegador (el gris de sistema), así que un botón nuevo sin `color` explícito
   se ve legible en oscuro por casualidad y GRIS SOBRE BLANCO en tema claro.
   Todas las piezas de abajo que son botones —`.nv-assist-bar`, `.nv-h6-card`,
   `.nv-qa-lista`— declaran `color: var(--nv-texto)`, y sus hijos que deben ir
   apagados declaran el suyo en vez de confiar en una herencia que no existe.

   ─── EL COLOR DE CADA PUERTA ───────────────────────────────────────────────
   Llega como `--nv-p`, puesta EN LÍNEA por el JS desde la tabla `PUERTAS`
   (`var(--nv-puerta-mercado)` y compañía, definidas en `tokens.css`). Se hace
   así y no con trece clases porque el color es un dato del item, no una regla:
   con clases habría que tocar dos archivos para añadir una puerta, y el día que
   se olvide uno la tarjeta sale sin color y nadie lo nota hasta producción.
   El tinte se usa SOLO en el chip del ícono y en el borde al pasar por encima:
   sobre el fondo entero de la tarjeta, seis colores saturados compiten entre sí
   y ninguno significa nada. El DIBUJO del ícono no cambia — es el de
   `icons.js`, la iconografía de la casa, sin un solo SVG nuevo.
   ========================================================================== */

/* ---------- La barra del Asistente (portada, arriba) ---------------------- */
/* Alargada y NO una tarjeta más del grid, a propósito: no es una sección entre
   seis, es la forma de hablar con la app. Si fuese una baldosa igual a las
   otras se leería como «una de tantas». */
.nv-assist-bar {
  display: flex; align-items: center; gap: 12px; width: 100%;
  text-align: left; cursor: pointer; margin-bottom: 14px;
  padding: 12px 14px; border-radius: var(--nv-r-md);
  background: linear-gradient(120deg, color-mix(in srgb, var(--nv-savia) 14%, var(--nv-superficie)), var(--nv-superficie));
  border: 1px solid var(--nv-linea-fuerte);
  color: var(--nv-texto);
  transition: var(--nv-dur-fast);
}
.nv-assist-bar:hover { border-color: var(--nv-savia); }
.nv-assist-bar:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: 2px; }
/* Caja reservada para el planeta: `logo.js` monta un canvas de 44x44 y sin alto
   fijo la barra da un salto cuando termina de montarse. */
.nv-assist-ic { width: 44px; height: 44px; flex-shrink: 0; display: grid; place-items: center; }
.nv-assist-ic canvas { width: 44px; height: 44px; display: block; }
.nv-assist-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.nv-assist-txt b { font-size: 14px; font-weight: 700; color: var(--nv-texto); }
/* La segunda línea se corta a dos renglones en vez de partirse en cuatro: a
   320 px, una frase de 66 caracteres empujaría las puertas fuera de la primera
   pantalla, que es justo lo que este rediseño viene a evitar. */
.nv-assist-txt span {
  font-size: 11.5px; line-height: 1.35; color: var(--nv-tenue);
  overflow: hidden; text-overflow: ellipsis;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

/* ---------- Las seis puertas de la portada -------------------------------- */
/* COLUMNAS FIJAS —dos y tres—, NO `auto-fill`, y esto se midió antes de
   escribirlo. Con `repeat(auto-fill, minmax(150px,1fr))` el panel de un
   escritorio de 1.100 px da CUATRO columnas: seis tarjetas quedan en una fila
   de cuatro y otra de dos, con dos huecos vacíos a la derecha. Justo lo que el
   encargo prohíbe — «seis tarjetas SIMÉTRICAS». Seis solo reparte exacto en 2,
   3 y 6; de esas, 2 es la del teléfono y 3 la de todo lo demás. */
.nv-home6 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
@media (min-width: 700px) { .nv-home6 { grid-template-columns: repeat(3, 1fr); } }
.nv-h6-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 9px;
  padding: 16px 14px 15px; min-height: 150px; text-align: left; cursor: pointer;
  background: var(--nv-superficie);
  border: 1px solid var(--nv-linea); border-radius: var(--nv-r-md);
  color: var(--nv-texto);
  box-shadow: var(--nv-sh-1);
  transition: transform var(--nv-dur-fast) var(--nv-ease), border-color var(--nv-dur-fast), box-shadow var(--nv-dur-fast);
}
.nv-h6-card:hover { transform: translateY(-2px); box-shadow: var(--nv-sh-2);
  border-color: color-mix(in srgb, var(--nv-p, var(--nv-savia)) 55%, var(--nv-linea)); }
.nv-h6-card:focus-visible { outline: 2px solid var(--nv-savia); outline-offset: 2px; }
/* ---------- EL CHIP DE COLOR (corregido el 2026-08-25) --------------------- */
/* 🔴 LO QUE ESTABA MAL, MEDIDO Y NO SUPUESTO. Aquí decía «tinte, no color
   plano»: 14% del color sobre la superficie, con el ícono supuestamente «a
   plena saturación» gracias a `color:`. Esa última parte ERA FALSA. Los dibujos
   de `icons.js` NO usan `currentColor`: llevan el degradado de marca cosido al
   trazo (`stroke="url(#nvIG)"`), así que `color:` no los tocaba. Sumado a un
   fondo al 14%, el resultado real en pantalla eran TRECE TARJETAS CASI
   IGUALES, todas con el mismo dibujo verde-azulado sobre un lavado que no se
   distingue. El color que el dueño aprobó estaba en `tokens.css` y en la tabla
   `PUERTAS`, y no llegaba al píxel. No cambió de opinión: nunca lo vio.

   LA CORRECCIÓN son dos piezas y hacen falta las dos: `NVIcon.get(id, px,
   {mono:true})` —modo nuevo en `assets/js/icons.js`— devuelve el MISMO dibujo
   con `stroke="currentColor"`, y aquí el chip pasa a color pleno con el ícono
   en blanco encima. Una sin la otra no sirve: chip pleno con ícono degradado es
   un borrón, y modo mono sobre el tinte del 14% es un ícono pálido invisible.

   EL DEGRADADO DEL CHIP es del MISMO color, no de dos, y NUNCA ACLARA: arranca
   en el tono exacto del token y baja a un 22% más oscuro. Da volumen sin
   inventar un segundo color que nadie aprobó.

   🔴 POR QUÉ NO ACLARA, que se midió y era al revés en el primer intento. El
   blanco sobre cada uno de los trece tonos, calculado en el navegador: el peor
   es `--nv-puerta-energia` (#D2860F) con 2.93:1 y le sigue `--nv-puerta-precios`
   (#B98A1E) con 3.13:1 — el mínimo de la WCAG para un elemento gráfico es 3:1,
   o sea que el más claro NO llega. Un degradado que empezara MÁS CLARO que el
   token (la primera versión de esta regla) hundía ese peor caso hasta ~2.6. Los
   otros once van de 3.37 a 6.48 y no tienen problema.

   LA SOMBRA DEL DIBUJO TAMPOCO ES ADORNO: es lo que le da al trazo blanco un
   borde oscuro de 1 px sobre esos dos tonos ámbar, que es como se separa una
   figura de su fondo cuando el contraste de área no da. En los once oscuros ni
   se nota. Si algún día se corrige `--nv-puerta-energia` a un ámbar más
   profundo, esto se puede quitar; mientras tanto sostiene el caso peor.

   `place-items: center` en las tres medidas: el ícono va centrado en los dos
   ejes, que es lo que se pidió al ver el renglón pequeño. */
.nv-h6-ic {
  width: 64px; height: 64px; flex-shrink: 0;
  display: grid; place-items: center; place-content: center;
  border-radius: 18px;
  background: linear-gradient(135deg,
    var(--nv-p, var(--nv-savia)),
    color-mix(in srgb, var(--nv-p, var(--nv-savia)) 78%, #000000));
  border: 1px solid color-mix(in srgb, var(--nv-p, var(--nv-savia)) 70%, #000000);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--nv-p, var(--nv-savia)) 30%, transparent);
  color: #FFFFFF;
}
.nv-h6-ic svg { display: block; filter: drop-shadow(0 1px 1px rgba(0,0,0,.28)); }
.nv-h6-t { font-family: var(--nv-f-display); font-size: 14.5px; font-weight: 700; line-height: 1.25; color: var(--nv-texto); }
.nv-h6-sub { font-size: 11.5px; line-height: 1.35; color: var(--nv-tenue); font-weight: 400; }

/* ---------- La bandeja de «Más» ------------------------------------------- */
/* Bottom sheet en el teléfono y ventana centrada de 620 px para arriba. El
   pulgar llega abajo y no al centro: una lista de ocho destinos flotando en
   mitad de la pantalla obliga a estirar la mano en el móvil, que es donde se
   usa esto. Reusa `.nv-md-velo`/`.nv-md` enteros — aquí solo se recolocan. */
.nv-md-velo.nv-mas-velo { align-items: flex-end; padding: 0; }
.nv-mas-velo .nv-md {
  max-width: 100%; width: 100%; max-height: 88vh;
  border-radius: var(--nv-r-lg) var(--nv-r-lg) 0 0;
  border-bottom: 0;
  animation: nvUp .28s var(--nv-ease);
}
@media (min-width: 560px) {
  .nv-md-velo.nv-mas-velo { align-items: center; padding: 16px; }
  .nv-mas-velo .nv-md { width: 100%; max-width: min(620px, calc(100vw - 32px));
    max-height: calc(100vh - 32px); border-radius: var(--nv-r-md);
    border-bottom: 1px solid var(--nv-linea); }
}
/* Dos columnas fijas y no `auto-fill`: dentro de una ventana de 620 px un
   `minmax(150px,1fr)` metería tres columnas y las ocho puertas quedarían en dos
   filas de tres y una de dos — la fila coja que la portada evita. Dos y cuatro
   son los dos únicos repartos que dejan ocho exacto. */
.nv-mas-rej { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
@media (min-width: 560px) { .nv-mas-rej { grid-template-columns: repeat(4, 1fr); } }
/* Dentro de la bandeja las tarjetas son algo más bajas y el ícono baja de 56 a
   48: ya hay un título encima que dice dónde estás, así que no tienen que
   competir en jerarquía con las de la portada. */
.nv-mas-rej .nv-h6-card { min-height: 132px; padding: 13px 12px; box-shadow: none; }
/* 56 y no 48: el ícono de dentro subió a 34 px y en un chip de 48 quedaba a 7 px
   del borde, que es como se ve un dibujo «apretado». El chip crece con él. */
.nv-mas-rej .nv-h6-ic { width: 56px; height: 56px; border-radius: 16px; }
.nv-mas-rej .nv-h6-t { font-size: 13.5px; }

/* ---------- El renglón de «lo que más abres» (portada) -------------------- */
/* UNA FILA QUE SE DESLIZA, NO UNA REJILLA — y esto también se midió antes de
   escribirlo. Cuatro tarjetas dentro de `.nv-home6` (dos columnas a 360 px)
   serían DOS FILAS enteras metidas entre el Asistente y las puertas, y las seis
   puertas se irían fuera de la primera pantalla del teléfono: justo lo que este
   rediseño vino a arreglar. Como fila ocupa UN renglón a cualquier ancho — en
   el teléfono entran dos y media y el corte de la derecha es la señal de que
   hay más; de 700 px para arriba las cuatro se reparten el ancho y el scroll ni
   llega a aparecer.

   Es la MISMA `.nv-h6-card`, no una pieza nueva: quien reconoce la puerta abajo
   tiene que reconocerla aquí arriba. Solo va más baja y con el ícono más chico
   —como ya hace la bandeja de «Más»— porque es un ATAJO, no una sección: si
   midiera igual que las de abajo competiría con ellas en jerarquía y la portada
   volvería a ser una lista larga. */
.nv-rec { margin-bottom: 14px; }
/* El `.nv-fam` de la casa trae el `padding` del sidebar (12px 8px 4px), que aquí
   deja el título desalineado con las tarjetas de abajo. */
.nv-rec .nv-fam { padding: 0 0 6px; }
.nv-rec-fila {
  display: flex; gap: 10px; overflow-x: auto;
  /* Sin barra visible: es un carrusel corto de cuatro, y la barra de scroll de
     escritorio le añade 15 px de alto muerto a un renglón que existe para
     ahorrar alto. El corte de la última tarjeta ya dice que hay más. */
  scrollbar-width: none;
  /* Aire para que la sombra y el `translateY(-2px)` del hover no salgan
     recortados por el `overflow` de la propia fila; el margen negativo lo
     devuelve para que el renglón siga alineado con la rejilla de abajo. */
  padding: 2px 2px 6px; margin: -2px -2px -6px;
}
.nv-rec-fila::-webkit-scrollbar { display: none; }
.nv-rec-fila .nv-h6-card {
  flex: 0 0 138px; min-height: 0; gap: 7px; padding: 12px 11px; box-shadow: none;
}
@media (min-width: 700px) { .nv-rec-fila .nv-h6-card { flex: 1 1 0; } }
/* El renglón pide la MISMA llamada que la portada (`tarjetaPuerta(p, false)`), o
   sea un SVG de 40 px con el tamaño escrito en el propio atributo. Se baja aquí
   —y no con un tercer argumento en el JS— porque el CSS gana a los atributos de
   presentación de un SVG, y porque todo lo demás que distingue este renglón ya
   vive en este bloque: el tamaño es una decisión de ESTA fila, no de la tarjeta.
   50/30 conserva el mismo aire alrededor del dibujo que 64/40 en la portada. */
.nv-rec-fila .nv-h6-ic { width: 50px; height: 50px; border-radius: 14px; }
.nv-rec-fila .nv-h6-ic svg { width: 30px; height: 30px; }
.nv-rec-fila .nv-h6-t { font-size: 13px; }
/* SIN SUBTÍTULO, a cualquier ancho. En una baldosa de 138 px son cuatro
   renglones de letra pequeña que doblan el alto del atajo para repetir lo que
   el ícono y el título ya dicen — y quien está en este renglón es porque YA
   estuvo dentro de esa puerta: no necesita que se la expliquen otra vez. */
.nv-rec-fila .nv-h6-sub { display: none; }

/* ---------- La rejilla ancha de módulos (explorador + puertas) ------------ */
/* Baldosa ANCHA, no la de 100 px de los antiguos accesos rápidos: aquí cada
   módulo lleva su descripción, que es lo que convierte una lista en algo donde
   se ENCUENTRA. Antes vivía en `style=` desde JS (ver `rejillaModulos`). */
.nv-qa-ancha { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
.nv-qa-lista { align-items: flex-start; text-align: left; color: var(--nv-texto); }
.nv-qa-nom { font-weight: 600; line-height: 1.25; color: var(--nv-texto); }
.nv-qa-desc { font-size: 11.5px; line-height: 1.35; font-weight: 400; color: var(--nv-tenue); }

/* ---------- EL COLOR, UN NIVEL MÁS ADENTRO (2026-08-25) -------------------- */
/* Segunda tanda del encargo: «todos los íconos de todos los botones dentro de
   cada botón principal» grandes y con color. Es la MISMA pieza que el chip de
   las puertas (`.nv-h6-ic` arriba) con la misma receta —color pleno, ícono
   blanco en modo `mono`, sombra de 1 px para los dos tonos claros— solo que más
   pequeña, porque aquí la baldosa lleva además nombre y descripción.

   EL COLOR LO PONE EL JS EN LÍNEA (`--nv-p`, desde `colorDeFam()`), igual que en
   las tarjetas y por la misma razón: es un dato del item, no una regla. Trece
   clases obligarían a tocar dos archivos por cada puerta nueva.

   `.nv-qa-color` EXISTE PARA NO PINTAR EL CHIP DONDE NO HAY COLOR. Sin la
   clase, cualquier `.nv-qa-lista` sin `--nv-p` caería al respaldo `--nv-savia` y
   TODAS las baldosas del fundador —y cualquier rejilla futura sin familia—
   saldrían verdes como si pertenecieran a una puerta que no existe. */
.nv-qa-color .nv-qa-ic {
  width: 48px; height: 48px; border-radius: 14px;
  display: grid; place-items: center; place-content: center;
  background: linear-gradient(135deg,
    var(--nv-p, var(--nv-savia)),
    color-mix(in srgb, var(--nv-p, var(--nv-savia)) 78%, #000000));
  border: 1px solid color-mix(in srgb, var(--nv-p, var(--nv-savia)) 70%, #000000);
  box-shadow: 0 2px 7px color-mix(in srgb, var(--nv-p, var(--nv-savia)) 28%, transparent);
  color: #FFFFFF;
}
.nv-qa-color .nv-qa-ic svg { display: block; filter: drop-shadow(0 1px 1px rgba(0,0,0,.28)); }
/* El borde de la baldosa se tiñe al pasar por encima, como ya hacen las tarjetas
   de puerta: dice a qué familia pertenece lo que estás a punto de abrir. */
.nv-qa-color:hover { border-color: color-mix(in srgb, var(--nv-p, var(--nv-savia)) 55%, var(--nv-linea)); }

/* EL «PRÓXIMAMENTE» VA GRANDE PERO GRIS, y es deliberado. Un módulo apagado con
   el mismo chip cobre que el vivo de al lado se lee como disponible — y este
   archivo ya se comió una vez un «próximamente» que mentía (ver la cabecera del
   explorador en `app.js`). El tamaño es lo que se pidió; el color es un estado,
   y el estado no se falsea. El JS ni siquiera le pone `--nv-p` a estos. */
.nv-qa-soon .nv-qa-ic {
  width: 48px; height: 48px; border-radius: 14px;
  display: grid; place-items: center; place-content: center;
  background: var(--nv-suelo);
  border: 1px dashed var(--nv-linea-fuerte);
}
/* Apagado de verdad: el JS le pone `disabled`, así que además del gris no
   responde ni al clic ni al tabulador. Sin `pointer-events:none` a propósito —
   el cursor de «no permitido» es información, y quitarlo deja a alguien
   pulsando sin entender por qué no pasa nada. El borde a rayas dice «esto no
   está terminado» sin tener que leer la insignia. */
.nv-qa-tile:disabled, .nv-qa-soon { cursor: not-allowed; background: var(--nv-suelo); border-style: dashed; }
.nv-qa-soon .nv-qa-nom, .nv-qa-soon .nv-qa-desc { color: var(--nv-fantasma); }
.nv-qa-soon .nv-qa-ic { color: var(--nv-fantasma); opacity: .8; }
.nv-qa-tile:disabled:hover { transform: none; border-color: var(--nv-linea); background: var(--nv-suelo); }

/* La insignia de «Próximamente». Ámbar de cosecha y no rojo: no es una avería,
   es algo que todavía no está. Mismo lenguaje que `.nv-soon` pero sin su
   `margin-top`, que dentro de una fila flex la descoloca. */
.nv-pronto {
  display: inline-block; align-self: flex-start;
  font-family: var(--nv-f-mono); font-size: 9.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--nv-cosecha);
  border: 1px solid color-mix(in srgb, var(--nv-cosecha) 40%, transparent);
  border-radius: var(--nv-r-pill); padding: 2px 7px;
}
/* En el sidebar el renglón apagado se atenúa entero y la insignia se encoge: un
   menú lateral no tiene sitio para una píldora de tamaño normal. */
.nv-nav-i.nv-nav-soon { cursor: not-allowed; color: var(--nv-fantasma); }
.nv-nav-i.nv-nav-soon:hover { background: none; color: var(--nv-fantasma); }
.nv-nav-i .nv-pronto { margin-left: auto; font-size: 8.5px; padding: 1px 5px; }

/* El glifo de respaldo cuando un módulo no tiene dibujo en `icons.js` (hoy:
   `fincas`, `plan`, `biogas`, `industria`, `recetario` y las cuatro energías
   apagadas). `pintarIcono()` le pone el tamaño en línea porque solo él sabe
   cuántos píxeles pidió el llamador; aquí va lo que no depende del tamaño.
   `line-height:1` es lo que impide que el glifo se vaya al borde superior de su
   caja: los caracteres tipográficos traen su propio interlineado y sin esto
   quedan descolgados dentro de un `grid` centrado. */
.nv-ic-glifo { line-height: 1; display: grid; place-items: center; font-weight: 400; }
