/* ============================================================================
   Margen — sistema de diseño
   Fuente única de verdad. Cualquier valor fuera de estas escalas es un error.
   Spec: DESIGN.md · Tablero: marca/marca.html
   ============================================================================ */

/* ── Tokens ──────────────────────────────────────────────────────────────── */
:root {
  /* Color — la interfaz es de tinta; el color pertenece al dinero */
  --bg:         oklch(98.0% 0.004 258);
  --surface:    oklch(100%  0     258);
  --surface-2:  oklch(95.8% 0.006 258);
  --ink:        oklch(20.5% 0.020 258);
  --ink-2:      oklch(48.0% 0.018 258);
  --ink-3:      oklch(62.0% 0.014 258);
  --line:       oklch(90.5% 0.008 258);
  --positivo:   oklch(48.0% 0.150 155);
  --negativo:   oklch(50.5% 0.190 25);
  --proyectado: oklch(52.0% 0.120 75);

  /* Acción primaria.
     Antes el botón era `--ink` sobre `--bg`: tinta pura contra fondo puro, o sea
     el contraste MÁXIMO que da la paleta — 17,9:1 medido. Leído en pantalla era
     una lápida blanca (o negra) que le ganaba a los propios montos, que son el
     dato que importa. Ahora la jerarquía la da el color, no el brillo: un tinte
     del mismo hue 258 de los neutros, así que sigue siendo parte del sistema y
     no un acento nuevo. El texto adentro queda en 9,5:1, muy por encima del
     4,5:1 que pide AA. El borde existe para que el control se despegue del fondo
     ≥3:1 (WCAG 1.4.11): sin él, el relleno solo llega a 1,9:1. */
  --accion:       oklch(92.0% 0.014 258);
  --accion-borde: oklch(61.0% 0.032 258);

  /* Material translúcido de la chrome flotante */
  --material:   oklch(100% 0 258 / 0.72);
  --material-blur: 24px;

  /* Espaciado — ocho pasos, sin intermedios */
  --s1: 0.25rem; --s2: 0.5rem;  --s3: 0.75rem; --s4: 1rem;
  --s5: 1.5rem;  --s6: 2rem;    --s7: 3rem;    --s8: 4rem;

  /* Radios */
  --r-sm: 12px; --r-md: 16px; --r-lg: 22px; --r-pill: 999px;

  /* Tipografía */
  --font: "Schibsted Grotesk", system-ui, -apple-system, sans-serif;
  --mono: "SF Mono", ui-monospace, "JetBrains Mono", Menlo, monospace;

  /* Sombras — la superficie grande lee más gruesa que el chip chico */
  --sombra-card:  0 1px 2px oklch(20% 0.02 258 / 0.05), 0 8px 24px -10px oklch(20% 0.02 258 / 0.12);
  --sombra-sheet: 0 -1px 0 var(--line), 0 -20px 60px -20px oklch(20% 0.02 258 / 0.30);

  /* Alto de la barra de navegación, para que el contenido reserve su lugar */
  --nav-h: 56px;

  /* z-index semántico — nunca valores arbitrarios */
  --z-nav: 100; --z-scrim: 200; --z-sheet: 210; --z-toast: 300;
}

html[data-theme="dark"] {
  --bg:         oklch(17.8% 0.008 258);
  --surface:    oklch(22.5% 0.010 258);
  --surface-2:  oklch(20.5% 0.009 258);
  --ink:        oklch(98.0% 0.004 258);
  --ink-2:      oklch(76.0% 0.012 258);
  --ink-3:      oklch(63.0% 0.012 258);
  --line:       oklch(31.0% 0.012 258);
  --positivo:   oklch(80.0% 0.150 155);
  --negativo:   oklch(72.0% 0.170 25);
  --proyectado: oklch(80.0% 0.120 80);
  --accion:       oklch(38.0% 0.030 258);
  --accion-borde: oklch(56.0% 0.036 258);
  --material:   oklch(22.5% 0.010 258 / 0.72);
  --sombra-card:  0 1px 2px oklch(0% 0 0 / 0.30), 0 8px 24px -10px oklch(0% 0 0 / 0.55);
  --sombra-sheet: 0 -1px 0 var(--line), 0 -20px 60px -20px oklch(0% 0 0 / 0.65);
}

/* ── Reset ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font);
  font-size: 1rem; line-height: 1.45;
  background: var(--bg); color: var(--ink);
  -webkit-font-smoothing: antialiased;
  /* La barra de navegación flota abajo: el contenido reserva su alto + la safe area */
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom) + var(--s5));
  overscroll-behavior-y: none;
}
/* El atributo `hidden` tiene que ganarle a cualquier `display` del sistema.
   La regla del navegador es `[hidden]{display:none}` con especificidad mínima,
   así que un `.btn{display:inline-flex}` la pisaba y el elemento seguía a la
   vista: pasó con el botón de Face ID, el selector de moneda y la leyenda. */
[hidden] { display: none !important; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
table { border-collapse: collapse; width: 100%; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 4px; }

/* ── Tipografía ──────────────────────────────────────────────────────────── */
/* El tracking cambia con el tamaño: negativo en grande, cero en cuerpo, positivo en chico */
.t-display  { font-size: 2.75rem; line-height: 1.05; letter-spacing: -0.022em; font-weight: 600; }
.t-title    { font-size: 1.75rem; line-height: 1.15; letter-spacing: -0.018em; font-weight: 600; }
.t-headline { font-size: 1.25rem; line-height: 1.25; letter-spacing: -0.011em; font-weight: 600; }
.t-body     { font-size: 1rem;    line-height: 1.45; letter-spacing: 0;        font-weight: 400; }
.t-caption  { font-size: 0.8125rem; line-height: 1.35; letter-spacing: 0.006em; font-weight: 500; }
/* 13px es el piso absoluto. No hay paso por debajo. */

.num, .monto {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.montos-ocultos .monto-privado {
  filter: blur(6px);
  user-select: none; pointer-events: none;
  /* Un `overflow:hidden` propio (el de `.panel .val`, puesto para el ellipsis)
     recorta el desenfoque contra la caja del texto y deja un corte recto. Mientras
     está borroso nadie lo va a leer, así que el ellipsis no hace falta. */
  overflow: visible; text-overflow: clip;
}

/* ── Layout ──────────────────────────────────────────────────────────────── */
.contenedor {
  width: 100%; max-width: 1100px; margin-inline: auto;
  padding-inline: max(var(--s4), env(safe-area-inset-left), env(safe-area-inset-right));
}
.pila       { display: flex; flex-direction: column; gap: var(--s4); }
.pila-sm    { display: flex; flex-direction: column; gap: var(--s2); }
.seccion    { margin-top: var(--s6); }
/* Nada de eyebrows en versalitas trackeadas: es el andamio genérico por defecto.
   El título de sección es texto normal que dice algo útil. */
.seccion-tit {
  font-size: 0.8125rem; letter-spacing: 0.006em;
  font-weight: 500; color: var(--ink-3); margin-bottom: var(--s3);
}

/* ── Barra de navegación — translúcida, abajo en mobile ──────────────────── */
.nav {
  position: fixed; z-index: var(--z-nav);
  left: 0; right: 0; bottom: 0;
  height: calc(var(--nav-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: var(--material);
  -webkit-backdrop-filter: blur(var(--material-blur)) saturate(180%);
  backdrop-filter: blur(var(--material-blur)) saturate(180%);
  border-top: 1px solid var(--line);
}
.nav a {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; min-height: 44px; text-decoration: none;
  /* 13px = el piso del sistema. iOS usa 10-11pt en su tab bar, pero acá manda
     la escala propia: la etiqueta entra cómoda en 430/4 y se lee de verdad. */
  color: var(--ink-3); font-size: 0.8125rem; letter-spacing: 0.006em; font-weight: 500;
}
.nav a[aria-current="page"] { color: var(--ink); }
.nav a svg { width: 22px; height: 22px; stroke-width: 1.75; }

/* Lo primero después de la barra necesita aire: pegado al encabezado se lee
   como si fuera parte de él. */
.barra + .contenedor > :first-child,
.barra + main > :first-child { margin-top: var(--s4); }

/* Encabezado de pantalla: el mes y las acciones, también translúcido */
.barra {
  position: sticky; top: 0; z-index: calc(var(--z-nav) - 1);
  display: flex; align-items: center; gap: var(--s2);
  padding: max(var(--s3), env(safe-area-inset-top)) 0 var(--s3);
  background: var(--material);
  -webkit-backdrop-filter: blur(var(--material-blur)) saturate(180%);
  backdrop-filter: blur(var(--material-blur)) saturate(180%);
}
.barra .mes { flex: 1; min-width: 0; }
/* Mes y año en la misma línea. La jerarquía la da el PESO y el color, no el
   tamaño: dos tamaños distintos parten en dos renglones un encabezado que son
   dos palabras. El año reusa la misma clase que el mes — la escala tipográfica
   es la fuente de verdad y no se inventan valores acá — y solo se le baja el peso.

   El encabezado bajó de `t-title` (28px) a `t-headline` (20px), medido: aun
   después de mudar dos botones al menú, "Septiembre 2026" necesita 254px a 28px
   y el contenedor da 187px en un iPhone SE. A 20px entra en los tres anchos con
   margen. De paso la jerarquía mejora: el mes es contexto, el número grande de
   abajo es la respuesta.
   `.en-linea` se aplica únicamente cuando el subtítulo ES el año: en Tarjeta a
   veces dice "desde agosto", que sí es secundario y va abajo y más chico. */
.barra .mes.en-linea { display: flex; align-items: baseline; gap: 0.4rem; }
.barra .mes.en-linea #mes-tit { flex: 0 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.barra .anio { font-weight: 400; color: var(--ink-3); flex: 0 0 auto; }

/* ── Objetivo táctil: 44px es el piso ────────────────────────────────────── */
.tap {
  min-width: 44px; min-height: 44px;
  display: inline-grid; place-items: center;
  border-radius: var(--r-pill); color: var(--ink-2);
  -webkit-tap-highlight-color: transparent;
}
.tap svg { width: 20px; height: 20px; stroke-width: 1.75; }
.tap:active { background: var(--surface-2); }

/* ── Botones — tinta, nunca color ────────────────────────────────────────── */
.btn {
  min-height: 48px; padding-inline: var(--s5);
  border-radius: var(--r-pill);
  background: var(--accion); color: var(--ink); border: 1px solid var(--accion-borde);
  font-weight: 600; font-size: 1rem; letter-spacing: -0.006em;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  -webkit-tap-highlight-color: transparent;
}
.btn-sec { background: transparent; color: var(--ink); border: 1px solid var(--line); }
/* El fantasma no lleva borde. Va explícito porque ahora `.btn` sí trae uno y,
   sin esto, heredaba el de la acción primaria y dejaba de ser fantasma. */
.btn-fantasma { background: transparent; color: var(--ink-2); border-color: transparent; }
.btn:active, .tap:active, .fila:active { transform: scale(0.97); }
.btn, .tap, .fila, .chip { transition: transform 100ms ease-out; }

/* ── Chips de filtro ─────────────────────────────────────────────────────────
   Envuelven en varias líneas en vez de deslizarse. Una tira horizontal escondía
   opciones enteras fuera de pantalla —"Elegir…" no se veía nunca— y encima se
   peleaba con el swipe de mes. Envolver no esconde nada y deja la app sin un
   solo scroller horizontal. */
.chips { display: flex; gap: var(--s2); padding-block: var(--s1); }
.chip {
  /* Se reparten el ancho en una sola línea. Envolver a dos líneas empujaba todo
     hacia abajo, y deslizarse escondía opciones enteras. */
  flex: 1 1 0; min-width: 0;
  /* Reparto por igual: el padding lateral solo le roba lugar al texto, y con
     cinco opciones "3 meses" se cortaba por un pixel. */
  min-height: 44px; padding-inline: var(--s1);
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill); border: 1px solid var(--line);
  color: var(--ink-2); font-size: 0.875rem; font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  -webkit-tap-highlight-color: transparent;
}
/* El chip activo usa el mismo tinte que la acción primaria: es la otra cosa que
   "está prendida" en la pantalla, y antes también iba a contraste máximo. */
.chip[aria-pressed="true"] { background: var(--accion); color: var(--ink); border-color: var(--accion-borde); }

/* ── Hero — la única pregunta de la pantalla ─────────────────────────────── */
.hero {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: var(--s5);
  box-shadow: var(--sombra-card);
}
.hero .etq { color: var(--ink-2); }
.hero .cifra {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 2.75rem; line-height: 1.05; letter-spacing: -0.022em; font-weight: 600;
  margin: var(--s2) 0 var(--s1);
}
.hero .cifra.pos { color: var(--positivo); }
.hero .cifra.neg { color: var(--negativo); }
/* Sin confirmar: el número es una proyección nuestra, no el estado de cuenta */
.hero .cifra.proy { color: var(--proyectado); }
.hero .cifra .usd { display: block; font-size: 1.25rem; letter-spacing: -0.011em;
  color: var(--ink-2); margin-top: var(--s1); }
.hero .sub { color: var(--ink-3); font-family: var(--mono); font-size: 0.8125rem; }
.hero .sub.urgente { color: var(--negativo); font-weight: 600; }

/* KPIs secundarios: UN panel con todo a la vista.
   Antes era una tira que se deslizaba: las tarjetas que salían de pantalla no se
   veían nunca, porque el swipe de mes se comía el gesto. Y aunque no se lo comiera,
   una tarjeta cortada es una señal débil. Cuatro datos, una superficie, cero scroll. */
.panel {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden;
  display: grid; grid-template-columns: 1fr 1fr;
}
.panel .dato { padding: var(--s4); border-bottom: 1px solid var(--line); min-width: 0; }
.panel .dato:nth-child(odd) { border-right: 1px solid var(--line); }
/* Sin borde de abajo a los de la ÚLTIMA fila. Con dos columnas, la última fila
   son dos elementos si el total es par, y uno solo si es impar. La regla vieja
   (`:nth-last-child(-n+2)`) daba por hecho que siempre eran dos: con 3 KPIs le
   sacaba el borde al segundo, que está en la primera fila, y la línea quedaba
   dibujada a medias. Para los totales pares el resultado es idéntico. */
.panel .dato:last-child,
.panel .dato:nth-last-child(2):nth-child(odd) { border-bottom: 0; }
.panel .dato:only-child, .panel .dato:nth-child(odd):last-child { border-right: 0; }
.panel .etq { color: var(--ink-3); }
/* La línea chica del KPI recorta con ellipsis. Va acá y no en estilo inline: el
   inline le ganaba a la regla de `montos-ocultos`, que necesita overflow visible
   para que el desenfoque no quede cortado de plano. */
.panel .dato .sub { color: var(--ink-3); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Saldo arrastrado de un mes anterior. Va con `--proyectado` y no con un color
   nuevo porque significa lo mismo que en el resto del sistema: un número que
   todavía no es firme. No usa `--negativo`, que está reservado para lo que está
   mal o vencido — un saldo viejo no está mal, está viejo.
   Va DESPUÉS de `.panel .dato .sub` a propósito: misma especificidad, así que
   el que pierde es el que está más arriba. Puesto antes, no pintaba nada. */
.panel .dato .sub.sub-viejo { color: var(--proyectado); font-weight: 500; }
.panel .val { font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 1.25rem; font-weight: 600; letter-spacing: -0.011em; margin-top: var(--s1);
  overflow: hidden; text-overflow: ellipsis; }
@media (min-width: 768px) { .panel { grid-template-columns: repeat(4, 1fr); }
  .panel .dato { border-bottom: 0; } .panel .dato:last-child { border-right: 0; } }

/* ── Segmentos — dos marcos de tiempo, no cuatro pestañas ────────────────────
   La pantalla de tarjeta responde dos preguntas distintas: qué pago ahora y qué
   me espera. Cuatro pestañas obligaban a recordar dónde estaba cada cosa; dos
   segmentos se leen enteros de una. El indicador se mueve con spring. */
.segmentos {
  position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-pill); padding: 3px;
}
.segmentos .ind {
  position: absolute; top: 3px; bottom: 3px; left: 3px;
  border-radius: var(--r-pill); background: var(--surface);
  box-shadow: 0 1px 3px oklch(20% 0.02 258 / 0.14);
  z-index: 0; pointer-events: none;
}
.segmentos button {
  position: relative; z-index: 1; min-height: 44px; /* el piso táctil manda */
  font-size: 0.9375rem; font-weight: 500; color: var(--ink-2);
  border-radius: var(--r-pill); -webkit-tap-highlight-color: transparent;
}
.segmentos button[aria-selected="true"] { color: var(--ink); font-weight: 600; }

/* ── Barras de proyección — una tabla de 12 filas no se lee en un pulgar ──── */
.barras { display: flex; flex-direction: column; gap: var(--s3); }
.barra-mes { display: grid; grid-template-columns: 4.25rem 1fr auto; gap: var(--s3);
  align-items: center; }
.barra-mes .m-nom { font-size: 0.8125rem; letter-spacing: 0.006em; color: var(--ink-3); }
.barra-mes.foco .m-nom { color: var(--ink); font-weight: 600; }
.barra-mes .pista { height: 8px; border-radius: var(--r-pill);
  background: var(--surface-2); overflow: hidden; }
/* display:block no es decorativo: un <i> inline ignora width y height, y la barra
   se renderiza con ancho cero aunque el estilo diga 65%. */
.barra-mes .relleno { display: block; height: 100%; border-radius: var(--r-pill);
  background: var(--ink-2); transform-origin: left center; }
.barra-mes.foco .relleno { background: var(--ink); }
.barra-mes .m-val { font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 0.8125rem; font-weight: 600; }

/* Real vs estimado en la misma barra: el relleno es lo gastado, la marca es el
   presupuesto y el exceso se pinta con el color de "te falta". Un gráfico de
   líneas no dice esto de un vistazo en un pulgar. */
.barra-mes .pista { position: relative; }
.barra-mes .exceso { position: absolute; top: 0; bottom: 0; display: block;
  background: var(--negativo); border-radius: 0 var(--r-pill) var(--r-pill) 0; }
.barra-mes .marca { position: absolute; top: -3px; bottom: -3px; width: 2px;
  background: var(--ink-3); border-radius: 1px; }

/* ── Barras por categoría — el nombre no entra en una columna fija ───────── */
.barra-cat { padding: var(--s3) 0; }
.barra-cat + .barra-cat { border-top: 1px solid var(--line); }
.barra-cat .cab { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s3); margin-bottom: var(--s2); }
.barra-cat .nom { font-size: 1rem; font-weight: 500; letter-spacing: -0.006em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.barra-cat .val { font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; flex-shrink: 0; }
.barra-cat .pista { position: relative; height: 8px; border-radius: var(--r-pill);
  background: var(--surface-2); overflow: visible; }
.barra-cat .relleno { display: block; height: 100%; border-radius: var(--r-pill);
  background: var(--ink-2); transform-origin: left center; }
.barra-cat .exceso { position: absolute; top: 0; bottom: 0; display: block;
  background: var(--negativo); border-radius: 0 var(--r-pill) var(--r-pill) 0; }
.barra-cat .marca { position: absolute; top: -3px; bottom: -3px; width: 2px;
  background: var(--ink-3); border-radius: 1px; }
.barra-cat .pie { font-size: 0.8125rem; line-height: 1.35; letter-spacing: 0.006em;
  color: var(--ink-3); margin-top: var(--s2); }

/* Leyenda: sin esto, la marca vertical es un adorno que nadie descifra */
.leyenda { display: flex; flex-wrap: wrap; gap: var(--s4); margin-bottom: var(--s3);
  font-size: 0.8125rem; letter-spacing: 0.006em; color: var(--ink-3); }
.leyenda span { display: inline-flex; align-items: center; gap: 6px; }
.leyenda i { width: 14px; height: 8px; border-radius: var(--r-pill); flex-shrink: 0; }
.leyenda .l-real   { background: var(--ink-2); }
.leyenda .l-exceso { background: var(--negativo); }
.leyenda .l-marca  { width: 2px; height: 12px; background: var(--ink-3); border-radius: 1px; }

/* ── Progreso de cuotas — cuánto de la compra ya está pago ───────────────── */
.progreso { height: 4px; border-radius: var(--r-pill); background: var(--surface-2);
  overflow: hidden; margin-top: 6px; max-width: 140px; }
.progreso i { display: block; height: 100%; background: var(--ink-3);
  border-radius: var(--r-pill); }

/* ── Vacío — dice qué falta, no "no hay datos" ───────────────────────────── */
.vacio { padding: var(--s5) var(--s4); text-align: center; color: var(--ink-3);
  font-size: 0.9375rem; }

/* ── Fila de gasto — dos niveles ─────────────────────────────────────────── */
.lista { background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden; }
.fila {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); min-height: 64px;
  border-bottom: 1px solid var(--line);
  -webkit-tap-highlight-color: transparent;
}
.fila:last-child { border-bottom: 0; }
.fila .desc { flex: 1; min-width: 0; }
.fila .d1 { font-size: 1rem; font-weight: 500; letter-spacing: -0.006em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fila .d2 { font-size: 0.8125rem; line-height: 1.35; letter-spacing: 0.006em;
  color: var(--ink-3); margin-top: 1px; }
.fila .der { text-align: right; flex-shrink: 0; }
.fila .m { font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; }
.fila.pagada .m { color: var(--ink-3); }

/* ── Estado — punto + palabra, legible en escala de grises ───────────────── */
.est {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 0.8125rem; line-height: 1.35; letter-spacing: 0.006em; font-weight: 500;
  margin-top: 2px;
}
.est::before { content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; flex-shrink: 0; }
/* Cuatro estados, cuatro lecturas. La regla es la del sistema: el color dice algo
   del dinero. Verde = la plata ya salió. Ámbar = todavía no está confirmado, el
   número puede cambiar. Rojo = vence o te pasaste. Tinta plena = firme y sin pagar. */
.est-pagado     { color: var(--positivo); }
.est-confirmado { color: var(--ink); }
.est-parcial    { color: var(--ink-2); }
.est-proyectado { color: var(--proyectado); }
.est-vence      { color: var(--negativo); }
.est-neutro     { color: var(--ink-3); }

/* Un vencimiento que puso el estimador, no una factura: se marca en la propia
   línea de contexto para no tener que abrir la fila y adivinar. */
.fila .d2 .proy { color: var(--proyectado); }

/* ── Hoja arrastrable — reemplaza al modal en mobile ─────────────────────── */
.scrim {
  position: fixed; inset: 0; z-index: var(--z-scrim);
  /* Visible por defecto: el scrim bloquea la interacción de atrás. Si el motion
     no carga, tiene que seguir cumpliendo esa función. La animación lo suaviza. */
  background: oklch(15% 0.01 258 / 0.45); opacity: 1;
}
.hoja {
  position: fixed; z-index: var(--z-sheet); left: 0; right: 0; bottom: 0;
  max-height: 92dvh; overflow-y: auto;
  background: var(--surface);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--sombra-sheet);
  padding: var(--s3) var(--s5) calc(var(--s6) + env(safe-area-inset-bottom));
  /* `pan-y`, no `none`. Con `none` el navegador no scrollea NADA dentro de la
     hoja, y una hoja más alta que la pantalla —la de Saldo con la lista de
     cuentas— quedaba inalcanzable en el teléfono: no había forma de llegar al
     campo del monto. Con `pan-y` el scroll nativo funciona, y el arrastre para
     cerrar sigue andando porque `rastrearGesto` no se activa si la hoja ya está
     scrolleada (ver `enScrollerV` en app.js). */
  touch-action: pan-y;
  overscroll-behavior: contain;
}
.hoja .agarre { width: 40px; height: 4px; border-radius: 2px; background: var(--line);
  margin: 0 auto var(--s4); }
@media (min-width: 768px) {
  .hoja { left: 50%; right: auto; bottom: auto; top: 50%; width: min(520px, 92vw);
    /* `translate` y NO `transform`.
       El centrado tiene que convivir con el arrastre: `hojaArrastrable()` y las
       animaciones de entrada y salida escriben `hoja.style.transform` en cada
       cuadro, y un `transform` inline pisa entero al del CSS — incluido el
       `-50%, -50%`. Sin él la hoja quedaba con su esquina superior izquierda
       clavada en (50vw, 50vh) y todo lo de abajo fuera de la ventana: en la hoja
       de Editar, que es la más alta, el botón de eliminar caía ~220px por debajo
       del borde. Y no había forma de llegar — es `position: fixed`, así que el
       scroll de la página no la mueve, y su contenido entra en su propia caja,
       así que tampoco scrollea por dentro.
       `translate` es una propiedad independiente y se COMPONE con `transform` en
       vez de competir: primero centra, después el arrastre desplaza. */
    translate: -50% -50%;
    /* Con la hoja centrada, 92dvh deja 4dvh arriba y 4dvh abajo. Si el contenido
       pasa de ahí, el `overflow-y: auto` de la regla base hace el resto. */
    max-height: 92dvh;
    border-radius: var(--r-lg); }
  .hoja .agarre { display: none; }
}

/* ── Campos ──────────────────────────────────────────────────────────────── */
.campo { display: flex; flex-direction: column; gap: var(--s2); }
.campo label { font-size: 0.8125rem; font-weight: 500; color: var(--ink-2);
  letter-spacing: 0.006em; }
.campo input, .campo select {
  /* `height` fija, no `min-height`: iOS le da al select su propia altura y quedaba
     más bajo que los input de al lado. */
  height: 48px; padding-inline: var(--s4);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--ink);
  font-size: 1rem; /* 16px: por debajo, iOS hace zoom al enfocar */
  width: 100%; font-family: inherit;
}
.campo select {
  -webkit-appearance: none; appearance: none;
  padding-right: calc(var(--s4) + 18px);
  /* Flecha propia: al sacarle la apariencia nativa hay que reponerla */
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right var(--s4) center, calc(100% - var(--s4) + 5px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.campo input:focus, .campo select:focus { border-color: var(--ink); outline: none; }
.campo input[type="month"] { -webkit-appearance: none; appearance: none; }

/* ── Skeleton — nunca un spinner ─────────────────────────────────────────── */
.skel { background: var(--surface-2); border-radius: var(--r-sm);
  animation: pulso 1.4s ease-in-out infinite; }
@keyframes pulso { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }

/* ── Desktop: la navegación sube, el contenido respira ───────────────────── */
@media (min-width: 768px) {
  :root { --nav-h: 60px; }
  body { padding-bottom: var(--s7); padding-top: calc(var(--nav-h) + var(--s4)); }
  .nav { top: 0; bottom: auto; padding-bottom: 0; height: var(--nav-h);
    border-top: 0; border-bottom: 1px solid var(--line);
    grid-auto-flow: column; grid-auto-columns: max-content;
    justify-content: center; gap: var(--s5); }
  .nav a { flex-direction: row; gap: var(--s2); font-size: 0.9375rem; padding-inline: var(--s3); }
  .barra { top: var(--nav-h); }
  .kpi { min-width: 200px; }
  .t-display, .hero .cifra { font-size: 3.5rem; }
}

/* ── Accesibilidad: las tres señales del sistema ─────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
    transition-duration: 200ms !important;
  }
  .btn:active, .tap:active, .fila:active { transform: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .nav, .barra { background: var(--bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
@media (prefers-contrast: more) {
  :root { --line: oklch(70% 0.01 258); --ink-3: var(--ink-2); }
  html[data-theme="dark"] { --line: oklch(50% 0.012 258); }
  .kpi, .hero, .lista, .chip { border-width: 2px; }
}

/* El período del encabezado es tocable: abre el rango a medida. Se toca lo que
   se quiere cambiar, en vez de esconderlo en un chip al final de una tira. */
.periodo-btn {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 44px; margin-top: -10px; margin-bottom: -10px;
  color: var(--ink-3); font-weight: 500; text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.periodo-btn::after { content: ""; width: 6px; height: 6px; flex-shrink: 0;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px); }
.periodo-btn:active { color: var(--ink-2); }

/* Una factura anulada sigue en la lista (es historia), pero no compite por la
   atención: se lee en gris y el monto tachado dice que ese dinero no existió. */
.fila.anulada .d1 { color: var(--ink-2); }
.fila.anulada .m { color: var(--ink-3); text-decoration: line-through; }
/* La segunda línea también recorta: sin esto, un concepto largo estira la fila */
.fila .d2 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Hoja de acciones — lo que se puede hacer con una fila ────────────────── */
.acciones { display: flex; flex-direction: column; margin-top: var(--s4);
  border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.acciones button {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  width: 100%; min-height: 56px; padding: var(--s3) var(--s4); text-align: left;
  border-bottom: 1px solid var(--line); background: var(--surface);
  -webkit-tap-highlight-color: transparent;
}
.acciones button:last-child { border-bottom: 0; }
.acciones button:active { background: var(--surface-2); }
.acciones .a1 { font-size: 1rem; font-weight: 500; letter-spacing: -0.006em; }
.acciones .a2 { font-size: 0.8125rem; line-height: 1.35; letter-spacing: 0.006em;
  color: var(--ink-3); }
.acciones button.peligro .a1 { color: var(--negativo); }

/* ── Entrada — la única pantalla sin navegación ──────────────────────────── */
body.sin-nav { padding-bottom: 0; }
.entrada {
  min-height: 100dvh; display: grid; place-items: center;
  padding: max(var(--s5), env(safe-area-inset-top))
           max(var(--s5), env(safe-area-inset-left))
           max(var(--s5), env(safe-area-inset-bottom));
}
.entrada-caja { width: 100%; max-width: 380px; }
/* El símbolo es la regla del margen: nunca en un color que no sea la tinta */
.entrada .marca { width: 40px; height: 40px; color: var(--ink); margin-bottom: var(--s4); }
.entrada .est { margin-top: 0; }
.oferta {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s5); margin-top: var(--s4);
}

/* ── Cerrojo de apertura ─────────────────────────────────────────────────────
   Tapa la app entera hasta que pase el Face ID. Se pinta desde el HTML, antes de
   que cargue el JS: si apareciera después, los montos quedarían un instante a la
   vista, que es justo lo que se quiere evitar. */
#cerrojo {
  position: fixed; inset: 0; z-index: 400;
  display: grid; place-items: center;
  background: var(--bg);
  padding: max(var(--s5), env(safe-area-inset-top)) var(--s5);
}
#cerrojo .caja { width: 100%; max-width: 320px; text-align: center; }
#cerrojo form { text-align: left; }
#cerrojo .marca { width: 40px; height: 40px; color: var(--ink); margin-bottom: var(--s4); }
#cerrojo .btn { width: 100%; margin-top: var(--s5); }
#cerrojo .btn-fantasma { width: 100%; min-height: 44px; margin-top: var(--s2); }
#cerrojo .est { justify-content: center; margin-top: var(--s4); }
/* Mientras el cerrojo está puesto, nada de atrás se ve ni se puede tocar */
body.bloqueada > *:not(#cerrojo) { visibility: hidden; }

/* El selector de orden vive al lado del título de la lista: cambia lo que se ve
   justo abajo, así que va ahí y no en una barra aparte. */
.orden-btn {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 44px; margin-top: -12px; padding-inline: var(--s2);
  color: var(--ink-2); font-weight: 500; flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
}
.orden-btn::after { content: ""; width: 5px; height: 5px; flex-shrink: 0;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px); }
.orden-btn:active { color: var(--ink); }
#cerrojo .campo label { font-size: 0.8125rem; }
#cerrojo form .btn { margin-top: var(--s4); }
