/* Mobile primero: la mayoría de los presupuestos se arman desde el celular,
   en el depósito o en lo del cliente. El escritorio es la excepción, no al
   revés.

   Los colores y las tipografías salen del manual de marca de HOMIQ
   (source/marca/MARCA.md). No inventar valores nuevos acá: si hace falta un
   tono que no está en la paleta, derivarlo con color-mix() del que
   corresponda, así sigue siendo el mismo azul. */

/* Open Sans es la tipografía secundaria de la marca y acá hace el trabajo
   pesado: es la que se lee mejor en pantallas chicas. Se sirve desde la
   propia app y no desde Google: la app tiene que funcionar sin internet, y
   además no hay por qué contarle a Google quién abre un presupuesto. */
@font-face {
  font-family: 'Open Sans'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('/fuentes/OpenSans-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Open Sans'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('/fuentes/OpenSans-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Open Sans'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('/fuentes/OpenSans-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Open Sans'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('/fuentes/OpenSans-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Open Sans'; font-style: normal; font-weight: 700;
  font-display: swap; src: url('/fuentes/OpenSans-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Open Sans'; font-style: normal; font-weight: 700;
  font-display: swap; src: url('/fuentes/OpenSans-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* MAAX es la tipografía principal de la marca, pero es comercial y no se
   puede redistribuir. Los .ttf están en el Drive de HOMIQ; ver
   source/marca/fuentes/LEEME.md para instalarlos. Mientras no estén, los
   títulos caen a Open Sans 700, que sigue siendo tipografía de marca — se
   nota menos de lo que parece. */
@font-face {
  font-family: 'Maax'; font-style: normal; font-weight: 500;
  font-display: swap; src: url('/fuentes/Maax-Medium.ttf') format('truetype');
}
@font-face {
  font-family: 'Maax'; font-style: normal; font-weight: 700;
  font-display: swap; src: url('/fuentes/Maax-Bold.ttf') format('truetype');
}

:root {
  /* --- Paleta institucional HOMIQ --- */
  --blanco:        #FCFCFC;  /* White       */
  --azul-hielo:    #DFE6EC;  /* Alice Blue  */
  --azure:         #007AFF;  /* Azure       */
  --gris-cadete:   #95A4B9;  /* Cadet Gray  */
  --glauco:        #5F7696;  /* Glaucous    */
  --negro-pasa:    #1F212D;  /* Raisin Black*/

  /* --- Roles --- */
  --fondo:   #F2F5F8;
  --panel:   var(--blanco);
  --panel-2: #F6F8FA;
  --tinta:   var(--negro-pasa);
  --suave:   var(--glauco);
  --tenue:   var(--gris-cadete);
  --linea:   var(--azul-hielo);
  --acento:  var(--azure);
  --acento-tinta: #FFFFFF;

  /* Semánticos: son otra familia que el acento a propósito. Si "aceptado"
     fuera azul como el botón principal, el estado dejaría de leerse de un
     golpe de vista. Van desaturados para convivir con el azul de marca. */
  --ok:          #0F7B52;
  --ok-fondo:    #E4F2EB;
  --alerta:      #8A5200;
  --alerta-fondo:#FBF0DF;
  --error:       #AE2B25;
  --error-fondo: #FBE9E7;

  --sombra: 0 1px 2px rgba(31,33,45,.05), 0 6px 20px rgba(31,33,45,.07);
  --radio: 12px;
  --barra-abajo: 62px;

  /* --- Tipografía --- */
  --fuente-titulos: 'Maax', 'Open Sans', -apple-system, BlinkMacSystemFont,
                    'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --fuente-texto:   'Open Sans', -apple-system, BlinkMacSystemFont,
                    'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --fuente-datos:   ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* El tema oscuro invierte los roles sin salirse de la paleta: el fondo pasa a
   ser el Raisin Black de la marca. El Azure puro pierde contraste sobre
   oscuro, así que se aclara — es el mismo tono, más luminoso. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --fondo:   #14161F;
    --panel:   var(--negro-pasa);
    --panel-2: #272A38;
    --tinta:   #E8EDF3;
    --suave:   var(--gris-cadete);
    --tenue:   #7C8AA0;
    --linea:   #333749;
    --acento:  #4D9BFF;
    --acento-tinta: #0A1626;

    --ok:          #5FD39B;
    --ok-fondo:    #10291E;
    --alerta:      #F0B876;
    --alerta-fondo:#2B2113;
    --error:       #FF9A91;
    --error-fondo: #2E1816;

    --sombra: 0 1px 2px rgba(0,0,0,.45), 0 6px 20px rgba(0,0,0,.35);
  }
}

:root[data-theme="dark"] {
  --fondo:   #14161F;
  --panel:   var(--negro-pasa);
  --panel-2: #272A38;
  --tinta:   #E8EDF3;
  --suave:   var(--gris-cadete);
  --tenue:   #7C8AA0;
  --linea:   #333749;
  --acento:  #4D9BFF;
  --acento-tinta: #0A1626;

  --ok:          #5FD39B;
  --ok-fondo:    #10291E;
  --alerta:      #F0B876;
  --alerta-fondo:#2B2113;
  --error:       #FF9A91;
  --error-fondo: #2E1816;

  --sombra: 0 1px 2px rgba(0,0,0,.45), 0 6px 20px rgba(0,0,0,.35);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* La barra de scroll ocupa lugar SIEMPRE, aparezca o no.

   Sin esto, filtrar una lista larga a tres resultados la hace desaparecer, y
   como el contenido va centrado con `margin: 0 auto` todo se corre unos
   píxeles hacia la derecha. Se ve como si la pantalla se reacomodara sola
   mientras escribís. */
html { scrollbar-gutter: stable; }

body {
  margin: 0;
  font: 15px/1.55 var(--fuente-texto);
  background: var(--fondo);
  color: var(--tinta);
  -webkit-font-smoothing: antialiased;
  /* El área segura es por el notch y la barra de gestos del celular. */
  padding-bottom: calc(var(--barra-abajo) + env(safe-area-inset-bottom));
}

h1, h2, h3 {
  margin: 0; font-family: var(--fuente-titulos); font-weight: 700;
  letter-spacing: -.018em; text-wrap: balance;
}
h1 { font-size: 22px; }
h2 { font-size: 17px; }
h3 { font-size: 15px; }
a { color: var(--acento); }

.oculto { display: none !important; }

/* --- Estructura --- */

.encabezado {
  position: sticky; top: 0; z-index: 30;
  background: var(--panel); border-bottom: 1px solid var(--linea);
  /* Sin padding horizontal: ese lo pone el interior, para que coincida con
     el de <main> y las dos columnas queden alineadas al píxel. */
  padding: 12px 0; padding-top: calc(12px + env(safe-area-inset-top));
}
/* La banda de fondo ocupa todo el ancho, pero el título se alinea con la
   columna de contenido. Sin esto, en escritorio el título quedaba pegado a la
   izquierda mientras las tarjetas estaban centradas y nada coincidía.

   El max-width y el padding son LOS MISMOS que los de <main>. Si se cambia
   uno, hay que cambiar los dos o se vuelven a desalinear. */
.encabezado-interior {
  display: flex; align-items: center; gap: 12px;
  width: 100%; max-width: 900px; margin: 0 auto; padding: 0 16px;
}
.encabezado .titulo { flex: 1; min-width: 0; }
.encabezado .titulo h1 { font-size: 18px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.encabezado .titulo .sub { font-size: 12.5px; color: var(--tenue); }

/* El `width: 100%` no es decorativo y NO se puede sacar.

   `margin: 0 auto` centra, pero en un contenedor grid o flex los márgenes
   automáticos **desactivan el estiramiento** del item: sin un ancho
   explícito, `main` se dimensiona según su contenido (`fit-content`) con
   techo de 900px. Con 285 filas de nombres largos llegaba al techo; al
   filtrar a 7 filas cortas se encogía a ~340px y toda la pantalla se
   reacomodaba mientras se escribía.

   Con `width: 100%` llena la columna, el techo lo pone `max-width` y los
   márgenes sólo centran. El ancho deja de depender de cuántos resultados
   haya. */
main { padding: 16px; width: 100%; max-width: 900px; margin: 0 auto; }

/* Una zona de resultados que no cambia de alto al filtrar.

   Sin el mínimo, pasar de 285 filas a 3 acorta la página de golpe: el scroll
   salta y da la sensación de que todo se encogió. El espacio vacío de abajo
   es mejor que ese salto. */
.zona-resultados { min-height: 55vh; }

.barra-nav {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  display: flex; background: var(--panel);
  border-top: 1px solid var(--linea);
  padding-bottom: env(safe-area-inset-bottom);
}
.barra-nav button {
  flex: 1; border: 0; background: none; color: var(--tenue);
  font: inherit; font-size: 11px; font-weight: 600;
  padding: 8px 2px 10px; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
/* Iconos de la barra: SVG, no caracteres.
   Los glifos Unicode que había (☺ para Clientes, ⚙ para Cuenta) iOS los
   dibujaba como EMOJI —amarillos y redondos— en medio de una barra sobria, y
   eso no se puede evitar desde CSS: gana la fuente de emoji del sistema.
   Con `stroke: currentColor` el icono toma el color del estado del botón,
   incluido el azul del que está activo, sin una regla más. */
.barra-nav button .icono { display: flex; line-height: 0; }
.barra-nav button .icono svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.barra-nav button[aria-current="page"] { color: var(--acento); }
/* Con siete botones (el séptimo es Cobros) las etiquetas no entran en un
   teléfono angosto: "Presupuestos" a 11px mide más que su sexto de pantalla.
   `min-width: 0` deja que el botón se encoja —sin eso flex usa el ancho del
   contenido como piso— y la elipsis corta en vez de desbordar la barra. */
.barra-nav button { min-width: 0; }
.barra-nav button { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 420px) {
  .barra-nav button { font-size: 10px; letter-spacing: -0.2px; padding-inline: 1px; }
}

@media (min-width: 860px) {
  body { padding-bottom: 0; display: grid; grid-template-columns: 208px 1fr;
         min-height: 100vh; }
  .encabezado { grid-column: 2; }
  /* El padding del encabezado acompaña al de main: los dos son cajas de
     900px con el mismo padding, así el título cae sobre el borde de las
     tarjetas. */
  .encabezado-interior { padding: 0 24px; }
  main { grid-column: 2; padding: 24px; }
  .barra-nav {
    position: sticky; top: 0; grid-row: 1 / 3; grid-column: 1;
    flex-direction: column; height: 100vh; align-self: start;
    border-top: 0; border-right: 1px solid var(--linea); padding: 16px 10px;
    gap: 2px;
  }
  .barra-nav button { flex: 0 0 auto; flex-direction: row; justify-content: flex-start;
    gap: 10px; font-size: 14px; padding: 10px 12px; border-radius: 9px; }
  .barra-nav button .icono svg { width: 19px; height: 19px; }
  .barra-nav button[aria-current="page"] { background: var(--panel-2); }
  .barra-nav .marca { padding: 4px 12px 16px; font-weight: 700; font-size: 16px; }
}
/* --- Mobile: la barra de abajo es parte del layout, no flota ---------------

   Con `position: fixed; bottom: 0` iOS despega la barra durante el scroll: se
   queda a media pantalla con contenido abajo. Pasa igual en la PWA, y no se
   arregla con trucos de repintado.

   Acá la barra deja de flotar y pasa a ser el último hijo de un body de
   altura fija: `main` scrollea adentro y la barra, simplemente, no se puede
   mover. `dvh` es lo que sigue a la barra de Safari cuando aparece y
   desaparece; `vh` primero, de respaldo para navegadores viejos. */
@media (max-width: 859px) {
  .barra-nav .marca { display: none; }

  body {
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding-bottom: 0;
  }

  /* El DOM trae la barra primero (por accesibilidad y por el escritorio);
     en el celular va abajo. */
  .encabezado { order: 1; flex: 0 0 auto; position: static; }
  main {
    order: 2;
    flex: 1 1 auto;
    min-height: 0;              /* sin esto un hijo alto no deja encoger el flex */
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-y: contain;   /* corta el rebote que arrastra la página */
    /* En el celular el que scrollea es `main`, así que el hueco de la barra
       va acá. En iOS la barra es flotante y no ocupa lugar, pero en Android
       sí, y ahí filtrar también corría el contenido. */
    scrollbar-gutter: stable;
  }
  .barra-nav { order: 3; position: static; flex: 0 0 auto; }

  .pantalla-login { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
}

/* El login es su propia pantalla completa y no convive con la barra. */
.pantalla-login {
  display: grid; place-items: center; min-height: 100vh; padding: 20px;
}

/* El logo se pinta con una máscara y no con <img>: así toma el color del
   tema —tinta en claro, casi blanco en oscuro— con un solo archivo. El SVG
   de marca es monocromo, que es justo lo que esta técnica necesita. */
.marca, .marca-login {
  background-color: currentColor;
  -webkit-mask: var(--logo) no-repeat left center / contain;
  mask: var(--logo) no-repeat left center / contain;
}
.barra-nav .marca {
  --logo: url('/marca/homiq-logotipo.svg');
  height: 30px; margin: 4px 12px 20px; color: var(--tinta);
}
.marca-login {
  --logo: url('/marca/homiq-logotipo.svg');
  height: 38px; width: 130px; margin: 0 0 20px; color: var(--tinta);
}
.titulo-login { margin-bottom: 4px; }
.sub-login { color: var(--tenue); font-size: 13px; margin: 0 0 18px; }

/* El monograma, para donde no entra el logotipo. */
.isotipo {
  display: inline-block; width: 1em; height: 1.55em; vertical-align: -.35em;
  background-color: currentColor;
  -webkit-mask: url('/marca/homiq-isotipo.svg') no-repeat center / contain;
  mask: url('/marca/homiq-isotipo.svg') no-repeat center / contain;
}

/* --- Bloques --- */

.tarjeta {
  background: var(--panel); border: 1px solid var(--linea);
  border-radius: var(--radio); padding: 16px; box-shadow: var(--sombra);
  margin-bottom: 14px;
}
.tarjeta > h2, .tarjeta > h3 { margin-bottom: 10px; }

/* Dos tarjetas lado a lado en pantalla ancha, apiladas en el celular.
   `align-items: start` para que cada una conserve su alto: estiradas al alto
   de la más grande quedaría media tarjeta vacía. El margen de las tarjetas se
   anula porque acá la separación la pone el `gap`. */
.dos-columnas { display: grid; gap: 14px; align-items: start; margin-bottom: 14px; }
.dos-columnas > .tarjeta { margin-bottom: 0; }
@media (min-width: 860px) { .dos-columnas { grid-template-columns: 1fr 1fr; } }
.rotulo { font-family: var(--fuente-titulos); font-size: 11px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--tenue);
  margin-bottom: 6px; }

.fila { display: flex; gap: 10px; align-items: center; }
.fila.entre { justify-content: space-between; }
.rejilla { display: grid; gap: 10px; }
@media (min-width: 620px) { .rejilla.dos { grid-template-columns: 1fr 1fr; } }

/* Cifras sueltas que se acomodan solas: tantas por fila como quepan.
   Con `dos` fijo, tres cifras dejaban la mitad de una fila vacía y la
   tarjeta quedaba más alta de lo necesario — se veía en el bloque de Ventas,
   con «Devoluciones» solo abajo y un hueco al lado. `auto-fit` las mete en
   una fila en el escritorio y en dos en el celular, sin media queries. */
.cifras { display: grid; gap: 10px 18px;
  grid-template-columns: repeat(auto-fit, minmax(135px, 1fr)); }

/* --- Formularios --- */

label { display: block; font-size: 12.5px; font-weight: 600;
  color: var(--suave); margin-bottom: 4px; }

input, select, textarea, button {
  font: inherit; color: var(--tinta);
}
input, select, textarea {
  width: 100%; background: var(--panel-2);
  border: 1px solid var(--linea); border-radius: 9px;
  padding: 11px 12px;
  /* 16px mínimo: abajo de eso Safari hace zoom al enfocar el campo. */
  font-size: 16px;
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--acento); outline-offset: -1px; border-color: var(--acento);
}
textarea { min-height: 78px; resize: vertical; }
input[type="number"] { text-align: right; font-variant-numeric: tabular-nums; }
.campo { margin-bottom: 12px; }

button {
  cursor: pointer; border-radius: 9px; border: 1px solid var(--linea);
  background: var(--panel); padding: 11px 16px; font-weight: 600;
}
button:disabled { opacity: .5; cursor: not-allowed; }
/* Un enlace que se ve como botón. Existe para "Abrir" en Cobros: tiene que
   ser un <a> de verdad y no un button con window.open, porque en el celular
   mantener apretado un enlace da "abrir en pestaña nueva" y "copiar
   dirección", y un botón no. Todos los estilos de botón cuelgan del selector
   de elemento `button`, así que un <a> no los hereda. */
a.boton {
  display: inline-block; text-decoration: none; color: inherit;
  cursor: pointer; border-radius: 9px; border: 1px solid var(--linea);
  background: var(--panel); padding: 11px 16px; font-weight: 600;
}
a.boton.chico { padding: 7px 11px; font-size: 13px; }
button.principal { background: var(--acento); border-color: var(--acento);
  color: var(--acento-tinta); }
button.grande { width: 100%; padding: 15px; font-size: 16px; }
button.chico { padding: 7px 11px; font-size: 13px; }
button.fantasma { background: none; border-color: transparent; color: var(--acento); }
/* Acción importante pero NO la principal de la pantalla.
   Una pantalla tiene un solo botón azul lleno. En «nuevo presupuesto» ese
   lugar es de «Guardar»: si «Agregar producto» también fuera azul lleno, los
   dos compiten y encima quedan uno sobre otro cuando la barra de guardar se
   pega al fondo. */
button.secundario { background: none; color: var(--acento);
  border-color: color-mix(in srgb, var(--acento) 45%, transparent); }
button.secundario:hover { background: color-mix(in srgb, var(--acento) 8%, transparent); }
button.peligro { color: var(--error); }

/* --- Listas de resultados --- */

.lista { border: 1px solid var(--linea); border-radius: var(--radio);
  overflow: hidden; background: var(--panel); }
.lista .item {
  display: flex; gap: 12px; align-items: center; width: 100%;
  padding: 12px 14px; border: 0; border-bottom: 1px solid var(--linea);
  background: none; text-align: left; border-radius: 0; font-weight: 400;
}
.lista .item:last-child { border-bottom: 0; }
.lista .item:hover { background: var(--panel-2); }
.lista .item .cuerpo { flex: 1; min-width: 0; }
.lista .item .principal-txt { font-weight: 600; }
.lista .item .sub { font-size: 12.5px; color: var(--tenue);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lista .item .derecha { text-align: right; white-space: nowrap;
  font-variant-numeric: tabular-nums; }

.miniatura {
  width: 46px; height: 46px; border-radius: 8px; object-fit: cover;
  background: var(--panel-2); border: 1px solid var(--linea); flex: none;
}
.miniatura.vacia { display: grid; place-items: center; color: var(--tenue);
  font-size: 11px; }

.vacio { text-align: center; color: var(--tenue); padding: 32px 16px; }

/* --- Chips de estado --- */

.chip { display: inline-block; font-size: 11.5px; font-weight: 700;
  padding: 3px 9px; border-radius: 99px; background: var(--panel-2);
  color: var(--suave); }
.chip.borrador  { background: var(--panel-2); color: var(--tenue); }
.chip.enviado   { background: #e7effd; color: #14509e; }
.chip.aceptado  { background: var(--ok-fondo); color: var(--ok); }
.chip.rechazado { background: var(--error-fondo); color: var(--error); }
.chip.vencido   { background: var(--alerta-fondo); color: var(--alerta); }
@media (prefers-color-scheme: dark) { .chip.enviado { background: #16243d; color: #8ab4ff; } }

/* Estados de stock. "Sin dato" es gris a propósito: no es lo mismo que no
   haya stock que que no sepamos cuánto hay, y el color no los tiene que
   confundir. */
.chip.st-ok        { background: var(--ok-fondo);     color: var(--ok); }
.chip.st-bajo      { background: var(--alerta-fondo); color: var(--alerta); }
.chip.st-sin_stock { background: var(--error-fondo);  color: var(--error); }
.chip.st-ambiguo   { background: var(--alerta-fondo); color: var(--alerta); }
.chip.st-sin_dato  { background: var(--panel-2);      color: var(--tenue); }

/* Números de stock en la tabla: tabulares para que las columnas se alineen. */
td.num { text-align: right; font-variant-numeric: tabular-nums;
  font-family: var(--fuente-datos); }

/* --- Avisos --- */

.aviso { border-radius: 10px; padding: 11px 13px; font-size: 13.5px;
  margin-bottom: 12px; border: 1px solid transparent; }
.aviso.error { background: var(--error-fondo); color: var(--error);
  border-color: color-mix(in srgb, var(--error) 25%, transparent); }
.aviso.ok { background: var(--ok-fondo); color: var(--ok);
  border-color: color-mix(in srgb, var(--ok) 25%, transparent); }
.aviso.nota { background: var(--alerta-fondo); color: var(--alerta);
  border-color: color-mix(in srgb, var(--alerta) 25%, transparent); }

/* --- Ítems del presupuesto --- */

.item-presu {
  border: 1px solid var(--linea); border-radius: 10px; padding: 12px;
  margin-bottom: 10px; background: var(--panel);
}
.item-presu .cabecera { display: flex; gap: 10px; align-items: flex-start;
  margin-bottom: 10px; }
.item-presu .cabecera .cuerpo { flex: 1; min-width: 0; }
.item-presu .sku { font-family: var(--fuente-datos);
  font-size: 11.5px; color: var(--tenue); }
.item-presu .controles { display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
  gap: 8px; align-items: end; }
.item-presu .controles select { padding: 11px 8px; }
.item-presu .controles label { font-size: 11px; }
.item-presu .importe { font-weight: 700; font-variant-numeric: tabular-nums;
  text-align: right; white-space: nowrap; padding-bottom: 11px; }
@media (max-width: 760px) {
  .item-presu .controles { grid-template-columns: 1fr 1fr; }
  .item-presu .importe { grid-column: 1 / -1; text-align: right; padding: 0;
    border-top: 1px solid var(--linea); padding-top: 9px; margin-top: 2px; }
}

/* --- Totales --- */

.totales { display: grid; gap: 6px; font-variant-numeric: tabular-nums; }
.totales .linea.final { font-family: var(--fuente-titulos); }
.totales .linea { display: flex; justify-content: space-between; gap: 16px;
  color: var(--suave); }
.totales .linea.final { border-top: 2px solid var(--tinta); margin-top: 6px;
  padding-top: 10px; color: var(--tinta); font-size: 21px; font-weight: 700; }
.totales .ref { font-size: 12.5px; color: var(--tenue); }
/* El descuento en el azul de marca: es lo que el cliente busca en el total. */
.totales .linea.descuento { color: var(--acento); font-weight: 600; }

/* Por defecto va en el flujo, al final del formulario. Se pega al fondo sólo
   con la clase `pegada`, que la pone el formulario cuando hay ítems: con el
   presupuesto vacío no hay nada que guardar y flotar ahí sólo tapaba
   «Forma de pago». */
.barra-accion {
  position: static;
  background: var(--panel); border: 1px solid var(--linea);
  border-radius: var(--radio); padding: 12px; box-shadow: var(--sombra);
  display: flex; gap: 10px; z-index: 20;
  /* Aire arriba y abajo: al estar pegada al fondo flota sobre la tarjeta que
     le queda detrás, y sin separación parecía encimada. La sombra sola no
     alcanzaba porque el contenido de atrás llegaba hasta el borde. */
  margin-top: 18px;
}
.barra-accion.pegada {
  position: sticky;
  bottom: calc(var(--barra-abajo) + env(safe-area-inset-bottom));
}
/* Y espacio al final del formulario para que la última tarjeta pueda
   scrollear por encima de la barra en vez de quedar tapada. */
.barra-accion + * { margin-top: 18px; }
@media (min-width: 860px) {
  .barra-accion.pegada { bottom: 16px; }
}

/* --- Gráficos -------------------------------------------------------------

   Sin librería: barras con CSS. Son responsivas sin calcular un viewBox, que
   en el celular es donde se rompen los SVG con ancho fijo.

   Los colores salen de la paleta validada con el script de la guía de
   visualización (checks de banda de luminosidad, piso de croma, separación
   para daltonismo y contraste contra el panel). NO cambiarlos a ojo: el aqua
   más oscuro que probé rompió la separación protan. Los tres de abajo pasan
   todos los checks en claro y en oscuro. */
.viz {
  --s1: #007AFF;   /* azure de marca */
  --s2: #EB6834;   /* naranja */
  --s3: #1BAF7A;   /* aqua */
  --pista: color-mix(in srgb, var(--linea) 70%, transparent);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .viz { --s1: #3987E5; --s2: #D95926; --s3: #199E70; }
}
:root[data-theme="dark"] .viz { --s1: #3987E5; --s2: #D95926; --s3: #199E70; }

/* Columnas: mes a mes, día de la semana. */
.g-cols { display: flex; align-items: flex-end; gap: 3px; height: 150px;
  border-bottom: 1px solid var(--linea); padding-bottom: 0; }
.g-col { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
  justify-content: flex-end; height: 100%; background: none; border: 0;
  padding: 0; cursor: pointer; }
/* Extremo redondeado de 4px y anclado a la base, como pide la guía. */
.g-col i { display: block; background: var(--s1); border-radius: 4px 4px 0 0;
  min-height: 2px; transition: background .12s; }
/* El importe encima de cada columna. Va en millones y sin unidad —la aclara
   el rótulo— porque con trece columnas en un teléfono "$ 87,9 M" no entra y
   "88" sí. */
.g-cima { display: block; font-size: 9px; line-height: 1.2; color: var(--tenue);
  font-variant-numeric: tabular-nums; margin-bottom: 3px;
  overflow: hidden; white-space: nowrap; }
.g-col[aria-pressed="true"] .g-cima { color: var(--tinta); font-weight: 700; }
.g-col[aria-pressed="true"] i { background: var(--tinta); }
.g-cols-pies { display: flex; gap: 3px; margin-top: 5px; }
.g-cols-pies span { flex: 1 1 0; min-width: 0; text-align: center;
  font-size: 9.5px; color: var(--tenue); overflow: hidden; }

/* Barras horizontales: cómo pagan, de dónde vienen, top clientes. */
.g-filas { display: grid; gap: 9px; }
.g-fila { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  align-items: baseline; }
.g-fila .et { font-size: 12.5px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.g-fila .va { font-size: 12.5px; font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.g-fila .pista { grid-column: 1 / -1; height: 7px; background: var(--pista);
  border-radius: 4px; overflow: hidden; }
.g-fila .pista i { display: block; height: 100%; background: var(--s1);
  border-radius: 4px; }

/* Barra apilada: el reparto entre facturado, sin factura y devoluciones.
   El hueco de 2px entre segmentos es de la guía: sin él, dos fills contiguos
   se leen como uno. */
.g-apilada { display: flex; height: 13px; border-radius: 4px; overflow: hidden;
  gap: 2px; background: var(--pista); }
.g-apilada i { display: block; height: 100%; min-width: 3px; }
.g-leyenda { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 10px;
  font-size: 12px; }
.g-leyenda span { display: inline-flex; align-items: center; gap: 6px; }
.g-leyenda b { width: 9px; height: 9px; border-radius: 3px; flex: none; }

/* --- Modal --- */

.modal-fondo {
  position: fixed; inset: 0; background: rgba(8,10,14,.5); z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0; backdrop-filter: blur(2px);
}
.modal {
  background: var(--panel); width: 100%; max-width: 560px;
  border-radius: 16px 16px 0 0; padding: 18px;
  /* dvh por lo mismo que la barra de abajo: con la barra de Safari a la
     vista, 92vh se sale de la pantalla. */
  max-height: 92vh;
  max-height: 92dvh;
  overflow-y: auto;
  padding-bottom: calc(18px + env(safe-area-inset-bottom));
}
@media (min-width: 620px) {
  .modal-fondo { align-items: center; padding: 20px; }
  .modal { border-radius: var(--radio); }
}
.modal .cabecera-modal { display: flex; justify-content: space-between;
  align-items: center; gap: 12px; margin-bottom: 14px; }

/* --- Tabla de mapeo de columnas --- */

.tabla-envoltorio { overflow-x: auto; -webkit-overflow-scrolling: touch;
  border: 1px solid var(--linea); border-radius: 10px; }
table.datos { border-collapse: collapse; width: 100%; font-size: 13px; }
table.datos th, table.datos td { padding: 7px 10px; text-align: left;
  border-bottom: 1px solid var(--linea); white-space: nowrap; }
table.datos th { background: var(--panel-2); font-size: 11px;
  text-transform: uppercase; letter-spacing: .06em; color: var(--tenue); }
table.datos tr:last-child td { border-bottom: 0; }

.cargando { display: inline-block; width: 15px; height: 15px;
  border: 2px solid var(--linea); border-top-color: var(--acento);
  border-radius: 50%; animation: girar .7s linear infinite; }
@keyframes girar { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .cargando { animation-duration: 2s; } }

/* Barra de avance de la descarga de fotos. */
.barra-progreso {
  height: 8px; border-radius: 99px; background: var(--panel-2);
  overflow: hidden; border: 1px solid var(--linea);
}
.barra-progreso .relleno {
  height: 100%; background: var(--acento); border-radius: 99px;
  transition: width .4s ease;
}
@media (prefers-reduced-motion: reduce) { .barra-progreso .relleno { transition: none; } }
