* { -webkit-font-smoothing: antialiased; }
input:focus { outline: none; }
/* Aparición suave de tarjetas al cargar la página.

   SOLO opacidad, nunca `transform`. Con `fill-mode: both` el elemento CONSERVA
   la transformación para siempre, y eso hacía dos destrozos a la vez:

     - las tarjetas se quedaban 12 px por debajo de su sitio, de forma
       permanente y en todas las cargas del Panel;
     - un ancestro con transform convierte en `absolute` todo `position: fixed`
       que tenga dentro, así que el botón flotante dejaba de anclarse a la
       pantalla en las pantallas afectadas.

   Es el mismo fallo que ya hubo en la animación de entrada de página. Si algún
   día se quiere recuperar el deslizamiento, tiene que ir en un elemento que no
   envuelva nada fijo Y sin fill-mode. */
@keyframes fadeUp {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* ⚠️ 220 ms, no 500. Medido: con medio segundo y ocho tarjetas escalonadas
   hasta 480 ms de retardo, la última terminaba de aparecer 980 ms DESPUÉS de
   que llegara el HTML -- para una pantalla que el servidor genera en 250. Y se
   repite en cada navegación, porque htmx vuelve a montar el contenido.
   La app no iba lenta: estaba animada despacio. Lo que se percibe como
   "rápido" no es el tiempo de servidor, es cuánto tarda la pantalla en
   quedarse quieta. */
.animate-in {
  animation: fadeUp 0.22s ease both;
}

/* Pequeño "lift" al pasar el ratón por una tarjeta */
.card-hover {
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card-hover:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 28px -10px rgba(11, 34, 56, 0.25);
}

/* Transición de opacidad para el carrusel de bancos */
#bankName, #bankAlias, #bankBalance {
  transition: opacity 0.25s ease;
}
.bank-fading { opacity: 0; }

.sidebar-scroll {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.sidebar-scroll::-webkit-scrollbar {
  display: none;
}
/* ============================ HOJAS INFERIORES ============================
   En móvil, los formularios de "añadir" dejan de ocupar sitio en la pantalla:
   viven en una hoja que sube desde abajo al pulsar el botón flotante. Un
   formulario de cinco campos siempre a la vista es justo lo que hace que una
   pantalla se lea como un formulario web y no como una app.

   En escritorio no existe ni la hoja ni el botón: el mismo marcado se pinta
   como el panel de siempre, en su sitio dentro de la página. Un solo bloque
   de HTML para las dos cosas, para que no haya dos versiones que mantener. */
@media (max-width: 767px) {
  .hoja {
    position: fixed;
    inset: 0;
    z-index: 60;
    visibility: hidden;
    /* El retraso es lo que hace que la bajada se VEA.
       `visibility` cambia de golpe, así que al cerrar la hoja desaparecía en el
       mismo instante y su animación de salida no llegaba a dibujarse nunca.
       Aquí se espera a que termine de bajar y entonces se oculta. Al abrir no
       hay espera: tiene que estar visible antes de empezar a subir. */
    transition: visibility 0s linear .26s;
  }
  .hoja.abierta {
    visibility: visible;
    transition-delay: 0s;
  }

  .hoja-fondo {
    position: absolute;
    inset: 0;
    background: rgba(2, 6, 12, 0.55);
    opacity: 0;
    transition: opacity .25s ease;
  }
  .hoja.abierta .hoja-fondo { opacity: 1; }

  .hoja-panel {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    /* 88dvh y no 88vh: con el teclado abierto, vh sigue midiendo la pantalla
       entera y la hoja se sale por debajo. */
    max-height: 88dvh;
    overflow-y: auto;
    border-radius: 20px 20px 0 0;
    border-bottom: 0;
    transform: translateY(100%);
    transition: transform .28s cubic-bezier(.2, .8, .25, 1);
    padding-bottom: calc(env(safe-area-inset-bottom) + 1rem);
  }
  .hoja.abierta .hoja-panel { transform: none; }

  /* La barrita de arriba: además de decir "esto se arrastra", es un objetivo
     táctil de 44 px para cerrar. */
  .hoja-agarre {
    display: block;
    width: 100%;
    padding: 10px 0 14px;
    background: none;
    border: 0;
  }
  .hoja-agarre::before {
    content: "";
    display: block;
    width: 40px;
    height: 4px;
    margin: 0 auto;
    border-radius: 999px;
    background: rgb(var(--c-line));
  }

  /* Por encima de la barra inferior (unos 64 px) y de la zona segura. */
  /* El botón se apoya en la barra inferior REAL, no en 5rem a ojo.
     La barra se mide sola al cargar (--alto-barra) porque su altura cambia con
     el móvil y con la franja de gestos; con la medida a mano el botón quedaba
     unas veces pegado a la barra y otras flotando por encima. */
  .hoja-disparador {
    bottom: calc(var(--alto-barra, 5rem) + 0.75rem);
    background: #fff;
    color: rgb(var(--c-primary));
    box-shadow: 0 2px 10px rgb(0 0 0 / .10), 0 0 0 1px rgb(var(--c-line));
  }
  .hoja-disparador:active { box-shadow: 0 1px 4px rgb(0 0 0 / .12), 0 0 0 1px rgb(var(--c-line)); }

  /* Con el aviso de cookies puesto, el "+" se sube por encima de él.
     El aviso es una caja fija abajo a z-50 y el botón está a z-40: tapaba el
     botón de añadir en Gastos, Ingresos, Ahorro, Cuentas y Proyectos, y
     pulsarlo no hacía nada. El alto del aviso lo mide el JS de base.html
     (`--alto-aviso`), porque su texto ocupa tres líneas o cuatro según el
     ancho de la pantalla. */
  body.con-aviso-cookies .hoja-disparador {
    bottom: calc(var(--alto-barra, 5rem) + var(--alto-aviso, 0px) + 1.25rem);
  }

  /* Y el propio aviso, por encima de la barra flotante y no encima de ella. */
  body.con-aviso-cookies #cookieNotice {
    bottom: calc(var(--alto-barra, 5rem) + 0.5rem);
  }

  /* Con la hoja abierta, lo de detrás no se desplaza: si no, arrastrar dentro
     de la hoja mueve la página del fondo y da sensación de pantalla rota. */
  body.hoja-abierta { overflow: hidden; }
}

@media (min-width: 768px) {
  .hoja-agarre,
  .hoja-fondo,
  .hoja-disparador { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hoja-panel, .hoja-fondo { transition: none; }
}

/* ---- Sitio para la barra inferior ----
   La reserva era un `pb-24` fijo (96 px). La barra mide unos 60 px MÁS la zona
   segura del teléfono (la franja del gesto de inicio, hasta 34 px), así que en
   esos móviles quedaban 2 px de aire y el último elemento de la página salía
   pegado o medio tapado.

   Ahora la altura real de la barra la mide el JS de base.html y la deja en
   --alto-barra. El 5rem es la reserva por si ese JS no llegara a ejecutarse:
   más vale sobrar espacio que tapar un botón. */
@media (max-width: 767px) {
  #contenido { padding-bottom: calc(var(--alto-barra, 5rem) + 2rem); }
}

/* ---- Deslizar para borrar ----
   Solo en móvil: en escritorio está la ✕ de siempre, que con ratón es más
   rápida y más precisa. La acción va DETRÁS de la fila y se descubre al
   arrastrar, en vez de ocupar sitio permanentemente. */
@media (max-width: 767px) {
  .deslizable {
    position: relative;
    overflow: hidden;
  }
  .deslizable-accion {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 88px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #D24B5A;  /* loss: rojo fijo, significa lo mismo en toda la app */
  }
  .deslizable-cuerpo {
    position: relative;
    transition: transform .2s ease;
    will-change: transform;
  }
  .deslizable.abierto .deslizable-cuerpo { transform: translateX(-88px); }
  /* Mientras el dedo arrastra no hay transición: la posición la manda el
     dedo, y una transición encima hace que la fila vaya por detrás del gesto
     y se sienta pegajosa. */
  .deslizable.arrastrando .deslizable-cuerpo { transition: none; }
}
@media (min-width: 768px) {
  .deslizable-accion { display: none; }
}

/* --- El cajón lateral, por debajo de la barra de estado --------------------
   ⚠️ Es `fixed inset-y-0`, o sea que empieza en el píxel 0 de la pantalla. En
   la app instalada (que va a pantalla completa) ese píxel está DEBAJO del reloj
   y la batería, así que "VANTAGLY" y la foto de perfil salían tapados.

   El resto de la app ya reservaba ese hueco --la barra de abajo, las cabeceras,
   las hojas--; a este se le había olvidado. Y abajo lo mismo, para que "Salir"
   no quede pegado a la barra de gestos. */
@media (max-width: 767px) {
  #appSidebar {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
  }
}

/* ---- Tirar para refrescar ---- */
@media (max-width: 767px) {
  #tirarRefrescar {
    position: fixed;
    top: calc(env(safe-area-inset-top) + 3rem);
    left: 50%;
    z-index: 50;
    width: 34px;
    height: 34px;
    margin-left: -17px;
    border-radius: 999px;
    background: #fff;
    box-shadow: 0 4px 14px rgba(2, 6, 12, .18);
    display: grid;
    place-items: center;
    /* Arranca fuera de la pantalla y sube según tira el dedo. */
    transform: translateY(-70px);
    opacity: 0;
    pointer-events: none;
  }
  #tirarRefrescar.soltando { transition: transform .25s ease, opacity .25s ease; }
  #tirarRefrescar svg { display: block; }
  #tirarRefrescar.girando svg { animation: girar .7s linear infinite; }
  @keyframes girar { to { transform: rotate(360deg); } }
}
@media (min-width: 768px) {
  #tirarRefrescar { display: none; }
}

/* ---- El aviso de cookies, por encima de la barra inferior ----
   Es `fixed bottom-4` con z-50, y la barra inferior es z-30 pegada abajo: en
   móvil el aviso caía justo encima y tapaba los botones de navegación. Quien
   no lo hubiera cerrado se quedaba sin poder cambiar de sección. */
@media (max-width: 767px) {
  #cookieNotice { bottom: calc(var(--alto-barra, 5rem) + 1rem); }
}

/* ============================ PANEL SIN CAJAS (MÓVIL) ============================
   NOTA (05/09/2026): todo esto vivía detrás de `html.mono`, así que la hoja sin
   cajas solo existía en el modo monocromo. En la paleta clara —la que ve todo
   el mundo por defecto— el Panel seguía siendo una pila de tarjetas con borde,
   y por eso en el móvil no parecía una app. El selector pasa a
   `html:not(.jarvis)`: vale para las cinco paletas claras y para monocromo, y
   deja fuera el modo futurista, que tiene su propia estética de HUD y a quien
   estos parches le sentarían mal.

   En el móvil el Panel deja de ser una rejilla de tarjetas y pasa a ser UNA hoja:
   secciones a todo el ancho, separadas por una línea fina.

   Ocho tarjetas con borde, esquinas redondeadas y su propio relleno en una
   pantalla de 375 px dejan el contenido en un pasillo estrecho y hacen que
   cada bloque parezca un objeto suelto. Sin las cajas, la pantalla se lee de
   arriba abajo de un tirón y se gana el ancho que se iba en bordes y márgenes.

   Todo dentro de una media query: EN ESCRITORIO NO CAMBIA NADA. */
@media (max-width: 767px) {
  /* Los hijos de las filas de la rejilla pierden su cromo de tarjeta. */
  html:not(.jarvis) .panel-plano [class*="grid"] > * {
    background: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    min-height: 0 !important;
  }

  /* Sin líneas: las secciones se separan con AIRE, no con marcas. Una línea
     sigue siendo un borde, y vuelve a partir la pantalla en trozos. El espacio
     ya dice dónde acaba una sección y empieza la siguiente. */
  html:not(.jarvis) .panel-plano [class*="grid"] > * + * {
    padding-top: 1.75rem !important;
  }
  html:not(.jarvis) .panel-plano [class*="grid"] {
    gap: 0 !important;
  }

  /* Las secciones oscuras dejan de serlo: sobre la hoja clara, el texto blanco
     no se leería. Se devuelve el color normal a lo que estaba pensado para ir
     sobre fondo oscuro. */
  html:not(.jarvis) .panel-plano [class*="grid"] > .text-white,
  html:not(.jarvis) .panel-plano [class*="grid"] > .text-white * {
    color: rgb(var(--c-900));
  }
  html:not(.jarvis) .panel-plano [class*="grid"] > .text-white [class*="text-white/"] {
    color: rgb(var(--c-muted));
  }
}

/* --- `.panel-grafica`: la portada de una pantalla, sin caja -----------------
   Se le pone a UN bloque por pantalla: el que lleva la cifra que manda y su
   curva (Inversiones, Ingresos...). En claro el resto de la pantalla conserva
   sus tarjetas —son listas, y ahí el marco sí separa—, pero esta no: es una
   cifra grande y una gráfica, y el marco sobre fondo blanco solo añade una
   línea. Es la misma idea que el Panel General, empaquetada en una clase para
   no volver a escribirla en cada pantalla.

   No va acotada a `.pantalla-plana`: la usa también Ingresos, que no lleva esa
   clase. */
@media (max-width: 767px) {
  html:not(.jarvis) .panel-grafica {
    background: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
  /* Si la caja era OSCURA (la de Ingresos es `bg-navy-900`), su texto blanco se
     queda sin fondo y hay que devolverle color, igual que en el Panel. */
  html:not(.mono):not(.jarvis) .panel-grafica [class*="text-white/"] {
    color: rgb(var(--c-muted));
  }
  html:not(.mono):not(.jarvis) .panel-grafica .text-white {
    color: rgb(var(--c-900));
  }
  /* El lienzo se pega al borde IZQUIERDO y no al derecho.
     - Por la izquierda gana el rem de relleno de la página: si no, las cifras
       del eje («10k», «8k») empiezan a 16 px del borde y la gráfica entera
       parece desplazada a la derecha, con un hueco raro al lado.
     - Por la derecha NO se sangra: ahí se iría el último punto de la serie, que
       es el de hoy y el dato que más importa de la pantalla. */
  html:not(.jarvis) .panel-grafica canvas,
  html:not(.jarvis) .panel-plano .grafica-heroe canvas {
    margin-left: -1rem !important;
    margin-right: 0 !important;
    width: calc(100% + 1rem) !important;
  }
  /* El dedo tiene que poder recorrer la curva SIN que la página se desplace,
     pero sin bloquear el desplazamiento vertical: `pan-y` deja subir y bajar y
     entrega a la gráfica los arrastres horizontales, que es justo el gesto de
     "¿cuánto llevaba aquí?". */
  .panel-grafica canvas,
  .panel-plano .grafica-heroe canvas {
    touch-action: pan-y;
  }
}

/* El punto rojo de la campana lleva un aro DEL COLOR DEL FONDO, no blanco: en
   monocromo la pantalla es negra y un aro blanco se veía como un segundo punto
   pegado al primero. Con `--c-surface` sigue al tema en las dos. */
.punto-aviso { box-shadow: 0 0 0 2px #fff; }
html.mono .punto-aviso { box-shadow: 0 0 0 2px #000; }

/* --- Que se sienta fluida ---------------------------------------------------
   Cosas pequeñas que separan "una web" de "una app". Ninguna cambia lo que hace
   la pantalla; cambian lo que se nota al usarla. */

/* Las fotos de perfil entraban de golpe: el hueco estaba reservado (llevan alto
   y ancho fijos) pero durante un instante era un agujero, y al llegar la imagen
   daba un pestañeo. Con un círculo relleno debajo y una entrada suave, el hueco
   ya parece un avatar antes de serlo. */
.avatar-suave {
  background-color: rgb(var(--c-line));
  animation: avatarEntra .35s ease-out both;
}
@keyframes avatarEntra {
  from { opacity: 0; transform: scale(.94); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 767px) {
  /* Respuesta al dedo. Sin esto, tocar la barra de abajo no da ninguna señal
     hasta que la pantalla siguiente termina de llegar, y parece que el toque no
     ha registrado. */
  #barraInferior a,
  #barraInferior button {
    -webkit-tap-highlight-color: transparent;
    transition: color .15s ease, transform .12s ease;
  }
  #barraInferior a:active,
  #barraInferior button:active { transform: scale(.92); }

  /* Aquí NO va animación de entrada para la barra. Se probó y quedaba peor:
     la barra se sustituye en cada navegación (viaja en el `hx-select-oob`), así
     que la animación se repetía CADA VEZ que cambiabas de sección y la barra
     daba un salto hacia arriba en cada toque. Una animación de entrada solo
     tiene sentido en algo que entra una vez. */

  /* Lo mismo para el botón "+" y las tarjetas que se pulsan. */
  .hoja-disparador { transition: transform .12s ease, box-shadow .2s ease; }
  a[href], button { -webkit-tap-highlight-color: transparent; }
}

/* --- El cambio de pantalla, en dos tiempos ----------------------------------
   Antes solo había entrada: el contenido viejo desaparecía de golpe y el nuevo
   aparecía con un fundido. Entre medias había un parpadeo, y el salto se notaba
   sobre todo yendo a Ajustes y volviendo.

   Ahora el saliente se atenúa MIENTRAS el servidor responde (la clase la pone
   base.html en `htmx:beforeRequest`), así que la transición no cuesta tiempo:
   ocurre dentro de la espera que ya había. La entrada la sigue haciendo
   `.page-fade-in`.

   Se atenúa a .3 y no a 0: apagándolo del todo, una respuesta lenta te deja
   mirando una pantalla en blanco, que es peor que ver la anterior difuminada.

   SOLO OPACIDAD, nunca `transform`. Está avisado en base.html y va en serio: un
   ancestro con `transform` convierte en `absolute` todo `position: fixed` que
   tenga dentro, y este elemento es `#contenido` -- el botón "+" y las hojas
   dejarían de anclarse a la pantalla para anclarse a un contenido de varias
   pantallas de alto. Un deslizamiento de 6 px costaría eso. */
/* El apagado antes de pedir la pantalla nueva. 90 ms y no 130: esto se paga
   ANTES de que salga la petición, así que se suma entero a lo que el usuario
   espera. Lo justo para que se note que algo pasa. */
#contenido.saliendo {
  opacity: .35;
  transition: opacity 90ms ease-in;
}

/* Quien haya pedido menos movimiento en su teléfono, no lo tiene. */
@media (prefers-reduced-motion: reduce) {
  .avatar-suave { animation: none; }
  #contenido.saliendo { transition: none; opacity: 1; }
}

/* --- La app en móvil, sobre BLANCO -----------------------------------------
   El <body> lleva un degradado azul claro puesto en línea (#EAF1FC → #DCE9F9).
   En escritorio funciona: las tarjetas blancas se recortan encima y el degradado
   es el fondo del tablero. En el móvil, al quitar las tarjetas, ese degradado
   pasó a ser el fondo de TODO -- y entonces la pantalla entera es azul pálido
   con texto azul oscuro y una curva azul: todo del mismo tono y sin un punto de
   contraste. Es lo que hacía que la app se viera apagada.

   Sobre blanco, el azul marino del texto y de la curva pasan a ser lo único con
   color en la pantalla, y se leen. Es el mismo truco de cualquier app de banca:
   el fondo no compite.

   Va en TODA la app y no solo en el Panel: el degradado tampoco aporta nada en
   Gastos o en Cuentas, y tener una pantalla blanca y la siguiente azulada al
   cambiar de sección es de las cosas que más delatan que esto es una web.

   Solo en móvil. En escritorio el degradado sigue siendo el tablero sobre el
   que se recortan las tarjetas, y ahí sí hace su trabajo.

   El `!important` no es opcional: el degradado va en el atributo `style` del
   <body>, y sin él ninguna regla de hoja puede quitarlo.

   `body:has(#contenido)` acota la regla a la app con sesión iniciada. Sin eso
   alcanzaba también a la LANDING (que tiene su propio fondo y es la página que
   mira Google) y a las pantallas de login y registro, y con `!important` les
   habría borrado el suyo sin remedio. Ninguna de esas plantillas tiene
   `#contenido`: es lo que las distingue del armazón de la app. */
@media (max-width: 767px) {
  html:not(.mono):not(.jarvis) body:has(#contenido) {
    background: #fff !important;
  }
}

/* --- Las cajas pensadas para ir sobre oscuro, en la hoja clara -------------
   La tarjeta de la cuenta del carrusel es `bg-white/10`: un blanco al 10% que
   sobre el azul oscuro de Capital Bancario se lee como una superficie. Al
   quitarle el fondo a la tarjeta en la paleta clara, ese 10% de blanco sobre
   una hoja ya casi blanca desaparece, y la cuenta se queda flotando sin caja
   —parece un fallo de pintado, no una decisión. Se le devuelve una superficie
   de verdad, la misma que usa el resto de la app. */
@media (max-width: 767px) {
  html:not(.mono):not(.jarvis) .panel-plano #bankCard {
    background: rgb(var(--c-surface)) !important;
    border: 1px solid rgb(var(--c-line));
  }
}

/* La variación del patrimonio, en verde o rojo.
   Va por id a propósito: la regla que devuelve el color al texto de las
   tarjetas oscuras (`.panel-plano ... > .text-white *`) tiene tres clases de
   especificidad y se comía el verde. Un id la gana sin recurrir a !important. */
#nwDelta.text-gain { color: #22A06B; }
#nwDelta.text-loss { color: #D24B5A; }

/* --- La gráfica del patrimonio, con altura propia --------------------------
   Sin cajas, las tarjetas llevan `min-height: 0`, y con eso el `flex-1` del
   contenedor de la gráfica no tiene nada a lo que estirarse: se quedaba en
   cero y la curva desaparecía de la pantalla. Aquí se le da la altura a mano,
   y generosa: es lo segundo que se mira después de la cifra. */
@media (max-width: 767px) {
  html:not(.jarvis) .panel-plano .grafica-heroe {
    /* ⚠️ `flex: none` NO SOBRA, y sin él `height` aquí no vale para nada.
       El contenedor lleva `flex-1` de Tailwind, o sea `flex: 1 1 0%`, y en un
       contenedor de columna **la base flexible SUSTITUYE a la altura**: el
       navegador ignoraba estos 210px y le daba al elemento el tamaño de su
       contenido. Y su contenido es un `<canvas>` que Chart.js dimensiona
       leyendo... la altura del contenedor. Cada vez que se repintaba la
       pantalla, el canvas se llevaba su propia altura multiplicada por la
       densidad de píxeles: 210 -> 401 -> y subiendo.
       Se veía al darle al ojo del modo privado, porque eso recarga el panel
       por htmx y vuelve a crear las gráficas. `min-height` no lo frenaba: pone
       suelo, y lo que se escapaba era el techo. */
    flex: none;
    height: 210px;
  }

}

/* --- Campo con su acción dentro ------------------------------------------
   Un recuadro con borde y, al lado, un botón oscuro: eso es un formulario. En
   una pantalla que ya tiene ocho tarjetas, tres pares de esos son seis cajas
   más compitiendo por la atención. Aquí es UNA pieza: el campo y su acción
   dentro del mismo contorno redondeado, y la acción en color de enlace en vez
   de un bloque negro. */
.campo-accion {
  display: flex;
  align-items: center;
  gap: .5rem;
  border: 1px solid rgb(var(--c-line));
  border-radius: 999px;
  padding: .15rem .35rem .15rem 1rem;
}
.campo-accion:focus-within { border-color: rgb(var(--c-primary)); }
.campo-accion > input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: inherit;
  padding: .55rem 0;
  font-size: .875rem;
}
.campo-accion > input:focus { outline: none; }
.campo-accion > button {
  flex: none;
  border-radius: 999px;
  padding: .45rem 1rem;
  font-size: .8125rem;
  font-weight: 600;
  color: #fff;
  background: rgb(var(--c-primary));
}

/* La gráfica con cara de panel (Gastos). Más alta que la genérica: al quitarle
   el marco y los ejes queda mucho aire, y con 180 px la curva se veía apretada
   contra el rótulo. */
/* 200 y no 240: con 240 y el rótulo grande encima, en un móvil la gráfica se
   comía la pantalla y empujaba los presupuestos fuera de vista. */
.grafica-panel { height: 200px; }

/* --- Cajas de gráfica: el canvas no puede medir a su propio contenedor -----

   ⚠️ El bucle que rompía el Panel General. Un `<canvas>` normal cuenta como
   contenido de su caja, así que la caja crece con él. Y Chart.js dimensiona el
   canvas leyendo la altura de la caja. Con `maintainAspectRatio: false` y una
   caja sin altura propia, eso es una pescadilla: cada repintado le sumaba al
   canvas su propia altura por la densidad de píxeles del móvil (179 -> 342 ->
   ...). Se notaba al darle al ojo del modo privado, porque recarga el panel
   por htmx y vuelve a crear las gráficas.

   Sacándolo del flujo, el canvas deja de contar: la caja mide lo que le diga
   su padre y el canvas se ajusta a la caja. En un sentido, no en los dos. */
.caja-grafica {
  position: relative;
  /* ⚠️ El cinturón. El canvas va posicionado en absoluto, así que si por lo
     que sea acaba midiendo más que su caja, se pinta ENCIMA de lo que venga
     debajo -- y en el Panel eso es el panel siguiente. Con esto, como mucho se
     recorta; nunca invade. Un recorte se ve y se arregla, una invasión parece
     que la app está rota. */
  overflow: hidden;
}
.caja-grafica > canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  /* ⚠️ `!important` EN LA ALTURA, y es obligatorio.
     Chart.js escribe la altura EN LÍNEA en el canvas (`style="height:187px"`),
     y un estilo en línea gana a cualquier regla de hoja que no lleve
     `!important`. O sea que esta línea, sin él, no servía para nada: mandaba
     Chart.js. Así fue como un canvas de 187 px acabó dentro de una caja de 0 y
     pintando sobre el panel de Suscripciones. */
  height: 100% !important;
}

/* ⚠️ Y los tirantes, que es lo que arregla la causa.
   En móvil, `html:not(.jarvis) .panel-plano [class*="grid"] > *` pone
   `min-height: 0 !important` para que los paneles se ajusten a su contenido.
   Está bien para un panel de texto, pero es letal para uno cuyo único
   contenido es un canvas en absoluto: no hay contenido que medir, el panel se
   queda con la altura del rótulo y la caja de la gráfica --que es `flex-1`, o
   sea `flex-basis: 0`-- se queda a CERO.

   Es la tercera vez hoy que muerde lo mismo: **`flex-1` no es una altura**.
   Ya pasó en Gastos con el ojo de privacidad y en Historial. Aquí igual: se le
   da una altura de verdad y se le quita el `flex`. */
@media (max-width: 767px) {
  html:not(.jarvis) .panel-plano .caja-grafica {
    flex: none;
    height: 170px;
  }
}

/* --- Panel de móvil: la cifra manda y la gráfica va a sangre ---
   Referencia de estilo: paneles de banca tipo Trade Republic. Lo que se toma
   son patrones de uso, no su diseño: dato grande arriba, gráfica de borde a
   borde sin marco, controles discretos debajo y un solo color de acento. */
@media (max-width: 767px) {
  /* La cifra principal de cada sección, más grande y con menos peso: en un
     móvil se lee de un vistazo y no compite con el resto. */
  .panel-plano [class*="grid"] > * > p.text-2xl,
  .panel-plano [class*="grid"] > * > p.text-3xl {
    font-size: 2.25rem;
    line-height: 1.1;
    font-weight: 600;
    letter-spacing: -0.02em;
  }

  /* Los títulos de sección se apartan: informan, no gritan.

     Los `> div >` no sobran: la mitad de los títulos del Panel viven dentro de
     una fila de cabecera junto a su enlace ("Buckets de Gasto · Editar ★",
     "Suscripciones Activas · Ver →"). Buscando solo hijos directos, esos se
     quedaban fuera y salían grandes y en negro mientras "PATRIMONIO NETO" salía
     pequeño y en gris: dos estilos de título en la misma pantalla. */
  .panel-plano [class*="grid"] > * > p.text-xs.uppercase,
  .panel-plano [class*="grid"] > * > p.text-sm.font-semibold,
  .panel-plano [class*="grid"] > * > div > p.text-xs.uppercase,
  .panel-plano [class*="grid"] > * > div > p.text-sm.font-semibold {
    font-size: 0.7rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    opacity: .55;
    font-weight: 500;
  }

  /* Gráficas a sangre: se cancelan los 16 px de relleno de #contenido para que
     la curva llegue a los dos bordes de la pantalla. Encerrada en su columna,
     una gráfica de 340 px de ancho no dice nada; a pantalla completa sí. */
  /* Misma sangría para gráficas sueltas fuera del Panel. */
  html.mono .grafica-a-sangre {
    margin-left: -1rem;
    margin-right: -1rem;
  }

  /* Las mini-gráficas de los buckets NO: viven dentro de una celda de 2x2,
     y sacarlas 16 px por cada lado las hacía cruzar por encima de la celda
     de al lado. La sangre es para las gráficas que mandan en su sección. */
  /* `#netWorthChart` va excluida: es la única con eje de cifras a la izquierda,
     y sacarla 1rem por cada lado le cortaba las cifras por un lado y el punto de
     HOY por el otro. Se queda dentro de su ancho. */
  html:not(.jarvis) .panel-plano canvas:not([id^="spark"]):not(#netWorthChart) {
    margin-left: -1rem;
    margin-right: -1rem;
    width: calc(100% + 2rem) !important;
    max-width: none !important;
  }

  /* Las temporalidades dejan de ser botones.
     Como píldoras con borde eran seis cajas gordas peleándose por la atención
     justo debajo de la gráfica, que es lo que de verdad hay que mirar. Aquí son
     lo que son: seis etiquetas de texto. La elegida se marca con el color de
     la app, no con una caja alrededor. */
  .panel-plano #nwRanges,
  #evoRangeButtons {
    justify-content: flex-start;
    gap: 0;
    margin-top: .875rem;
  }
  .panel-plano .nw-btn,
  #evoRangeButtons .evo-range-btn,
  #rangeButtons .range-btn {
    background: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    padding: .25rem .625rem !important;
    font-size: .75rem !important;
    font-weight: 500;
    letter-spacing: .01em;
    color: rgb(var(--c-muted)) !important;
    box-shadow: none !important;
  }
  /* La activa: color de la app y un trazo fino debajo, del ancho del texto. */
  .panel-plano .nw-btn.bg-white\/15,
  #evoRangeButtons .evo-range-btn.bg-navy-900,
  #rangeButtons .range-btn.bg-gain {
    color: rgb(var(--c-primary)) !important;
    /* Subraya el TEXTO, no la caja.
       Con una sombra interior la raya medía todo el botón, relleno incluido, y
       al caer en medias posiciones de píxel el navegador dejaba unos hilillos
       más finos asomando a los lados. El subrayado va exactamente bajo las
       letras y siempre sale nítido. */
    text-decoration: underline;
    text-decoration-thickness: 1.5px;
    text-underline-offset: 6px;
  }
}


/* --- Inversiones en móvil: fuera las cajas ---------------------------------
   Misma idea que el Panel: en una pantalla de 400 px, seis marcos blancos con
   borde y esquinas redondeadas parten la vista en trozos y roban ancho a lo
   único que importa, que es la gráfica. Aquí las secciones se separan con
   espacio. En escritorio no cambia nada: todo esto vive en un @media. */
@media (max-width: 767px) {
  /* Se busca el PANEL, no su profundidad.
     Perseguir niveles (`> div > div`) fallaba en cuanto una pantalla anidaba
     una caja más adentro -- en Ahorros estaban a tres. Lo que define un panel
     en esta app son sus esquinas grandes: `rounded-xl` y `rounded-2xl`. Los
     campos y botones usan `rounded-md`, así que no entran, y el panel de la
     hoja se excluye a mano porque su caja sí tiene sentido: es lo que la separa
     de la pantalla que hay detrás. */
  html.mono .pantalla-plana [class*="rounded-xl"]:not(.hoja-panel),
  html.mono .pantalla-plana [class*="rounded-2xl"]:not(.hoja-panel) {
    background: none;
    border-color: transparent;
    border-radius: 0;
    box-shadow: none;
    padding-left: 0;
    padding-right: 0;
  }
  /* El relleno lateral se va: la gráfica llega al borde de la pantalla. */
  html.mono .pantalla-plana > * > *[class*="p-3"],
  html.mono .pantalla-plana > * > *[class*="p-4"],
  html.mono .pantalla-plana > *[class*="px-3"] {
    padding-left: 0;
    padding-right: 0;
  }
  /* Las listas conservan su separación, pero sin línea: solo aire. */
  html.mono .pantalla-plana .divide-y > * + * {
    border-top-width: 0;
  }
  /* Las fechas del deslizador dejan de ser dos cajitas con borde. */
  html.mono .pantalla-plana #evoDesde,
  html.mono .pantalla-plana #evoHasta {
    border: 0;
    padding-left: 0;
    padding-right: 0;
  }
  /* Las secciones respiran entre sí.

     Con `gap` y NO con `margin-top` en el hermano: estas pantallas reordenan
     sus bloques con `order`, así que el «segundo del marcado» puede ser el
     PRIMERO en pantalla -- en Inversiones lo es, la portada con la curva. Con
     el margen, ese bloque arrancaba con 20 px muertos pegados a la cabecera y
     parecía que la pantalla no empezaba arriba del todo. El `gap` solo separa
     entre elementos y no deja hueco delante del primero, se ordenen como se
     ordenen. */
  .pantalla-plana {
    gap: 1.25rem !important;
  }

  /* Y la gráfica a sangre: se cancelan los 16 px de relleno de #contenido.
     Encerrada en su columna la curva mide 360 px de un móvil de 402; suelta,
     ocupa la pantalla entera, que es de lo que va esta sección. */
  /* Se excluye lo que ya vive dentro de un `.panel-grafica`: esa clase sangra
     la gráfica SOLO por la izquierda, a posta, para no comerse el último punto
     de la serie (el de hoy). Cruzándose las dos reglas ganaba el ancho de
     aquí (100% + 2rem) y el margen derecho de allí (0), y el lienzo se salía
     16 px por la derecha: en Inversiones y en modo oscuro la pantalla se podía
     arrastrar de lado. */
  html.mono .pantalla-plana canvas:not([id^="spark"]) {
    margin-left: -1rem;
    margin-right: -1rem;
    width: calc(100% + 2rem) !important;
    max-width: none !important;
  }
  html.mono .pantalla-plana .panel-grafica canvas {
    margin-right: 0 !important;
    width: calc(100% + 1rem) !important;
  }
  /* Y si el HUECO ya sangra (`.grafica-a-sangre`), el lienzo no vuelve a
     hacerlo: sangraban los dos, uno dentro del otro, y el resultado eran 32 px
     de más por cada lado. Se veía en Inversiones en oscuro, con la pantalla
     todavía sin datos. */
  html.mono .pantalla-plana .grafica-a-sangre canvas {
    margin-left: 0;
    margin-right: 0;
    width: 100% !important;
  }
}


/* --- Monocromo de verdad en móvil ------------------------------------------
   El modo ya ponía las GRÁFICAS en blanco y negro, pero el color de la app
   (`--c-primary`) se quedaba como estaba: en una pantalla en blanco y negro
   seguían saliendo toques morados -- el rótulo de Capital bancario, los chips
   de temporalidad, el "+" de añadir. Aquí el acento pasa a ser blanco, que es
   lo que este modo viene a hacer. Solo en móvil: el escritorio no se toca. */
@media (max-width: 767px) {
  html.mono {
    --c-primary: 255 255 255;
    --c-primary-claro: 255 255 255;
    /* El gris apagado y las líneas también salen de la paleta, así que con la
       morada eran un gris con tono morado y una línea malva: en blanco y negro
       se notan igual que un acento de color. Aquí son grises de verdad. */
    --c-muted: 142 142 147;
    --c-line: 35 35 38;
  }
  /* Sobre un acento blanco, lo de encima tiene que ir en negro o desaparece. */
  html.mono .hoja-disparador {
    background: #fff;
    color: #000;
  }
  html.mono .bg-primary,
  html.mono [class*="bg-primary"] {
    color: #000 !important;
  }

}

/* --- La hoja, con el acabado de una app y no de una web -------------------- */
@media (max-width: 767px) {
  /* Curva de salida: rápida al principio y frenando al final, como cuando
     sueltas algo y se posa. La de antes iba a velocidad casi constante y se
     notaba mecánica. */
  .hoja-panel {
    transition: transform .32s cubic-bezier(.22, 1, .36, 1) !important;
  }

  /* El fondo oscurece un pelín después, no a la vez: primero se ve subir la
     hoja y luego se apaga lo de detrás. */
  .hoja-fondo {
    transition: opacity .32s ease;
  }

  /* Al cerrar se va algo más rápido que al abrir. Cerrar es una decisión ya
     tomada: esperar a que la animación termine se hace largo. */
  .hoja:not(.abierta) .hoja-panel {
    transform: translateY(100%);
    transition: transform .26s cubic-bezier(.32, 0, .67, 0) !important;
  }

  /* El contenido entra un instante después que la hoja, así no llega todo de
     golpe. Es el detalle que separa "se abrió un formulario" de "subió algo". */
  .hoja.abierta .hoja-panel > * {
    animation: hojaContenido .28s ease-out both;
    animation-delay: .08s;
  }
  @keyframes hojaContenido {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}



/* --- Negro de verdad, sin recuadros -----------------------------------------
   El modo monocromático pinta cada panel de #0B0B0C sobre un fondo #000: un gris
   casi negro que aun así se ve, y deja la pantalla partida en rectángulos. Es
   justo lo contrario de lo que se busca aquí. Su regla gana por especificidad,
   así que hay que decirlo más alto. */
@media (max-width: 767px) {
  html.mono .pantalla-plana,
  html.mono .pantalla-plana [class*="rounded-xl"]:not(.hoja-panel),
  html.mono .pantalla-plana [class*="rounded-2xl"]:not(.hoja-panel),
  html:not(.jarvis) .panel-plano [class*="grid"] > * {
    background: transparent !important;
    border-color: transparent !important;
  }
  /* Las divisiones internas de las listas también desaparecen: el aire basta. */
  html.mono .pantalla-plana [class*="divide-"] > * + * {
    border-top-color: transparent !important;
  }
}


/* --- El único recuadro de la pantalla --------------------------------------
   En una pantalla sin marcos, un recuadro deja de ser decoración y pasa a
   significar algo: esto se toca. Por eso el selector de cartera lleva borde y
   el resto no. Se marca lo justo -- un borde tenue y un fondo apenas más claro
   que el negro -- para que se note sin volver a llenar la pantalla de cajas. */
@media (max-width: 767px) {
  .selector-cartera {
    padding: .75rem 1rem;
    border: 1px solid rgb(var(--c-line));
    border-radius: 14px;
    background: rgb(var(--c-surface) / .5);
    transition: background-color .15s ease, border-color .15s ease;
  }
  .selector-cartera:active {
    background: rgb(var(--c-surface));
    border-color: rgb(var(--c-muted) / .5);
  }
  html.mono .selector-cartera {
    border-color: #232326;
    background: #0B0B0C;
  }
  html.mono .selector-cartera:active {
    background: #141416;
    border-color: #3A3A3C;
  }
  /* La flecha gira al abrir: dice que la hoja viene de este botón. */
  .selector-cartera svg { transition: transform .25s ease; }
  body.hoja-abierta .selector-cartera svg { transform: rotate(180deg); }
}


/* --- El donut deja de comerse la pantalla ----------------------------------
   Al quitar las cajas, su contenedor pasó a estirarse con la columna y el
   círculo se hizo tan ancho como la pantalla: 392 px de alto en un móvil de
   402, más que las dos gráficas de arriba juntas. Un donut no dice más por ser
   más grande; lo que dice está en las proporciones. */
@media (max-width: 767px) {
  .panel-plano div:has(> #allocChart),
  .pantalla-plana div:has(> #catChart) {
    height: 200px;
    flex: none;
  }
  /* El de Inversiones lleva etiquetas fuera del aro, y esas necesitan su sitio:
     con 200 px el aro se queda sin margen y los nombres se pisan. */
  .pantalla-plana div:has(> #distChart) {
    height: 250px;
    flex: none;
  }
}


/* --- Inversiones con la misma voz que el Panel -----------------------------
   Quitar las cajas no bastaba: los títulos seguían en negrita grande, como
   cuando cada uno encabezaba su propio recuadro. Sin marco alrededor, un título
   así compite con las cifras en vez de ordenarlas. Mismo trato que en el Panel:
   el título se aparta y el dato manda. */
@media (max-width: 767px) {
  .pantalla-plana h2,
  .pantalla-plana h3 {
    font-size: .7rem !important;
    font-weight: 500 !important;
    letter-spacing: .14em;
    text-transform: uppercase;
    opacity: .55;
  }
  /* El subtítulo que iba debajo del título sobra: repetía lo que ya se ve. */
  .pantalla-plana h2 + span.text-xs,
  .pantalla-plana h3 + span.text-xs {
    display: none;
  }

  /* Las secciones se separan con aire, y bastante. Es lo único que hace de
     frontera ahora que no hay bordes: si el hueco es corto, todo se lee como
     una lista larga sin principio ni final. */
  /* La separación la pone el `gap` del contenedor, no un margen entre
     hermanos. Con `order` cambiando el orden en pantalla, un `+ *` mira el
     orden del HTML y no el que se ve: unas secciones salían pegadas y otras
     con doble hueco. El `gap` no depende del orden. */
  html.mono .pantalla-plana > *[class*="flex"],
  html.mono .pantalla-plana > *[class*="grid"] {
    gap: 1.5rem !important;
  }

  /* Las filas de datos respiran igual que las de la lista de activos, así las
     dos mitades de la pantalla parecen la misma pantalla. */
  .pantalla-plana .space-y-4 > * + * {
    margin-top: .875rem;
  }

  /* Las barras de peso, más finas: son una referencia, no el dato. */
  .pantalla-plana .perf-bar,
  .pantalla-plana .perf-bar:first-child {
    border-radius: 999px;
  }
  .pantalla-plana [class*="h-1.5"] {
    height: 3px;
  }
}


/* --- El modo futurista, con el mismo cuidado que el monocromático -----------
   `jarvisBoot` envuelve la página entera y `jarvisCard` cada celda de la
   rejilla, las dos con `both` y con transform: el mismo fallo que tenía el modo
   monocromático, que dejaba el botón de añadir yéndose con el scroll y la hoja
   abriéndose a media página.
   En móvil se cambian por su versión sin transform. En escritorio se quedan tal
   cual: allí no hay ni botón flotante ni hojas, así que no rompen nada y su
   animación de entrada es parte del modo. */
@media (max-width: 767px) {
  html.jarvis .page-fade-in { animation-name: jarvisBootPlano; }
  html.jarvis main .grid > * { animation-name: jarvisCardPlano; }

  @keyframes jarvisBootPlano {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  @keyframes jarvisCardPlano {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}


/* --- Los últimos milímetros -------------------------------------------------
   Ajustes de colocación, que en una pantalla de 380 px son la diferencia entre
   que algo parezca una app o parezca una web encogida. */
@media (max-width: 767px) {
  /* 1. Arriba sobraba un dedo de negro. El relleno de #contenido está pensado
        para una página con título; estas empiezan directamente por el dato. */
  /* Solo en oscuro: sin cajas, el relleno de arriba dejaba un dedo de fondo
     muerto y había que recuperarlo. Con tarjetas ese hueco es la separación
     entre la cabecera y la primera tarjeta, y quitarlo las pega al borde. */
  html.mono .pantalla-plana,
  html:not(.jarvis) .panel-plano {
    margin-top: -1rem;
  }
  /* Los dos botones necesitan separarse de lo que viene debajo: pegados al
     título siguiente parecían encabezarlo en vez de ser un control aparte. */
  .pantalla-plana .selector-cartera {
    margin-bottom: .75rem;
  }
  /* Y el relleno de arriba de cada panel también sobra: era el aire de dentro
     de una caja, y ya no hay caja. La separación entre secciones la pone el
     margen de abajo, no el relleno de cada una. */
  html.mono .pantalla-plana > * > * {
    padding-top: 0;
  }

  /* 2. Las temporalidades, a la derecha. A la izquierda dejaban un hueco negro
        a su derecha que no era de nadie; alineadas al borde, ese hueco
        desaparece y la fila queda cerrada. */
  .panel-plano #nwRanges,
  #evoRangeButtons,
  #rangeButtons {
    justify-content: flex-end;
    /* `justify-content` no bastaba: el padre reparte con `justify-between` y,
       como en móvil su otro hijo está oculto, este se queda solo y se va al
       principio. El margen automático a la izquierda lo empuja al borde. */
    margin-left: auto;
  }
  /* El último chip pierde su relleno derecho para que el texto quede a plomo
     con el resto de la columna en vez de flotando un poco adentro. */
  .panel-plano .nw-btn:last-child,
  #evoRangeButtons .evo-range-btn:last-child,
  #rangeButtons .range-btn:last-child {
    padding-right: 0 !important;
  }

  /* 3. Las cifras de los ejes ya no tocan el borde.
        La gráfica va a sangre a propósito, pero las etiquetas se dibujan dentro
        del lienzo: pegadas al filo de la pantalla se leen mal y parecen
        cortadas. Se les devuelve el margen por dentro. */
  .pantalla-plana canvas,
  .panel-plano canvas {
    padding-left: .5rem;
    padding-right: .5rem;
    box-sizing: border-box;
  }

  /* 4. Los dos botones, más bajos: son un control, no una tarjeta. */
  .selector-cartera {
    padding: .625rem .75rem;
  }

  /* 5. Patrimonio Neto: el título en su línea, la cifra debajo, y las
        temporalidades al final del todo, bajo la gráfica.

        `display: contents` deshace la fila de título+temporalidades sin tocar
        el HTML, así que sus dos hijos pasan a ser filas de la tarjeta y se
        pueden reordenar. En escritorio la fila se queda como estaba, que es
        importante: ahí los dos comparten ancho, y sacando los botones de la
        fila para anclarlos con `position:absolute` se montaban encima del
        rótulo (la tarjeta mide unos 260 px y no caben los dos).

        OJO con el selector: `div:has(> #nwRanges)` señala «el div que tiene a
        #nwRanges de hijo directo». Si algún día #nwRanges deja de estar dentro
        de esa fila, esto pasa a señalar la TARJETA y la disuelve entera: sus
        hijos se vuelven hijos de la rejilla y el `order` de aquí manda la
        gráfica al final de TODO el Panel, a mil píxeles de su cifra. */
  .panel-plano div:has(> #nwRanges) {
    display: contents;
  }
  .panel-plano div:has(> #netWorthChart) {
    order: 5;
  }
  /* Los botones van ENTRE la variación y la gráfica, alineados a la derecha:
     es donde los puso Mauro en el boceto. Puestos debajo quedaban separados de
     la cifra por 210 px de curva y parecían el encabezado de la sección
     siguiente en vez del control de esta. */
  .panel-plano #nwRanges {
    order: 4;
  }
  /* Sin la separación entre letras el título cabe en una línea. */
  .panel-plano p.uppercase.tracking-\[0\.2em\] {
    letter-spacing: .12em;
    white-space: nowrap;
  }
}


/* El deslizador de fechas seguía con el acento de la paleta: un tirador morado
   en una pantalla en blanco y negro. */
@media (max-width: 767px) {
  html.mono #evoSlider {
    accent-color: #fff;
  }
}


/* --- Cuentas: una lista, no un tablero -------------------------------------
   Dos columnas de tarjetas en 380 px dejan cada cuenta en una caja diminuta con
   el saldo apretado dentro. Como lista de filas altas se leen todas seguidas y
   el saldo tiene sitio para respirar. Las cuentas son pocas y se miran en
   vertical: es una lista, no un tablero. */
@media (max-width: 767px) {
  .pantalla-plana > div[class*="grid-cols-1"] {
    grid-template-columns: 1fr;
    gap: 0 !important;
  }
  /* Cada cuenta se separa de la siguiente con aire, sin línea ni caja. */
  .pantalla-plana > div[class*="grid-cols-1"] > * + * {
    margin-top: 1.25rem;
  }
}

/* --- El botón de elegir archivo --------------------------------------------
   Venía con el aspecto que le pone el navegador: un botón gris del sistema y,
   al lado, un "Sin archivos seleccionados" en el color por defecto, que sobre
   fondo negro se lee fatal. Ahora es un botón de la app como los demás. */
@media (max-width: 767px) {
  input[type="file"] {
    width: 100%;
    font-size: .75rem;
    color: rgb(var(--c-muted));
  }
  input[type="file"]::file-selector-button {
    margin-right: .75rem;
    padding: .5rem 1rem;
    border: 1px solid rgb(var(--c-line));
    border-radius: 10px;
    background: transparent;
    color: rgb(var(--c-primary));
    font-weight: 600;
    font-size: .75rem;
  }
  html.mono input[type="file"]::file-selector-button {
    border-color: #232326;
    background: #0B0B0C;
    color: #fff;
  }
}


/* --- El texto de las tarjetas oscuras, en monocromo ------------------------
   Al quitarles el fondo oscuro hay que devolverle un color al texto que estaba
   pensado para ir en blanco sobre negro. Se usaba `--c-900`, el color de texto
   normal de la app... que en modo monocromático NO cambia: sigue siendo el de
   la paleta elegida. Con la paleta morada eso es un rgb(36,21,54) que sobre
   negro se ve como un morado apagado -- exactamente lo que este modo viene a
   quitar. En blanco y negro, el texto es blanco. */
@media (max-width: 767px) {
  /* OJO: este bloque es SOLO de monocromo y tiene que seguir siéndolo. Al
     abrir la hoja sin cajas a las paletas claras se cambiaron de golpe todos
     los `html.mono .panel-plano` del fichero, y este se coló: dejaba la cifra
     del patrimonio en BLANCO sobre la hoja clara, o sea invisible. El de
     arriba (el que le devuelve el color normal al texto) es el que vale para
     todas las paletas; este solo pisa el color cuando la hoja es negra. */
  html.mono .panel-plano [class*="grid"] > .text-white,
  html.mono .panel-plano [class*="grid"] > .text-white *,
  html.mono .pantalla-plana .text-white,
  html.mono .pantalla-plana .text-white * {
    color: #fff;
  }
  /* MENOS los botones de color, que en monocromo se repintan de BLANCO con la
     letra negra (`html.mono .bg-gain`, ver _head.html). La regla de aquí
     arriba es más específica y les devolvía la letra blanca: el botón
     «Aportar» de cada meta de Ahorro salía en blanco sobre blanco, sin texto.
     Se enumeran las tres clases en vez de excluir cualquier `bg-*`: probado
     con `:not([class*="bg-"])` y se lleva por delante la tarjeta del
     patrimonio del Panel, que es `bg-gradient-to-br` y sí pierde el fondo en
     monocromo -- la cifra quedaba en azul marino sobre negro. */
  html.mono .panel-plano .bg-gain.text-white,
  html.mono .panel-plano .bg-gain.text-white *,
  html.mono .panel-plano .bg-loss.text-white,
  html.mono .panel-plano .bg-loss.text-white *,
  html.mono .pantalla-plana .bg-gain.text-white,
  html.mono .pantalla-plana .bg-gain.text-white *,
  html.mono .pantalla-plana .bg-loss.text-white,
  html.mono .pantalla-plana .bg-loss.text-white * {
    color: #000;
  }
  html.mono .panel-plano [class*="grid"] > .text-white [class*="text-white/"],
  html.mono .pantalla-plana [class*="text-white/"] {
    color: #8E8E93;
  }
}


/* --- Las estadísticas del perfil, sueltas ----------------------------------
   Tres por fila, sin caja y con la cifra pequeña. Lo que se mira aquí es el
   conjunto -- racha, patrimonio, ahorro, crecimiento, rentabilidad -- y para
   eso hay que poder verlo todo junto, no una cifra por pantalla. */
@media (max-width: 767px) {
  .estadisticas-sueltas {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.25rem .75rem !important;
  }
  html.mono .estadisticas-sueltas > * {
    background: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    padding: 0 !important;
  }
  .estadisticas-sueltas > * > p:first-child {
    font-size: .625rem;
    letter-spacing: .1em;
    opacity: .55;
    line-height: 1.3;
  }
  .estadisticas-sueltas > * > p:nth-child(2) {
    font-size: 1.125rem !important;
    margin-top: .25rem !important;
    letter-spacing: -0.01em;
  }
  .estadisticas-sueltas > * > p:nth-child(3) {
    margin-top: 0 !important;
  }
}


/* --- Gastos: la gráfica sube, y los movimientos pierden la caja ------------
   La gráfica estaba al final de la pantalla: había que recorrer categorías,
   suscripciones, importaciones y movimientos para llegar a la única vista que
   resume el mes entero. Sube justo debajo del presupuesto restante, que es su
   sitio: primero cuánto queda, luego en qué se está yendo.

   Aquí NO se quitan todas las cajas a propósito. En las categorías y en las
   importaciones cada caja es una cosa distinta que se toca por separado, y el
   marco ayuda a separarlas. La lista de movimientos es una lista: ahí la caja
   solo estrecha el texto. */
@media (max-width: 767px) {
  .gastos-movil {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
  }
  /* `space-y-*` reparte con márgenes entre hermanos, y con `order` esos
     márgenes miran el orden del HTML y no el que se ve. El `gap` no. */
  .gastos-movil > * {
    margin-top: 0 !important;
    order: 3;
  }
  .gastos-movil > *:first-child { order: 1; }
  .gastos-movil > .graficas-gasto { order: 2; }

  html.mono .movimientos-recientes {
    background: none !important;
    border-color: transparent !important;
    border-radius: 0 !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}


/* --- Los dos últimos tonos ------------------------------------------------
   El título del diálogo de confirmación usa `text-navy-950`, que con la paleta
   morada es un rgb(26,16,37): sobre el fondo negro del modo monocromático se
   lee casi tan mal como no ponerlo. Y los apodos del perfil llevan un azul
   fijo de Tailwind, que en una pantalla en blanco y negro es lo único con
   color. Los dos, a blanco. */
@media (max-width: 767px) {
  html.mono #confirmModalTitle {
    color: #fff;
  }
  html.mono .text-blue-300 {
    color: #fff;
  }
}


/* --- La barra de scroll deja de dibujarse ---------------------------------
   `main` lleva `overflow-y: scroll` para que la página no dé un salto lateral
   cuando aparece o desaparece contenido. En un ordenador eso pinta una barra
   gris de 10 px; en un móvil no hace falta ninguna, porque el dedo ya sabe que
   se desplaza. Esa barra es la línea vertical que se veía pegada al borde
   derecho en todas las pantallas. */
@media (max-width: 767px) {
  main {
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Sin esto, al tirar hacia abajo estando ya arriba del todo el navegador
       despega el contenido y deja ver el fondo: un hueco negro que aparece y
       se queda hasta que sueltas. Es el rebote del sistema, no un margen. El
       gesto de tirar para refrescar lo lleva el script y sigue funcionando. */
    overscroll-behavior-y: contain;
  }
  main::-webkit-scrollbar {
    width: 0;
    height: 0;
    display: none;
  }
}


/* --- Las cifras de la lista, todas del mismo tamaño ------------------------
   Cada fila venía de una tarjeta distinta del escritorio, y cada una traía su
   tamaño y su tipografía: unas en `text-sm` monoespaciada, otras en `text-lg`
   de la fuente normal, y algunas con `mt-2` que las descolgaba de su etiqueta.
   Apiladas en una lista eso se ve de golpe y parece descuidado. Una lista de
   datos se lee comparando, y para comparar todos tienen que medir igual. */
@media (max-width: 767px) {
  .filas-dato > div > p:not(:first-child),
  .filas-dato > div > p:not(:first-child) span {
    font-size: .8125rem !important;
    font-weight: 600 !important;
    font-family: inherit !important;
    margin-top: 0 !important;
    line-height: 1.4;
  }
  /* La etiqueta, igual de estable que el dato. */
  .filas-dato > div > p:first-child,
  .filas-dato > div > div > p:first-child {
    font-size: .8125rem !important;
    font-weight: 400;
  }
}


/* --- Ficha de un activo: el orden de un móvil ------------------------------
   En escritorio esto es una rejilla y todo se ve a la vez, así que da igual el
   orden del HTML. En un móvil se lee en columna, y ahí el orden ES el guion:
   qué es y cuánto vale, cómo se ha movido, qué es por dentro y, al final, los
   datos de tu compra -- cantidad, precio y cartera --, que se consultan de
   vez en cuando y no tienen por qué cruzarse por delante de la gráfica.
   Las cuatro casillas de esos datos pasan a ser filas de una lista: son
   etiqueta y valor, que es exactamente lo que es una fila. */
@media (max-width: 767px) {
  .ficha-activo {
    display: flex;
    flex-direction: column;
    gap: 1.75rem;
  }
  .ficha-activo > * {
    margin-top: 0 !important;
    order: 3;
  }
  .ficha-activo > a { order: 0; }                 /* volver */
  .ficha-activo > *:nth-child(2) { order: 1; }    /* cabecera */
  .ficha-activo > .grafico-y-ficha { order: 2; }  /* gráfica + información */
  .ficha-activo > .datos-clave { order: 4; }      /* tu compra, al final */

  /* Las casillas de datos, en filas. */
  .datos-clave {
    grid-template-columns: 1fr;
    gap: 0 !important;
  }
  .datos-clave > * {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding: .3125rem 0 !important;
  }
  .datos-clave > * > p:first-child {
    font-size: .8125rem !important;
    text-transform: none;
    letter-spacing: 0;
  }
  .datos-clave > * > p:not(:first-child) {
    font-size: .8125rem !important;
    font-weight: 600 !important;
    margin-top: 0 !important;
  }
}


/* --- Los buckets recuperan su recuadro -------------------------------------
   Aquí un borde sí gana algo. Son cuatro cosas independientes puestas en una
   rejilla 2x2, cada una con su nombre, su gráfica y su cifra; con solo aire de
   por medio no se sabe si una cifra pertenece al bucket de arriba o al de al
   lado. Es el mismo caso que las categorías de Gastos. Se marca lo justo: un
   borde tenue y un fondo apenas despegado del negro. */
@media (max-width: 767px) {
  .panel-plano .grid-cols-2 > div[class*="rounded-xl"] {
    background: rgb(var(--c-surface) / .45) !important;
    border: 1px solid rgb(var(--c-line)) !important;
    border-radius: 14px !important;
    padding: .75rem !important;
  }
  /* Este negro fijo es SOLO de monocromo (se coló al abrir la hoja a las
     paletas claras, y pintaba las cuatro celdas de negro sobre fondo claro).
     La regla de arriba, con `--c-surface`, es la que vale para todas. */
  html.mono .panel-plano .grid-cols-2 > div[class*="rounded-xl"] {
    background: #0B0B0C !important;
    border-color: #232326 !important;
  }
  /* La línea, con aire por arriba y por abajo: pegada al nombre y a la cifra
     parecía un subrayado del título, no una gráfica. */
  .panel-plano .grid-cols-2 > div > div[style*="height:38px"] {
    margin-top: .5rem !important;
    margin-bottom: .5rem !important;
  }
}


/* --- En móvil no se elige apariencia --------------------------------------
   El modo se fuerza a monocromo, así que dejar los selectores a la vista sería
   mentir: marcas una paleta, se guarda, y al recargar sigue viéndose en negro.
   Se esconden los dos y en su sitio queda una línea que lo explica. Lo que hay
   guardado no se toca: manda en el ordenador. */
@media (max-width: 767px) {
  /* La paleta y el modo vuelven a poder elegirse: claro y oscuro están los dos
     cuidados para móvil. El futurista todavía no, así que esa opción se
     esconde -- ofrecerla sería enseñar algo a medias, y además el forzado la
     devolvería a oscuro al recargar, que es peor que no ofrecerla. */
  #modosUi label:has(input[value="futurista"]) {
    display: none;
  }
  #modosUi {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}


/* --- Ahorros: las cifras, detrás de un botón -------------------------------
   Debajo de la gráfica había tres bloques de cifras (la tasa de ahorro, el
   fondo de emergencia y de dónde recortar) que en un móvil eran tres pantallas
   de desplazamiento antes de llegar a las metas, que es a lo que se entra.
   Ahora arrancan plegados y los abre el botón de la esquina.

   Solo en móvil: en escritorio esas cifras viven en la columna de la derecha,
   al lado de la gráfica, y no estorban a nadie. Por eso la regla vive dentro
   del @media y el botón lleva `md:hidden`. */
@media (max-width: 767px) {
  [data-estadisticas="cerrado"] .bloque-estadistica {
    display: none;
  }
  /* La flecha del botón mira hacia arriba cuando está abierto. */
  [data-estadisticas="abierto"] #btnEstadisticas svg {
    transform: rotate(180deg);
  }
}


/* --- Que una palabra larga no desplace la app de lado ----------------------
   En Ahorros se podía arrastrar la pantalla hacia los lados. La causa: una meta
   con nombre largo. El `h3` lleva `truncate`, pero eso solo funciona si su
   celda puede encogerse, y **un hijo de una rejilla no puede por defecto**: su
   `min-width` vale `auto`, que significa "no bajes de tu contenido mínimo". La
   pista era que la columna medía 433 px dentro de un contenedor de 343.

   `grid-cols-N` de Tailwind ya declara las columnas como `minmax(0, 1fr)`, o
   sea "podéis encogeros hasta cero". Eso manda sobre la PISTA, no sobre el
   elemento que va dentro; esto completa la misma intención en el elemento.

   Va para todas las rejillas de la app y no solo para Ahorros: el fallo no
   tiene nada que ver con esa pantalla, le puede pasar a cualquiera en cuanto
   alguien escriba un nombre largo, y perseguirlo caso por caso es garantizar
   que vuelva. */
@media (max-width: 767px) {
  #contenido [class*="grid-cols-"] > * {
    min-width: 0;
  }
}


/* --- Etiquetas pálidas sobre fondo negro -----------------------------------
   Las píldoras («Extra», la categoría, el estado) llevan fondos de la paleta al
   10% -- pensados para ir sobre blanco. En monocromo el texto pasa a blanco
   pero el fondo sigue siendo ese casi-blanco: blanco sobre blanco, ilegible.
   Aquí la píldora se da la vuelta: fondo oscuro y letra clara. */
@media (max-width: 767px) {
  html.mono [class*="rounded-full"][class*="bg-gain/"],
  html.mono [class*="rounded-full"][class*="bg-loss/"],
  html.mono [class*="rounded-full"][class*="bg-amber/"],
  html.mono [class*="rounded-full"][class*="bg-primary/"],
  html.mono [class*="rounded-full"][class*="bg-navy-100"],
  html.mono [class*="rounded-full"][class*="bg-surface"],
  html.mono [class*="rounded-lg"][class*="bg-navy-100"],
  html.mono [class*="rounded-md"][class*="bg-navy-100"] {
    background: #1A1A1C !important;
    color: #EDEDEF !important;
    border: 1px solid #232326;
  }
}


/* El navegador no debe interpretar el gesto: lo lleva el script. Sin esto, el
   panel se desplazaba también de lado y la hoja parecía suelta. */
@media (max-width: 767px) {
  .hoja-panel {
    touch-action: pan-y;
    overscroll-behavior: contain;
  }
}


/* --- Ficha de un activo: los dos datos de contexto, abajo -------------------
   «Días en cartera» y «Rentabilidad anualizada» se leen una vez y de pasada; no
   tienen por qué separar el nombre del activo de su gráfica, que es lo que se
   viene a ver. Bajan con el resto de datos de tu compra.

   `display: contents` deshace la fila de la cabecera sin tocar el HTML, así
   que sus dos hijos pasan a ser filas de la ficha y se pueden ordenar. En
   escritorio la rejilla se queda como está. */
@media (max-width: 767px) {
  .ficha-activo > div:has(> .cabecera-activo) {
    display: contents;
  }
  .ficha-activo .cabecera-activo { order: 1; }
  .ficha-activo .datos-secundarios { order: 5; }

  /* Y con la misma pinta que las filas de abajo, para que se lean como una
     sola tabla y no como un resto de otra pantalla. */
  .datos-secundarios {
    grid-template-columns: 1fr !important;
    gap: 0 !important;
    padding: 0 !important;
  }
  .datos-secundarios > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding: .3125rem 0;
  }
  .datos-secundarios > div > p:first-child {
    font-size: .8125rem !important;
    text-transform: none;
    letter-spacing: 0;
  }
  .datos-secundarios > div > p:not(:first-child) {
    font-size: .8125rem !important;
    font-weight: 600 !important;
    margin-top: 0 !important;
  }
}


/* La lista de datos de tu compra NO lleva el aire de separar secciones.
   `.datos-clave` es rejilla y cae bajo la regla que da 1.5rem de hueco entre
   secciones, así que sus filas quedaban a 80 px unas de otras: parecían cuatro
   secciones distintas en vez de cuatro renglones de la misma tabla. Aquí se
   dice más alto que son una lista. */
@media (max-width: 767px) {
  .pantalla-plana > .datos-clave {
    gap: 0 !important;
  }
}


/* --- Que el móvil no haga zoom al tocar un campo ---------------------------
   Esto no era «el panel se amplía»: es el navegador. Si la letra de un campo
   mide menos de 16 px, al enfocarlo el móvil acerca la página para que se lea
   -- y al salir NO la devuelve a su sitio. Te quedas con toda la app ampliada y
   tienes que deshacerlo a mano con dos dedos.
   Con 16 px justos no hay motivo para acercar, así que no lo hace.

   ⚠️ 16 px es un SUELO, no un objetivo. Por encima de 16 tampoco hay zoom, así
   que un campo que quiera ser más grande puede serlo: para eso está
   `.campo-grande`. Sin esa salida, esta regla obligaba a que la cifra
   principal de la ficha de un gasto se viera igual que su etiqueta, y ahí el
   importe es justo lo que se viene a mirar. Lo que NO puede haber es un campo
   por DEBAJO de 16. */
@media (max-width: 767px) {
  input:not(.campo-grande),
  select,
  textarea {
    font-size: 16px !important;
  }
}


/* --- Modo claro en móvil: las cajas vuelven --------------------------------
   Sobre negro, dos secciones se separan solas porque cambia la luminosidad, y
   por eso ahí sobran los marcos. Sobre blanco no ocurre: sin superficies, la
   pantalla entera se lee como una hoja continua y las cifras de una sección se
   confunden con las de la siguiente.

   Así que en claro se conservan las tarjetas, pero con el reparto nuevo: el
   mismo orden de secciones, los mismos controles y la misma tipografía. Cambia
   el material, no el plano.

   EXCEPCIÓN, desde el 05/09/2026: el PANEL GENERAL. Ahí se ha ido a lo
   contrario a propósito —cifra grande y gráfica a sangre, sin marco, también en
   claro— porque con tarjetas no parecía una app. El argumento de arriba sigue
   valiendo para el resto de pantallas, así que estas reglas se quedan pero ya
   no hablan de `.panel-plano`. */
@media (max-width: 767px) {
  html:not(.mono):not(.jarvis) .pantalla-plana [class*="rounded-xl"]:not(.hoja-panel) {
    border-radius: 14px;
  }

  /* Las gráficas se quedan DENTRO de su tarjeta. A sangre solo tienen sentido
     sin marco: dentro de una caja, una gráfica que se sale por los lados es un
     desbordamiento, no un recurso. (En el Panel ya no hay caja, y por eso se
     ha quitado de aquí: esta regla le ganaba a la de la sangre y dejaba la
     curva metida en su columna con 16 px muertos a cada lado.) */
  html:not(.mono):not(.jarvis) .pantalla-plana canvas {
    margin-left: 0;
    margin-right: 0;
    width: 100% !important;
  }

  /* El botón flotante, con el acento de la paleta sobre blanco. */
  html:not(.mono):not(.jarvis) .hoja-disparador {
    background: rgb(var(--c-primary));
    color: #fff;
    box-shadow: 0 4px 14px rgb(var(--c-primary) / .35);
  }
}


/* --- Modo claro en móvil: el mismo plano con otro material -----------------
   Todo lo de arriba es reflujo para una pantalla SIN cajas y vive bajo
   `html.mono`. Aquí van las pocas cosas que el modo claro necesita de más,
   porque sí tiene cajas y una caja trae sus propias reglas: relleno por
   dentro, separación por fuera y contraste con lo que hay detrás. */
@media (max-width: 767px) {
  /* 1. La lista de «tu compra» era un cajón por dato: cuatro tarjetas blancas
        seguidas para cuatro cifras cortas. Es UNA lista, así que es UNA
        tarjeta con filas dentro. */
  html:not(.mono):not(.jarvis) .datos-clave,
  html:not(.mono):not(.jarvis) .datos-secundarios {
    background: #fff;
    border: 1px solid rgb(var(--c-line));
    border-radius: 14px;
    padding: .25rem 1rem !important;
  }
  html:not(.mono):not(.jarvis) .datos-clave > *,
  html:not(.mono):not(.jarvis) .datos-secundarios > * {
    background: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    padding: .625rem 0 !important;
  }
  html:not(.mono):not(.jarvis) .datos-clave > * + *,
  html:not(.mono):not(.jarvis) .datos-secundarios > * + * {
    border-top: 1px solid rgb(var(--c-line)) !important;
  }

  /* 2. Las estadísticas del perfil, igual: sueltas sobre blanco quedaban en el
        aire. Una tarjeta con las cinco dentro. */
  html:not(.mono):not(.jarvis) .estadisticas-sueltas {
    background: #fff;
    border: 1px solid rgb(var(--c-line));
    border-radius: 14px;
    padding: 1rem !important;
  }

  /* 3. Aire entre tarjetas: menos que sin ellas. Cuando el marco ya separa, el
        hueco grande solo desparrama la pantalla. */
  html:not(.mono):not(.jarvis) .pantalla-plana > *[class*="flex"],
  html:not(.mono):not(.jarvis) .pantalla-plana > *[class*="grid"] {
    gap: .75rem !important;
  }

  /* 4. La hoja inferior tiene que despegarse del fondo claro. Con el mismo
        blanco que la pantalla de detrás, no parecía que subiera una hoja:
        parecía que el contenido daba un salto. */
  html:not(.mono):not(.jarvis) .hoja-panel {
    box-shadow: 0 -8px 30px rgb(0 0 0 / .18);
  }
  html:not(.mono):not(.jarvis) .hoja-fondo {
    background: rgb(15 23 42 / .35);
  }

  /* 5. Los títulos discretos se apagan un punto menos: sobre blanco, un gris al
        55% se queda por debajo de lo legible. */
  html:not(.mono):not(.jarvis) .pantalla-plana h2,
  html:not(.mono):not(.jarvis) .pantalla-plana h3,
  html:not(.mono):not(.jarvis) .panel-plano p.text-xs.uppercase {
    opacity: .75;
  }
}


/* --- La fila de indicadores, pareja ----------------------------------------
   La primera celda es `bg-navy-900` y las otras seis `bg-white` con borde. En
   claro esa diferencia se lee como jerarquía: la cifra que manda va sobre
   oscuro. En monocromo no: el fondo oscuro se confunde con el de la página y
   la celda se queda sin marco, así que la fila sale con seis tarjetas y un
   hueco al principio.

   Se le devuelve el marco y una superficie propia. Sigue destacando -- su
   texto va en blanco y las demás en gris -- pero pertenece a la fila.
   Solo afecta a escritorio: en móvil esa celda está oculta. */
html.mono .filas-dato > div:first-child {
  background: #141416;
  border: 1px solid #232326;
}


/* --- El escudo que es casi negro -------------------------------------------
   El logo de Interactive Brokers es una marca roja sobre NEGRO, y ese negro es
   parte de la marca: no se le quita. El problema es dónde se pinta el círculo:
   sobre el fondo negro del modo oscuro no se ve el borde y parece que falta el
   logo, y sobre la tarjeta azul marino del carrusel, igual.

   Un aro blanquísimo lo resuelve en los dos sitios a la vez, y sobre fondo
   claro no se nota -- ahí el escudo negro ya se distingue solo, y un aro
   blanco sobre blanco no pinta nada. Por eso no hay que ramificar por modo.

   `box-shadow` y no `border`: el borde metería un píxel HACIA DENTRO y le
   comería un pelo al logo, que ya llega justo al filo. */
.escudo-oscuro {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .22);
}

/* ============ PRUEBA: ESCRITORIO SIN TARJETAS (06/09/2026) ================
   Solo la usa Inversiones, y solo porque la lleva puesta en la plantilla
   (`sin-cajas-pc`). Es la misma idea que ya se hizo en móvil, llevada a la
   pantalla grande: en vez de nueve rectángulos blancos con borde flotando
   sobre un degradado azul, una hoja blanca con las secciones separadas por
   aire y alguna línea fina.

   TODO EL BLOQUE VIVE AQUÍ Y SE QUITA DE UNA PIEZA. Si no convence: se borra
   la clase de investments.html y este bloque, y no queda rastro. Por eso nada
   de esto se ha metido en `pantalla-plana`, que ya la usan seis pantallas.

   Va en un @media de ESCRITORIO: en móvil manda `pantalla-plana`, que ya hace
   lo suyo y no se toca. */
@media (min-width: 768px) {
  /* El fondo deja de ser el degradado azul.
     Con tarjetas, ese degradado es lo que hace que se lean como tarjetas. Sin
     ellas, el contenido queda flotando sobre un azul que no es de nadie: la
     hoja tiene que ser lisa, igual que en el móvil.
     `!important` porque el fondo del body va en un `style=` en línea. */
  body:has(.sin-cajas-pc) {
    background: #fff !important;
  }
  html.mono body:has(.sin-cajas-pc) { background: #000 !important; }

  /* Fuera el cromo de tarjeta. Se buscan las esquinas grandes, igual que en
     móvil: `rounded-xl` y `rounded-2xl` son paneles, y los campos y botones
     usan `rounded-md` y `rounded-full`, así que no entran. */
  .sin-cajas-pc [class*="rounded-xl"],
  .sin-cajas-pc [class*="rounded-2xl"],
  .sin-cajas-pc .hoja-panel {
    background: none !important;
    border-color: transparent !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
  /* Menos lo que de verdad es un control o una superficie con sentido: el
     desplegable de ajustes de la cartera y el formulario de crear una. Un
     recuadro en una pantalla sin recuadros significa "esto se toca". */
  .sin-cajas-pc details,
  .sin-cajas-pc #newPfForm {
    background: rgb(var(--c-surface)) !important;
    border-color: rgb(var(--c-line)) !important;
    border-radius: 12px !important;
    padding-left: .75rem !important;
    padding-right: .75rem !important;
  }

  /* La tarjeta azul del total pierde el fondo, así que su texto blanco se
     queda sin nada debajo. Se le devuelve el color normal, como en el Panel. */
  /* `html:not(.mono):not(.jarvis)`: solo en las paletas CLARAS. En monocromo
     el texto ya tiene que ser blanco -- la hoja es negra -- y sin esta guarda
     la cifra del total salía gris apagada mientras las de al lado eran
     blancas. */
  html:not(.mono):not(.jarvis) .sin-cajas-pc [class*="rounded-xl"][class*="bg-navy-9"] .text-white {
    color: rgb(var(--c-900));
  }
  html:not(.mono):not(.jarvis) .sin-cajas-pc [class*="rounded-xl"][class*="bg-navy-9"] [class*="text-white/"] {
    color: rgb(var(--c-muted));
  }
  /* Y pasa a ser LA cifra de la pantalla: sin caja que lo destaque, lo que
     destaca es el tamaño. */
  .sin-cajas-pc [class*="rounded-xl"][class*="bg-navy-9"] .text-white:last-of-type {
    font-size: 2rem;
    line-height: 1.15;
    letter-spacing: -0.02em;
  }

  /* La tira de indicadores: columnas separadas por una línea fina, que es como
     se lee una fila de cifras cuando no hay cajas. La primera de cada fila no
     lleva línea, o se abre con un palo suelto a la izquierda. */
  .sin-cajas-pc .filas-dato {
    gap: 1.75rem !important;
  }
  .sin-cajas-pc .filas-dato > * {
    border-left: 1px solid rgb(var(--c-line));
    padding-left: 1.75rem !important;
  }
  .sin-cajas-pc .filas-dato > *:nth-child(4n+1) {
    border-left: 0;
    padding-left: 0 !important;
  }

  /* Aire entre secciones, y una línea encima de cada una. Es lo único que hace
     de frontera ahora que no hay bordes. */
  .sin-cajas-pc > * + *:not(.hoja) {
    border-top: 1px solid rgb(var(--c-line));
    padding-top: 2rem;
    margin-top: 2rem !important;
  }
  /* Las gráficas, más separadas entre sí: pegadas y sin marco se leen como una
     sola mancha. */
  .sin-cajas-pc > [class*="grid"] {
    gap: 2.5rem !important;
  }
}

/* A partir de xl la tira de indicadores son SIETE columnas, no cuatro, así que
   la que no lleva línea es otra. Va en su propia consulta porque la regla de
   arriba tiene que seguir valiendo entre 768 y 1279. */
@media (min-width: 1280px) {
  .sin-cajas-pc .filas-dato > *:nth-child(4n+1) {
    border-left: 1px solid rgb(var(--c-line));
    padding-left: 1.75rem !important;
  }
  .sin-cajas-pc .filas-dato > *:nth-child(7n+1) {
    border-left: 0;
    padding-left: 0 !important;
  }
}

/* ---- Ajustes: filas de lista, como las de un teléfono ----

   La pantalla era una rejilla de campos con su etiqueta encima, que es la
   forma de un formulario: seis cajas con borde, un desplegable dentro de cada
   una y aire de sobra alrededor. Se lee como "rellena esto", cuando lo que se
   viene a hacer es MIRAR cómo está algo y a lo sumo tocarlo.

   Una lista de filas dice otra cosa: el nombre a la izquierda, lo que vale
   ahora a la derecha, y el resto es una línea fina. Cabe el triple en la misma
   pantalla y se recorre de un vistazo.

   Va aquí y no en clases de Tailwind sueltas porque son diez filas iguales:
   repetidas a mano, la undécima acaba con dos píxeles de más y no se sabe por
   qué. */
/* ⚠️ `app.css` se carga DESPUÉS de `tailwind.css`, así que a igualdad de
   especificidad gana lo de aquí. `.fila-ajuste { display: flex }` le ganaba a
   `md:hidden` (que es `.md\:hidden { display: none }` dentro de una consulta,
   misma especificidad): en el ordenador salían las filas de Idioma y Moneda
   además de sus listas, y encima muertas -- abrir una hoja fuera del móvil no
   hace nada. Por eso el "solo en móvil" de estas filas es `.solo-movil`, de
   aquí abajo, y no la clase de Tailwind. */
.fila-ajuste {
  display: flex;
  align-items: center;
  gap: .75rem;
  width: 100%;
  min-height: 3rem;
  padding: .7rem 0;
  text-align: left;
}
/* La línea separadora va ENTRE filas, no debajo de cada una: con un
   `border-bottom` la última deja una raya suelta encima del borde de la
   tarjeta y se ven dos líneas juntas. */
.fila-ajuste + .fila-ajuste { border-top: 1px solid rgb(var(--c-line)); }

/* Lo de la derecha: el valor actual. Se encoge antes que la etiqueta, que es
   la que dice de qué va la fila. */
.valor-ajuste {
  margin-left: auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: rgb(var(--c-muted));
  font-size: .875rem;
}

/* Los desplegables que se quedan (cuenta por defecto y tipo de gráfica) dejan
   de parecer un campo de formulario: sin caja, alineados a la derecha y del
   color del valor, como el resto de las filas. Sigue siendo un <select>
   nativo, así que en el móvil abre la rueda del sistema. */
.select-ajuste {
  margin-left: auto;
  max-width: 62%;
  border: 0;
  background: transparent;
  padding: .25rem 0;
  text-align: right;
  /* `text-align` no mueve el texto de la caja cerrada de un <select>; el que lo
     hace es `text-align-last`. Con solo el primero, la etiqueta quedaba a la
     izquierda y el valor pegado a ella por el centro de la fila. */
  text-align-last: right;
  color: rgb(var(--c-muted));
  font-size: .875rem;
}
.select-ajuste:focus { outline: none; color: rgb(var(--c-primary)); }

/* Una fila escondida no puede dejar su línea detrás.
   Idioma y moneda solo se ven en el móvil (en el ordenador su lista se pinta
   entera más abajo). Sin esto, en el ordenador la primera fila que SÍ se ve
   heredaba la separación de una fila invisible y aparecía una raya suelta
   justo debajo del título del bloque. */
@media (min-width: 768px) {
  .solo-movil { display: none; }
  .fila-ajuste.solo-movil + .fila-ajuste { border-top: 0; }
}

/* La línea entre filas de una lista agrupada, cuando la fila no es un
   `.fila-ajuste` (por ejemplo una fila deslizable). Va arriba y no abajo por lo
   mismo: con `border-bottom` la última deja una raya suelta pegada al borde de
   la tarjeta y se ven dos juntas. */
.borde-fila { border-top: 1px solid rgb(var(--c-line)); }

/* El saldo editable de una fila de Cuentas. Deja de parecer un campo de
   formulario -- sin caja, alineado a la derecha y en la misma tipografía de
   cifra que el resto -- pero sigue siéndolo: se toca y se escribe encima. En
   una lista de seis cuentas, seis recuadros con borde son seis cosas que
   compiten con lo único que importa, que es el número. */
.saldo-fila {
  width: 7.5rem;
  border: 0;
  background: transparent;
  text-align: right;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .95rem;
  /* `inherit` y no un color de la paleta: los `<input>` NO heredan el color
     por defecto, y así el saldo sigue al tema (claro, oscuro, monocromo) sin
     tener que acordarse de este sitio al añadir uno nuevo. */
  color: inherit;
  padding: .25rem 0;
}
.saldo-fila:focus {
  outline: none;
  /* Al escribir sí se marca dónde estás: sin ninguna señal, tocar el número y
     que no cambie nada parece que la app no responde. */
  border-bottom: 2px solid rgb(var(--c-primary));
}
/* Las flechitas de subir y bajar de un `number` no pintan nada aquí y roban
   ancho a la cifra. */
.saldo-fila::-webkit-outer-spin-button,
.saldo-fila::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.saldo-fila { -moz-appearance: textfield; }

/* --- La ficha de un gasto: campos que no parecen campos -------------------
   Misma idea que el saldo de Cuentas. Dentro de una lista, un recuadro con
   borde por fila son cinco cosas compitiendo con lo que se viene a leer. El
   campo se marca al escribir, no antes. */
.campo-ficha,
.importe-ficha {
  border: 0;
  background: transparent;
  color: inherit;   /* los `input` no heredan el color: sin esto no siguen al tema */
}
.campo-ficha {
  margin-left: auto;
  min-width: 0;
  max-width: 60%;
  text-align: right;
  font-size: .875rem;
  padding: .25rem 0;
}
.campo-ficha:focus,
.importe-ficha:focus {
  outline: none;
  border-bottom: 2px solid rgb(var(--c-primary));
}
.importe-ficha {
  width: 8rem;
  text-align: left;
  font-size: 2rem;
  font-weight: 600;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.importe-ficha::-webkit-outer-spin-button,
.importe-ficha::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.importe-ficha { -moz-appearance: textfield; }

/* La bandera, dentro de su círculo. El aro finísimo es lo que despega del
   fondo a las que llevan blanco (Japón, Canadá): sin él, la de Japón sobre una
   tarjeta blanca es un punto rojo flotando. */
.circulo-bandera {
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 999px;
  overflow: hidden;
  box-shadow: 0 0 0 1px rgb(var(--c-line));
  display: flex;
}

/* La marca de la opción elegida dentro de la hoja. */
.fila-ajuste[aria-current="true"] .tick-ajuste { visibility: visible; }
.tick-ajuste { visibility: hidden; color: rgb(var(--c-primary)); flex: none; }

/* La flecha del plegable de Apariencia. Es la única fila de Ajustes que no
   abre una hoja -- la paleta y el modo son campos del formulario y no pueden
   salir de él --, así que se despliega en el sitio y la flecha lo cuenta
   girando, como en la lista de un teléfono. */
.flecha-plegable { transition: transform .2s ease; }
.plegable-btn[aria-expanded="true"] .flecha-plegable { transform: rotate(90deg); }

/* ---- Orden de las tarjetas del Panel General, solo en móvil ----

   En el ordenador el Panel es un tablero de dos filas por cuatro columnas y el
   orden ES la composición: moverlo descuadraría las filas. En el móvil todo va
   en una columna, y entonces "qué quiero ver primero al abrir la app" tiene una
   respuesta distinta para cada uno.

   El problema a resolver: las cinco tarjetas que se pueden mover están
   repartidas entre DOS rejillas, y `order` solo reordena dentro de su propio
   contenedor. Por eso en móvil las dos filas se disuelven con `display:
   contents` y sus tarjetas pasan a ser hijas del panel, que aquí es una
   columna flexible. Así las cinco compiten en el mismo sitio.

   ⚠️ `display: contents` NO cambia el árbol del DOM, solo el de cajas, así que
   todas las reglas de arriba que apuntan a `.panel-plano [class*="grid"] > *`
   siguen encontrando las tarjetas igual. Lo que sí desaparece es el `gap` de
   las rejillas: la separación pasa a ponerla el propio panel.

   El `order` va por CLASE y no por un `style=` en línea a propósito: en línea
   se aplicaría también en el ordenador y ahí reordenaría las columnas del
   tablero, que es justo lo que no se quiere. */
@media (max-width: 767px) {
  .panel-plano {
    display: flex;
    flex-direction: column;
    /* El mismo aire que tenían las tarjetas dentro de su fila (`gap-3`). Antes
       entre las dos filas había 1.25rem y dentro 0.75rem; ahora es uniforme,
       que es lo que pide una hoja sin cajas. */
    gap: .75rem;
  }
  .panel-plano > .fila-panel { display: contents; }

  /* El 1 es del Patrimonio Neto y no se reparte: va siempre el primero. Del 2
     al 6 los asigna el servidor con lo que el usuario haya ordenado en Ajustes.
     La guía de primeros pasos no lleva clase, así que se queda en 0 y sale
     por encima de todo, que es donde tiene que estar. */
  .orden-panel-1 { order: 1; }
  .orden-panel-2 { order: 2; }
  .orden-panel-3 { order: 3; }
  .orden-panel-4 { order: 4; }
  .orden-panel-5 { order: 5; }
  .orden-panel-6 { order: 6; }
}

/* ---- Una hoja que también lo es en el ORDENADOR ----

   Por defecto `.hoja` solo es una hoja en el móvil: en escritorio el mismo
   marcado se pinta como un panel más de la página, que es lo que quiere casi
   toda la app. La excepción es el formulario de nueva posición de Inversiones:
   son doce campos que ocupaban media pantalla estando casi siempre sin usar,
   así que ahí se esconde detrás del mismo botón flotante que en el móvil.

   Se marca con `.hoja-pc` y NO se cambia el comportamiento por defecto: el
   resto de pantallas siguen con su panel en su sitio. */
@media (min-width: 768px) {
  .hoja-pc {
    position: fixed;
    inset: 0;
    z-index: 60;
    visibility: hidden;
    transition: visibility 0s linear .26s;
  }
  .hoja-pc.abierta { visibility: visible; transition-delay: 0s; }

  .hoja-pc .hoja-fondo {
    display: block;
    position: absolute;
    inset: 0;
    background: rgba(2, 6, 12, 0.5);
    opacity: 0;
    transition: opacity .25s ease;
  }
  .hoja-pc.abierta .hoja-fondo { opacity: 1; }

  /* En el ordenador no sube desde abajo: aparece centrada, como un diálogo.
     Una hoja pegada al borde inferior de una pantalla de 27 pulgadas queda a
     medio metro del ratón. */
  .hoja-pc .hoja-panel {
    position: absolute;
    left: 50%;
    top: 50%;
    width: min(46rem, calc(100vw - 4rem));
    max-height: 86vh;
    overflow-y: auto;
    transform: translate(-50%, -46%);
    opacity: 0;
    transition: transform .22s cubic-bezier(.2, .8, .25, 1), opacity .18s ease;
  }
  .hoja-pc.abierta .hoja-panel { transform: translate(-50%, -50%); opacity: 1; }

  /* El botón flotante, abajo a la derecha. Aquí no hay barra inferior que
     esquivar, así que se apoya en el borde de la ventana. */
  .hoja-pc-disparador {
    display: flex;
    bottom: 1.75rem;
    right: 1.75rem;
    background: rgb(var(--c-primary));
    color: #fff;
    box-shadow: 0 6px 20px rgb(15 23 42 / .22);
  }
  .hoja-pc-disparador:hover { filter: brightness(1.07); }
}

/* ---- Inversiones en ESCRITORIO: la estructura de la referencia ------------

   La foto que pasó Mauro: a la izquierda una columna con el análisis de la
   cartera (el donut arriba y el rendimiento por tipo debajo), y a la derecha,
   ocupando el doble de ancho, la evolución arriba y la lista de activos
   debajo. Nada más, y nada partido en trozos que no estaban.

   El problema a resolver: esos cuatro paneles NO son hermanos en el HTML --
   están repartidos en dos secciones, y esas secciones son las que dan el orden
   del MÓVIL con `order-*`. Reordenar el HTML habría arreglado el escritorio y
   roto el móvil, que es justo lo que no se puede tocar.

   Por eso las dos secciones se disuelven con `display: contents` y los cuatro
   paneles se colocan a mano en la rejilla del contenedor. `display: contents`
   no cambia el árbol del DOM, solo el de cajas: el móvil no se entera de nada
   porque todas estas reglas viven dentro de un @media de escritorio. Es la
   misma técnica que el orden de las tarjetas del Panel General. */
@media (min-width: 768px) {
  .pantalla-plana:has(.inv-activos) {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: start;
    column-gap: 1.25rem;
    /* Sin hueco entre filas A PROPÓSITO: es lo que deja que el donut y el
       rendimiento se toquen y se lean como un solo cuadrante. El hueco de la
       columna derecha lo pone la evolución con su propio margen. */
    row-gap: 0;
  }
  /* La tira de indicadores y cualquier otra sección suelta ocupan el ancho. */
  .pantalla-plana:has(.inv-activos) > *:not(.zona-inv) {
    grid-column: 1 / -1;
  }
  .pantalla-plana:has(.inv-activos) > .zona-inv { display: contents; }

  /* Las alturas van fijas y CUADRADAS entre columnas -- 21.25 arriba y 27
     abajo -- porque es lo único que quita los huecos: si cada panel mide lo
     que le pide su contenido, la fila la marca el más alto y el otro se queda
     con un pie de página en blanco. Eso era el "hueco enorme" debajo de la
     evolución. Con alturas fijas, lo que sobra se desplaza DENTRO del panel y
     la pantalla entera cabe de una vez. */
  .inv-distribucion { grid-column: 1; grid-row: 2; height: 19.25rem; margin-top: 1.25rem; }
  .inv-rendimiento  { grid-column: 1; grid-row: 3; height: 27rem; }
  .inv-evolucion    { grid-column: 2 / -1; grid-row: 2; height: 18rem; margin-top: 1.25rem; margin-bottom: 1.25rem; }
  .inv-activos      { grid-column: 2 / -1; grid-row: 3; height: 27rem; }
  /* ⚠️ CADA FILA LA MARCA SU PANEL MÁS ALTO, así que las dos columnas tienen
     que sumar lo mismo EN CADA FILA, no solo al final:

       fila 2 -> izquierda 1.25 + 19.25 = 20.5    derecha 1.25 + 18 + 1.25 = 20.5
       fila 3 -> 27 y 27

     Si se sube la costura del cuadrante izquierdo hay que bajar la evolución
     lo mismo; si no, distribución se queda corta dentro de su fila y vuelve a
     aparecer un hueco entre ella y el rendimiento -- que es justo la línea
     fina que Mauro quiere ver pegada. El margen de arriba es el aire que
     separa la tira de indicadores de los paneles, el mismo que hay entre la
     evolución y los activos. */

  /* --- El cuadrante de la izquierda, como una sola tarjeta ---
     Son dos paneles hermanos en el HTML y así se quedan (el móvil los usa
     sueltos y en otro orden). Lo que se quita es la costura: el borde y el
     redondeo de donde se juntan. */
  .inv-distribucion {
    display: flex;
    flex-direction: column;
    border-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }
  .inv-rendimiento {
    display: flex;
    flex-direction: column;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
  /* El donut se adapta al hueco que le queda en vez de imponer 280px. */
  .inv-distribucion > .relative { height: auto; flex: 1; min-height: 0; }
  .inv-distribucion canvas { max-height: 200px; }
  /* La lista de tipos de activo se desplaza dentro de su panel. */
  .inv-rendimiento > .space-y-4 { flex: 1 1 0; overflow-y: auto; min-height: 0; }

  /* --- Los activos, en una caja que se desplaza por dentro ---
     Con veinte posiciones la página se hacía interminable y el resto de
     paneles quedaba a tres pantallas de distancia. La cabecera y las carteras
     se quedan fijas y solo rueda la tabla. */
  .inv-activos { display: flex; flex-direction: column; overflow: hidden; }
  .inv-activos > .tabla-activos { flex: 1 1 0; overflow-y: auto; min-height: 0; }
}

/* ---- La tira de indicadores: un rectángulo largo, no siete tarjetas -------

   En la referencia los números de arriba van en una sola caja blanca separados
   por filetes de un píxel. Es el mismo marcado que en el móvil -- una tarjeta
   con divisiones -- solo que allí las divisiones son horizontales y aquí
   verticales, así que no hay bloque duplicado que se desincronice.

   Los filetes se pintan a mano porque `divide-x` de Tailwind los pondría
   también al principio de la segunda fila cuando la rejilla baja a 4 columnas.
   Con `nth-child` se sabe dónde empieza cada fila. */
@media (min-width: 768px) {
  .tira-kpi > *                    { border-left: 1px solid rgb(var(--c-line)); }
  .tira-kpi > *:nth-child(4n + 1)  { border-left: 0; }
  /* El `:not([hidden])` no filtra nada: está para EMPATAR en especificidad con
     el `divide-y-0` de Tailwind, que se escribe
     `.md\:divide-y-0 > :not([hidden]) ~ :not([hidden])` y por sus dos
     selectores de atributo ganaba a un `nth-child` a secas. Sin esto, la
     segunda fila de indicadores se quedaba sin su filete horizontal. */
  .tira-kpi > *:nth-child(n + 5):not([hidden]) { border-top: 1px solid rgb(var(--c-line)); }
}
@media (min-width: 1280px) {
  /* A 7 columnas cabe todo en una fila: vuelven los filetes de las columnas
     1 y 5, y desaparece el que separaba las dos filas. Misma especificidad que
     las reglas de arriba y más abajo en el archivo, por eso ganan. */
  .tira-kpi > *:nth-child(4n + 1)  { border-left: 1px solid rgb(var(--c-line)); }
  .tira-kpi > *:nth-child(n + 5):not([hidden]) { border-top: 0; }
  .tira-kpi > *:first-child        { border-left: 0; }
}

/* --- Las páginas legales ---------------------------------------------------
   Se leen desde dos sitios: desde Ajustes (con sesión, dentro de la cáscara de
   la app) y desde la landing o el login (sin sesión, como documento suelto).

   `.fondo-app` es el degradado de la app escrito UNA vez y en la hoja, no en
   un atributo `style` de la plantilla. Esa era la causa de que estas dos
   páginas volvieran a claro con el tema oscuro puesto: un fondo en línea gana
   a cualquier regla, así que `html.mono body` no podía apagarlo. Aquí sí. */
.fondo-app {
  background: linear-gradient(160deg, #EAF1FC 0%, #DCE9F9 55%, #E8EDF8 100%);
}

@media (max-width: 767px) {
  /* Sin caja: son diez pantallas de texto seguido, y un marco alrededor de
     algo que no cabe en la pantalla no delimita nada -- solo estrecha la
     columna y obliga a leer en renglones más cortos. */
  html:not(.jarvis) .panel-legal {
    background: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}

/* --- La pantalla se abre desde el botón, como en un iPhone -----------------
   Tocar Ajustes apagaba el contenido a un 35 % y sacaba una rayita de carga
   arriba: se quedaba «cargando». Ahora la pantalla nueva crece desde el punto
   exacto que se ha tocado, que es lo que hace un teléfono.

   ⚠️ Se anima una INSTANTÁNEA (`::view-transition-*`), no `#contenido`.
   Animarlo a él está prohibido y está explicado más arriba en este mismo
   fichero: un `transform` en `#contenido` convierte en `absolute` todo
   `position: fixed` que tenga dentro, y el botón «+» y las hojas inferiores
   dejan de anclarse a la pantalla. Los pseudo-elementos de la transición los
   pinta el navegador por encima de todo y el DOM real ni se entera.

   `transicion-nativa` la pone el JS de `base.html`, y SOLO si es un móvil, el
   navegador sabe hacerlo y el usuario no ha pedido menos movimiento. Si falta
   cualquiera de las tres, no hay nada de esto y queda el fundido de siempre.

   El nombre va en `#contenido` y no en la raíz a propósito: si se anima la
   página entera, la barra de abajo y la cabecera hacen el zoom también, y esas
   dos no se abren -- se quedan. En un iPhone el marco del sistema tampoco se
   mueve cuando abres una app. */
html.transicion-nativa #contenido { view-transition-name: pantalla; }

/* Con la transición nativa puesta sobran los dos efectos viejos: los tres a la
   vez se pisan y lo que se ve es un parpadeo. */
html.transicion-nativa #contenido.saliendo { opacity: 1; transition: none; }
html.transicion-nativa .page-fade-in { animation: none; }

html.transicion-nativa::view-transition-old(pantalla) {
  animation: pantallaSale 160ms ease-in both;
}
html.transicion-nativa::view-transition-new(pantalla) {
  /* El origen lo escribe el JS al tocar (`--abre-x` / `--abre-y`, en píxeles
     desde la esquina de la ventana). El centro es el respaldo: si el toque no
     vino de un botón --teclado, volver atrás-- crecer desde el medio se lee
     como "cambio de pantalla" y no como un fallo. */
  transform-origin: var(--abre-x, 50%) var(--abre-y, 50%);
  animation: pantallaEntra 260ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

@keyframes pantallaSale {
  from { opacity: 1; }
  to   { opacity: 0; }
}
@keyframes pantallaEntra {
  /* 0.92 y no 0.6: el zoom de iOS es corto. Desde muy pequeño parece que la
     pantalla sale despedida, y en una app que se abre veinte veces al día eso
     cansa a la tercera. */
  from { opacity: 0; transform: scale(0.92); }
  to   { opacity: 1; transform: scale(1); }
}

/* --- El rotulo de una seccion ----------------------------------------------
   «Estadisticas», «Inversiones destacadas», «Inversiones recientes»... Antes
   cada seccion ponia lo que le parecia: unas en gris claro y versalitas, otras
   en `text-sm` normal, otras dentro de la caja. Tres estilos para la misma
   cosa hacen que la pagina no tenga jerarquia: el ojo no sabe que es un titulo
   y que es un dato.

   Negro fuerte y con peso, a proposito. Un rotulo de seccion en gris claro
   compite en tono con el contenido que anuncia y se lee como una nota al pie;
   la jerarquia la marca el CONTRASTE, no el tamano.

   Va en `app.css` y no en clases sueltas de Tailwind para poder cambiarlo en
   un sitio el dia que se quiera mover la aguja en toda la app. */
.titulo-seccion {
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.3;
  color: rgb(var(--c-900));
  letter-spacing: -0.01em;
}

/* --- El panel de Ahorro (12/09/2026) ---------------------------------------

   ⚠️ UNA SOLA REJILLA, Y EL ORDEN DEL HTML ES EL DE MÓVIL.
   El móvil manda en el orden del marcado --gráfica, metas, simulador, salud--
   porque es lo que pidió Mauro y porque apilado no hay CSS que lo arregle: lo
   que se lee es el orden en que están escritos. El escritorio NO reordena el
   HTML, coloca cada tarjeta en su celda con `grid-area`, que es justo para lo
   que sirve. Dos filas de rejilla separadas no valían: no se puede mover una
   tarjeta de una a otra.

   ⚠️ VA AQUÍ Y NO EN CLASES DE TAILWIND. El CSS compilado no trae
   `lg:grid-cols-12`, ni `lg:col-span-4`, ni utilidades de `grid-area`: el
   navegador las ignoraría en silencio (ver CONTEXTO.md y
   `tests/test_clases_css.py`). `app.css` se sirve tal cual.

   ⚠️ LAS CUATRO COLUMNAS ARRANCAN EN 1280, NO EN 1024. La maqueta era de 1024
   de ancho Y SIN MENÚ LATERAL; aquí el lateral se lleva 236, así que a 1150 el
   contenido son 860 y la tercera columna se quedaba en 120 px: «Lo que
   apartas» salía como «Lo que ap…». Entre 1024 y 1280 van de dos en dos. */
@media (min-width: 1280px) {
  /* ⚠️ `minmax(0, Nfr)` Y NO `Nfr` A SECAS. Un `1fr` pelado es
     `minmax(auto, 1fr)`: la columna no puede encogerse por debajo del contenido
     más ancho que tenga dentro. Con una gráfica y tres fichas de importes
     dentro, las proporciones salían 342/342/222/194 en vez de 2,3/2/1,3/1,95 y
     los importes se desbordaban de su caja. */
  .panel-ahorro {
    grid-template-columns:
      minmax(0, 2.3fr) minmax(0, 2fr) minmax(0, 1.3fr) minmax(0, 1.95fr);
  }
  .pa-grafica   { grid-area: 1 / 1 / 2 / 2; }
  .pa-simulador { grid-area: 1 / 2 / 2 / 3; }
  .pa-salud     { grid-area: 1 / 3 / 2 / 4; }
  .pa-cifras    { grid-area: 1 / 4 / 2 / 5; }
  .pa-metas     { grid-area: 2 / 1 / 3 / 4; }
  .pa-nueva     { grid-area: 2 / 4 / 3 / 5; }
  .pa-recortes  { grid-area: 3 / 1 / 4 / 4; }
  .pa-liquido   { grid-area: 3 / 4 / 4 / 5; }
}

/* Entre 1024 y 1280 no caben cuatro: dos columnas y en el orden del marcado. */
@media (min-width: 1024px) and (max-width: 1279px) {
  .panel-ahorro { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

/* La flechita de los desplegables, que gira al abrir. */
[aria-expanded="true"] .flecha-despliegue {
  transform: rotate(180deg);
}
.flecha-despliegue {
  transition: transform .18s ease;
}
