/* ============================================================================
   Samapp 1.0 — Base y componentes
   ----------------------------------------------------------------------------
   Sustituye por completo a app.css de 0.1.5 (5.210 líneas). La reducción no
   viene de comprimir: viene de que ya no hay tarjetas anidadas, ni gradientes,
   ni variantes de un mismo control repetidas por pantalla.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  /* 1.32.1 — SIN ESTO, DESLIZAR SACA AL USUARIO DE LA APP.
     Reproducido con toques reales: un gesto horizontal que la página no
     consume se lo queda el navegador como "atrás/adelante", y en la prueba el
     documento entero desaparecía a media pulsación (body sin data-view, UI sin
     definir). No es un problema del gesto nuevo: es que hasta ahora ningún
     gesto horizontal llegaba tan lejos y nadie lo había pisado.
     `contain` corta la cadena en la app: dentro se sigue pudiendo desplazar lo
     que tenga su propio scroll (carruseles, tiras de fichas), pero el
     desbordamiento ya no se convierte en navegación del navegador. */
  overscroll-behavior-x: contain;
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
}

h1, h2, h3 { margin: 0; font-weight: 600; }
p { margin: 0; }
button { font: inherit; color: inherit; }

/* Foco visible siempre. Nunca se elimina el outline sin sustituirlo. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ===========================================================================
   TIPOGRAFÍA DE PANTALLA
   El serif aparece solo en el título de pantalla y en el saludo. Es el único
   gesto tipográfico con personalidad; todo lo demás es la pila de sistema.
   Usarlo en más sitios lo convertiría en decoración.
   ========================================================================= */
.screen-title {
  font-family: var(--font-display);
  font-size: var(--t-display);
  /* 1.9.1 — Quicksand: 600 es su punto dulce en títulos (700 engorda las
     curvas); las geométricas redondeadas no quieren tracking apretado. */
  font-weight: 600;
  letter-spacing: -.008em;
  line-height: var(--lh-tight);
  color: var(--ink-strong);
}

.greeting {
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 600;
  letter-spacing: -.008em;
  color: var(--ink-strong);
}

.screen-sub {
  color: var(--ink-muted);
  font-size: var(--t-meta);
  margin-top: var(--s1);
}

.section-label {
  font-size: var(--t-meta);
  font-weight: 600;
  color: var(--ink);
  margin: var(--s6) 0 var(--s2);
}
.section-label:first-child { margin-top: 0; }

/* ===========================================================================
   ESTRUCTURA
   ========================================================================= */
.app {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

.appbar {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: calc(var(--safe-top) + var(--s3)) var(--s5) var(--s2);
}

.appbar-brand {
  display: flex;
  align-items: center;
  /* 2.1.1 — Con la letra del sistema al 200% la marca crecía hasta empujar los
     tres botones de la barra fuera de la pantalla (medido a 320px: el último
     acababa en x=384). La marca cede sitio; los botones no, que son los que se
     tocan. */
  min-width: 0;
  gap: var(--s2);
  font-family: var(--font-display);
  font-size: 1.1875rem;
  font-weight: 500;
  color: var(--ink-strong);
}
.appbar-brand img { width: 28px; height: 28px; flex: 0 0 auto; }
.appbar-brand > span:not(.sam-mark) { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appbar-brand .sam-mark { flex: 0 0 auto; }
.appbar-spacer { flex: 1; }

/* 40px: por debajo de eso el objetivo táctil se vuelve incómodo en móvil.
   Detectado por la prueba automática de altura mínima. */
.avatar {
  width: var(--tap); height: var(--tap);
  /* 2.1.1 — Los iconos de la barra superior medían 41x44 a 320px: eran 44 de
     ancho hasta que el flex de la cabecera los encogía. */
  min-width: var(--tap); flex-shrink: 0;
  border-radius: var(--r-full);
  border: 0;
  background: var(--action);
  color: var(--action-ink);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: .02em;
  cursor: pointer;
  display: grid;
  place-items: center;
}

/* El scroll lo lleva la página, no este contenedor.
   `.app` usa min-height, así que `.content` nunca quedaba limitado en alto y
   su `overflow-y: auto` no llegaba a activarse jamás: era una declaración
   muerta que además hacía creer que el contenedor scrolleaba. En móvil el
   scroll de documento se lleva mejor con la barra dinámica de Safari. */
.content {
  flex: 1;
  padding: var(--s2) var(--s5) calc(var(--tabbar-h) + var(--safe-bottom) + var(--s6));
}

/* ===========================================================================
   FILA — el componente que carga toda la app
   ----------------------------------------------------------------------------
   Pendientes, items de lista, movimientos y eventos son la misma fila con
   distinto contenido. Una sola superficie, un solo borde, sin anidar.
   ========================================================================= */
.row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--tap);
  padding: var(--s3) var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  transition: background var(--dur) var(--ease);
}
.row + .row { margin-top: var(--s2); }
.row:hover { background: var(--surface-hover); }
.row-link:active { background: var(--surface-hover); }

/* El tinte de área es OPT-IN, no automático.
   Teñir todas las filas convertía Pendientes en un semáforo y rompía la regla
   90% neutro / 10% color. El tinte solo se gana en vistas de agenda —donde
   agrupa bloques de tiempo de un vistazo— y en filas de atención.
   En listas y pendientes el color vive únicamente en el nombre del área. */
.row.tinted[data-ctx]:not([data-ctx="neutro"]) {
  background: var(--ctx-tint);
  border-color: transparent;
}

.row-body { flex: 1; min-width: 0; }

.row-title {
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.row-meta {
  font-size: var(--t-meta);
  color: var(--ink-muted);
  margin-top: 1px;
}
/* El nombre del área hereda su color; el resto del metadato no. */
.row-meta .ctx { color: var(--ctx, var(--ink-muted)); font-weight: 500; }

.row-amount {
  font-family: var(--font-num);
  font-size: var(--t-body);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.row-amount.negative { color: var(--ink); }
.row-amount.positive { color: var(--ctx-finanzas); }

.row.completed .row-title {
  color: var(--ink-faint);
  text-decoration: line-through;
}

/* --------------------------------------------------------------- CHECK --
   Círculo, no cuadro. Un cuadro se lee como formulario; un círculo se lee
   como algo que se puede palomear al pasar. */
.check {
  position: relative;
  flex: 0 0 auto;
  width: var(--tap); height: var(--tap);
  margin: -10px 0 -10px -10px;   /* el botón crece sin engordar la fila */
  border: 0;
  background: transparent;
  cursor: pointer;
  display: grid;
  place-items: center;
  padding: 0;
}
.check::before {
  content: "";
  position: absolute;
  width: 22px; height: 22px;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--r-full);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.check svg {
  position: relative;
  width: 13px; height: 13px;
  stroke: transparent; stroke-width: 2.5; fill: none;
}
.check[aria-checked="true"]::before {
  background: var(--ctx-finanzas-soft);
  border-color: var(--ctx-finanzas-soft);
}
.check[aria-checked="true"] svg { stroke: #fff; }

/* El área táctil se consigue haciendo GRANDE el propio botón y dibujando el
   círculo pequeño dentro con ::before.
   La versión anterior usaba un ::after absoluto de 44px como "área táctil
   extendida", pero `.check` no era `position: relative`, así que ese bloque
   invisible se posicionaba contra `.row` y quedaba flotando sobre la fila
   bloqueando clicks — justo lo que el Gate B prohíbe. La prueba automática
   de controles tapados lo detectó. */

/* --------------------------------------------------- MARCADOR DE HORA --
   La línea vertical de color es el marcador de área en las vistas de agenda.
   Reemplaza al ícono de color: menos ruido, misma información. */
.time-col {
  flex: 0 0 62px;
  font-size: var(--t-meta);
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.row-rule {
  /* 1.14.2 — El ancho pasa a token: 3px en Pastel (sin cambio) y 6px en Vivo.
     Este elemento ya iba a tono pleno, pero a 3px no se veía; duplicarlo es la
     forma más barata de que una fila se lea como coloreada sin tocar el
     contraste de nada, porque no tiene texto encima. */
  flex: 0 0 var(--rule-w);
  align-self: stretch;
  border-radius: var(--r-full);
  background: var(--ctx, var(--line-strong));
  min-height: 34px;
}
/* Conector de agenda en la pantalla nativa de Calendario (§1 1.0.7).
   El marcador de área (.row-rule) ya existe y no se toca — es un
   componente compartido con Hoy y no se puede recolorear sin afectar esa
   pantalla también. Lo que se añade aquí es un hairline neutro que solo
   se asoma en el hueco de 5px entre filas consecutivas, uniendo los
   marcadores visualmente sin duplicar ni sustituir el componente. Scope
   exclusivo a #screen-calendar: no toca Hoy, Pendientes ni Finanzas. */
#screen-calendar .col-main .row-rule { position: relative; }
#screen-calendar .col-main .row-rule::before {
  content: "";
  position: absolute;
  left: 50%; top: -15px; bottom: -15px;
  width: 1px;
  transform: translateX(-50%);
  background: var(--line);
}
#screen-calendar .col-main .row:first-child .row-rule::before { top: 0; }
#screen-calendar .col-main .row:last-child .row-rule::before { bottom: 0; }

/* ===========================================================================
   SWIPE PARA ELIMINAR
   ----------------------------------------------------------------------------
   El fondo destructivo vive detrás de la fila, no dentro. La fila se traslada
   con transform, así que el gesto no reflowea la lista ni pelea con el scroll
   vertical (eso lo resuelve el JS con un umbral de eje).
   ========================================================================= */
.swipe {
  position: relative;
  border-radius: var(--r-md);
  overflow: hidden;
  touch-action: pan-y;
}
.swipe + .swipe { margin-top: var(--s2); }
.swipe .row { margin-top: 0; }

.swipe-back {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: var(--s5);
  background: var(--ctx-atencion);
  border-radius: var(--r-md);
}
/* 1.15.0 — La etiqueta va en --ink-on-color y no en blanco. El fondo es
   --ctx-atencion, un rojo CLARO por definición en las tres paletas, y el
   blanco encima daba 2.12 a 2.91 según combinación: fallo de AA anterior a
   este encargo, no una consecuencia suya. Con la tinta oscura el peor caso de
   las seis combinaciones es 5.24. */
.swipe-back button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border: 0;
  background: transparent;
  color: var(--ink-on-color);
  font-size: var(--t-micro);
  font-weight: 600;
  cursor: pointer;
  min-width: var(--tap);
  min-height: var(--tap);
  justify-content: center;
}
.swipe-back svg { width: 17px; height: 17px; stroke: var(--ink-on-color); fill: none; stroke-width: 1.8; }

.swipe-front {
  position: relative;
  transform: translateX(0);
  transition: transform var(--dur) var(--ease);
  will-change: transform;
}
.swipe.dragging .swipe-front { transition: none; }
.swipe.open .swipe-front { transform: translateX(-88px); }

/* Alternativa accesible: quien usa teclado o mouse no depende del gesto. */
.row-more {
  flex: 0 0 auto;
  width: var(--tap); height: var(--tap);
  margin: -10px -10px -10px 0;
  border: 0; background: transparent;
  border-radius: var(--r-full);
  /* 2.1.1 — El ⋯ de cada pendiente medía 2.42-2.75:1 sobre su fila (mínimo 3
     para un icono). Es la única alternativa visible al gesto de deslizar. */
  color: var(--ink-muted);
  cursor: pointer;
  display: grid; place-items: center;
}
.row-more:hover { background: var(--surface-sunken); color: var(--ink); }

/* ===========================================================================
   BOTONES
   ========================================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: var(--tap);
  /* 2.1.1 — Medido a 320px: «Agregar pendiente», «Nueva lista» y «Nuevo
     hábito» quedaban en 40x44 porque el botón es un flex item dentro de la
     cabecera y encogía. `min-width` es un suelo que flex no puede cruzar, así
     que el ancho nunca baja del objetivo táctil. */
  min-width: var(--tap);
  padding: 0 var(--s5);
  border-radius: var(--r-full);
  border: 1px solid transparent;
  font-size: var(--t-body);
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform .12s var(--ease);
  white-space: nowrap;
}
.btn-primary   { background: var(--action); color: var(--action-ink); }
.btn-primary:hover { background: var(--action-hover); }
.btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--line-strong); }
.btn-secondary:hover { background: var(--surface-hover); }
.btn-danger { background: var(--ctx-atencion); color: #fff; border-color: var(--ctx-atencion); }
/* 1.21.6 — Peligro en tono suave, como `.sheet-item.danger`: el botón sigue
   siendo secundario, pero deja de pesar lo mismo que un "Cerrar". `.btn-danger`
   (rojo macizo) se reserva para lo que de verdad destruye datos. */
.btn.danger { color: var(--color-red-ink); }
.btn-danger:hover { filter: brightness(.96); }
.btn-quiet     { background: transparent; color: var(--ink-muted); }
.btn-quiet:hover { color: var(--ink); background: var(--surface-sunken); }
.btn-block     { width: 100%; }
/* Presión táctil discreta: 150–220ms, sin rebote. Un solo lugar para toda la
   app en vez de repetirlo botón por botón. */
.btn:active, .chip:active, .msg-action:active, .sam-starter:active, .quick-sam-desktop:active {
  transform: scale(.98);
}

/* --------------------------------------------------------------- CHIPS --
   Filtros de Pendientes y selector de mes. */
.chips { display: flex; gap: var(--s2); overflow-x: auto; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }

.chip {
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 var(--s4);
  border-radius: var(--r-full);
  border: 1px solid transparent;
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: var(--t-meta);
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease), transform .12s var(--ease);
}
.chip[aria-selected="true"] { background: var(--action); color: var(--action-ink); }

/* ===========================================================================
   SAM
   ----------------------------------------------------------------------------
   Sam nunca es una tarjeta con degradado. Es una superficie hundida y neutra
   con el husky monocromático a la izquierda y, cuando aplica, una sola acción
   concreta. El color solo entra si el mensaje es de atención.
   ========================================================================= */
.sam-note {
  display: flex;
  gap: var(--s3);
  padding: var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  margin-top: var(--s5);
}
.sam-note[data-tone="atencion"] {
  background: var(--tint-atencion);
  border-color: transparent;
}

/* El husky se pinta como máscara CSS, no como imagen.
   Un <img> blanco sobre papel claro simplemente desaparece — pasó en la
   primera pasada de QA. Con máscara, el archivo solo aporta la silueta y el
   color lo pone `background`, así que:
     · es monocromático por construcción, no por convención;
     · sirve igual en claro y en oscuro con un único archivo;
     · sus estados son un cambio de color, nunca un halo ni un degradado.
   Requisito del Gate G: Sam debe ser reconocible sin efectos permanentes. */
.sam-mark {
  flex: 0 0 auto;
  display: block;
  width: 34px; height: 34px;
  background: var(--ink);
  -webkit-mask: var(--sam-src) center / contain no-repeat;
  mask: var(--sam-src) center / contain no-repeat;
  transition: background var(--dur) var(--ease);
}
:root { --sam-src: url("../brand/sam-mark.png"); }

.sam-body { flex: 1; min-width: 0; }
.sam-body p { font-size: var(--t-body); color: var(--ink); }
.sam-body .btn { margin-top: var(--s3); }

/* Estados de Sam. Cambia el color de la tinta, no se le añade nada encima.
   "Listo" es el único momento en que Sam toma un color, y dura lo que dura la
   confirmación. */
.sam-mark[data-state="escuchando"] { animation: sam-pulse 1.6s var(--ease) infinite; }
.sam-mark[data-state="pensando"]   { animation: sam-think 1.1s var(--ease) infinite; }
.sam-mark[data-state="listo"]      { background: var(--ctx-finanzas-soft); }
.sam-mark[data-state="atencion"]   { background: var(--ctx-atencion); }

@keyframes sam-pulse { 0%,100% { opacity: .9; } 50% { opacity: .55; } }
@keyframes sam-think { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }

/* ===========================================================================
   BLOQUE DE FINANZAS
   El único bloque de color sólido de toda la app. Se lo gana porque resume
   las cifras que el usuario busca de un vistazo y porque el verde petróleo
   separa el dinero del resto del producto sin necesidad de un título.
   ----------------------------------------------------------------------------
   §4 1.0.7: "Saldo en cuentas" (flujo acumulado) se separa en su propio
   renglón de "Ingresos/Gastos/Balance del mes" (flujo del periodo) para que
   no se lean como cuatro cifras equivalentes — mismo criterio que ya usa el
   briefing financiero de Sam. Sigue siendo UN bloque de color, no cuatro
   tarjetas sueltas: la separación es de posición, no de superficie.
   ========================================================================= */
.money-block {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-areas: "a b" "c c" "d d";
  gap: 1px;
  padding: 1px;
  background: color-mix(in srgb, var(--ctx-finanzas) 82%, var(--surface));
  border-radius: var(--r-lg);
  color: #fff;
}
.money-cell { text-align: center; padding: var(--s4) var(--s2); background: var(--ctx-finanzas); }
.money-cell:nth-child(1) { grid-area: a; border-radius: calc(var(--r-lg) - 1px) 0 0 0; }
.money-cell:nth-child(2) { grid-area: b; border-radius: 0 calc(var(--r-lg) - 1px) 0 0; }
.money-cell:nth-child(3) { grid-area: c; }
.money-cell:nth-child(4) { grid-area: d; border-radius: 0 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px); }
.money-cell span {
  display: block;
  font-size: var(--t-meta);
  opacity: .82;
}
.money-cell strong {
  display: block;
  font-family: var(--font-num);
  font-size: 1.1875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
/* Jerarquía dentro del bloque sólido: Ingresos, Gastos y Saldo son datos de
   apoyo; Balance del mes es la cifra que responde "¿cómo voy?" y se lee
   primero, sin dejar de compartir la misma superficie de color (§14, §18 —
   coherencia con el briefing financiero de Sam, que ya trata el balance
   como su métrica hero). */
.money-cell:nth-child(3) span { opacity: 1; font-weight: 600; }
.money-cell:nth-child(3) strong { font-size: 24px; }

/* ===========================================================================
   COMPOSER
   ========================================================================= */
.composer {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s2) var(--s2) var(--s4);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-full);
}
.composer input,
.composer textarea {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  resize: none;
  outline: none;
}
/* 1.21.2 — El campo de Sam es un textarea que crece solo (App.growComposer).
   Cinco líneas de tope: suficiente para releer una frase dictada larga sin que
   el teclado y el hilo se queden sin sitio. Pasado el tope, desliza. El
   relleno vertical es lo que le da zona táctil: un `min-height` la daría
   igual, pero en un textarea empuja la primera línea contra el borde de
   arriba en vez de dejarla centrada como estaba con el <input>. */
.composer textarea {
  max-height: 120px;
  overflow-y: auto;
  line-height: 1.35;
  padding: 10px 0;
}
.composer input::placeholder,
.composer textarea::placeholder { color: var(--ink-faint); }
/* Con una sola línea sigue siendo una píldora; al crecer, un rectángulo
   redondeado — si no, las curvas de los extremos muerden el texto. */
.composer.is-grown { border-radius: var(--r-lg); align-items: flex-end; }
.composer-tool {
  flex: 0 0 auto;
  width: var(--tap); height: var(--tap);
  border: 0; background: transparent;
  border-radius: var(--r-full);
  color: var(--ink-muted);
  cursor: pointer;
  display: grid; place-items: center;
}
.composer-tool:hover { background: var(--surface-sunken); color: var(--ink); }
.composer-tool .i { width: 18px; height: 18px; display: inline-grid; place-items: center; }
.composer-tool .i svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* Enviar conserva el mismo lenguaje visual, pero el botón completo alcanza
   el target táctil canónico de 44px. */
.composer-send { background: var(--action); color: var(--action-ink); }
.composer-send:hover:not(:disabled) { background: var(--action); color: var(--action-ink); filter: brightness(.96); }
.composer-send:disabled {
  background: transparent;
  color: var(--ink-faint);
  opacity: .52;
  cursor: default;
}
.composer-send:disabled:hover { background: transparent; color: var(--ink-faint); }

/* ===========================================================================
   TABBAR
   ========================================================================= */
.tabbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  align-items: center;
  height: calc(var(--tabbar-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  background: var(--paper);
  backdrop-filter: none;
  border-top: 1px solid var(--line);
  transition: transform 160ms var(--ease), opacity 160ms var(--ease);
}

.tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  border: 0;
  background: transparent;
  color: var(--ink-faint);
  font-size: var(--t-micro);
  font-weight: 500;
  cursor: pointer;
  padding: var(--s1) 0;
  min-height: var(--tap);
}
.tab svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.7; }
/* 2.1.1 — El resalte visual se engancha a data-nav-active y NO a
   aria-current="page": en Hábitos y en Listas la pestaña encendida es
   prestada (Hoy y Pendientes) y ahí ui.js pone aria-current="true", que es lo
   honesto para el lector. El aspecto no cambia ni un píxel. */
.tab[data-nav-active] { color: var(--ink); }

/* Sam ocupa el centro y sobresale. Es la única pieza elevada de la barra
   porque es la entrada principal al producto. */
.tab-sam { position: relative; }
.tab-sam .tab-sam-disc {
  position: absolute;
  top: -20px; left: 50%;
  transform: translateX(-50%);
  width: 52px; height: 52px;
  border-radius: var(--r-full);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-lift);
  display: grid; place-items: center;
}
.tab-sam .tab-sam-disc img { width: 34px; height: 34px; }
.tab-sam > span:last-child { margin-top: 30px; }

/* ===========================================================================
   TOAST · DESHACER
   Un swipe destructivo siempre deja salida. El toast es la garantía del
   Gate I: sin él, el gesto no se habilita.
   ========================================================================= */
.toast {
  position: fixed;
  left: var(--s4); right: var(--s4);
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--s3));
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3) var(--s3) var(--s3) var(--s5);
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lift);
  font-size: var(--t-meta);
  transform: translateY(140%);
  transition: transform .26s var(--ease);
}
.toast.visible { transform: translateY(0); }
.toast-undo {
  margin-left: auto;
  border: 0;
  background: transparent;
  color: var(--paper);
  font-weight: 600;
  font-size: var(--t-meta);
  cursor: pointer;
  min-height: 32px;
  padding: 0 var(--s2);
  text-decoration: underline;
}
/* 2.1.2 — En pantalla táctil «Deshacer» es la única salida de un borrado por
   deslizamiento y sólo recibía el toque en 32 px de alto. La zona sensible
   sube a --tap (44) y el margen negativo se come la diferencia dentro del
   relleno que el aviso ya tenía: el aviso mide lo mismo y no se ve distinto. */
@media (pointer: coarse) {
  .toast-undo { min-height: var(--tap); margin-block: calc((32px - var(--tap)) / 2); }
}

/* ===========================================================================
   VACÍOS
   Un estado vacío invita a actuar; no se disculpa ni decora.
   ========================================================================= */
.empty {
  width: min(100%, 420px);
  margin: var(--s4) auto 0;
  padding: var(--s7) var(--s5);
  text-align: center;
  color: var(--ink-muted);
  font-size: var(--t-meta);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

/* ===========================================================================
   ESCRITORIO
   La misma familia visual: cambia la distribución, no el lenguaje. Ni un
   componente nuevo exclusivo de desktop.
   ========================================================================= */
@media (min-width: 900px) {
  .app { flex-direction: row; }
  .tabbar { display: none; }

  /* La marca ya vive en el sidebar. Dejar también la appbar la duplicaba en
     pantalla — detectado en la primera pasada de QA visual. */
  .appbar { padding: var(--s6) var(--s8) 0; }
  .appbar > .appbar-brand { display: none; }

  /* El swipe es un gesto táctil. Con mouse y teclado la salida es el menú de
     la fila, que debe estar siempre visible: Gate I exige que el gesto nunca
     sea la única vía a una acción destructiva. */
  .swipe.open .swipe-front { transform: none; }
  .swipe-back { display: none; }

  .sidebar {
    flex: 0 0 216px;
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    padding: var(--s6) var(--s3);
    border-right: 1px solid var(--line);
    height: 100dvh;
    position: sticky;
    top: 0;
  }
  .sidebar .appbar-brand { padding: 0 var(--s3) var(--s6); }

  .nav-link {
    display: flex;
    align-items: center;
    gap: var(--s3);
    min-height: 40px;
    padding: 0 var(--s3);
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--ink-muted);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    text-align: left;
  }
  .nav-link svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; }
  .nav-link:hover { background: var(--surface-sunken); color: var(--ink); }
  .nav-link[data-nav-active] { background: var(--surface-sunken); color: var(--ink); }

  .content {
    padding: var(--s7) var(--s8) var(--s8);
    max-width: 1180px;
  }
  .screen-title { font-size: 34px; }
  .toast { left: auto; right: var(--s7); width: 380px; bottom: var(--s7); }
}

@media (max-width: 899px) {
  .sidebar { display: none; }
}

/* ============================================================================
   Samapp 1.0 — Ajustes de densidad y jerarquía
   ----------------------------------------------------------------------------
   Correcciones sobre la primera pasada visual:

     · menos sensación de tarjeta grande;
     · más densidad útil en móvil;
     · nada de contenido tapado por la tabbar ni por el toast;
     · el serif reservado a un solo lugar por pantalla.
   ========================================================================== */

/* ------------------------------------------------------------- DENSIDAD --
   Las filas bajan de 44px de padding vertical a 10px y el radio de 14 a 11.
   El área táctil sigue siendo de 44px por el min-height, así que se gana
   densidad sin perder accesibilidad. */
.row {
  padding: 10px var(--s4);
  border-radius: var(--r-sm);
  gap: var(--s3);
}
.row + .row,
.swipe + .swipe,
.swipe + .row,
.row + .swipe { margin-top: 6px; }

.swipe, .swipe-back { border-radius: var(--r-sm); }

/* Las filas contiguas del mismo grupo se leen como una lista, no como cinco
   tarjetas sueltas: se juntan y solo el bloque tiene esquinas. */
.row-title { font-size: 0.9375rem; }
.row-meta  { font-size: 0.8125rem; }

/* La columna de hora se estrecha: 62px sobraban y empujaban el título. */
.time-col { flex: 0 0 62px; font-size: 0.75rem; white-space: nowrap; }

/* ------------------------------------------------------------ JERARQUÍA --
   El serif aparece UNA vez por pantalla, en el título. El saludo de Hoy es
   ese título; no lleva además un `screen-title` debajo. */
.screen-head { margin-bottom: var(--s4); position: relative; }
.task-screen-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
}
.task-add-button { flex: 0 0 auto; gap: 6px; }
.task-add-button svg {
  width: 16px; height: 16px;
  fill: none; stroke: currentColor; stroke-width: 2;
}
.greeting, .screen-title { font-size: 25px; }
.screen-sub { font-size: 0.8125rem; margin-top: 2px; }

.section-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: var(--s5) 0 var(--s2);
}
.section-label-row:first-of-type { margin-top: var(--s4); }
.section-label { margin: 0; font-size: 0.8125rem; letter-spacing: .01em; }

/* --------------------------------------------------------- SAM DISCRETO --
   Sin borde ni superficie propia: Sam es un apunte al margen, no un módulo.
   Solo toma fondo cuando el tono es de atención, porque ahí sí compite por
   la mirada a propósito. */
.sam-note {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  padding: var(--s4) 0 0;
  margin-top: var(--s5);
  align-items: flex-start;
}
.sam-note[data-tone="atencion"] {
  background: var(--tint-atencion);
  border-top: 0;
  border-radius: var(--r-sm);
  padding: var(--s4);
}
.sam-mark { width: 26px; height: 26px; }
.sam-body p { font-size: 0.875rem; color: var(--ink-muted); }
.sam-note[data-tone="atencion"] .sam-body p { color: var(--ink); }
/* 2.1.2 — La nota de Sam repite títulos del usuario («Te faltan N cosas en
   …»): una palabra larga empujaba Listas a 541 px y sacaba de la vista la
   barra inferior y las hojas. Parte palabras largas. */
.sam-body p { overflow-wrap: anywhere; }

/* 1.16 — El icono se centra con la PRIMERA LÍNEA del texto, no con el borde
   superior de su caja. `align-items: flex-start` alinea cajas: el husky mide
   26px y la línea de `.sam-body p` mide 21px (14px × 1.5), así que los dos
   arrancaban a la misma altura y el centro del icono caía 3px por DEBAJO del
   centro de la línea — medido, no supuesto. Subirlo la mitad de la diferencia
   pone los dos centros en la misma horizontal.

   La variable deja el número a la vista: si mañana cambia el cuerpo del
   párrafo, aquí se ve qué hay que tocar. */
.sam-note { --sam-note-line: 21px; }
.sam-note > .sam-mark { margin-top: calc((var(--sam-note-line) - 26px) / 2); }

.btn-sm { min-height: 40px; padding: 0 var(--s4); font-size: 0.875rem; }
.sam-body .btn { margin-top: var(--s2); }

/* ------------------------------------------------- NADA TAPA CONTENIDO --
   El toast se apoya sobre la tabbar y el contenido reserva sitio para AMBOS.
   En la primera pasada el toast se comía la fila de abajo. */
.content {
  padding-bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--s6));
}
.toast { bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--s2)); }

/* 2.1.2 — RED DE SEGURIDAD EN EL TELÉFONO. Un texto que desbordaba (una
   palabra larga en Listas, un monto enorme en Finanzas) ensanchaba la página
   entera: en el teléfono la ventana crecía de 360 a 541 px, la barra inferior
   se estiraba y quedaba fuera de la vista. `clip` recorta sin crear un
   contenedor de scroll, así que sticky y las tiras desplazables siguen igual
   y el eje vertical no se toca. Sólo por debajo de 900 px: el escritorio no
   se recorta. Los culpables se corrigen en su pantalla; esto evita que uno
   nuevo vuelva a romper la barra. */
@media (max-width: 899px) {
  #content { overflow-x: clip; }
}
/* 2.1.2 — Con el recorte, un control que desbordara dejaría de alcanzarse
   (antes se alcanzaba desplazando la página entera). Los que lo hacían con la
   letra al 200 % a 320 px están en componentes comunes de ui.js: la fila de
   sección deja bajar su enlace («Ver todos») al renglón siguiente y el botón
   del apunte de Sam parte su texto. Con letra normal no cambia nada. */
.section-label-row { flex-wrap: wrap; }
.sam-body .btn { max-width: 100%; white-space: normal; text-align: center; }

/* Con el teclado abierto la tabbar deja de competir con el composer. No se
   elimina del DOM: sale discretamente y vuelve al cerrar el teclado. */
@media (max-width: 899px) {
  body.mobile-keyboard-open .tabbar {
    transform: translateY(calc(100% + var(--safe-bottom)));
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }
  body.mobile-keyboard-open .content { padding-bottom: var(--s2); }
  body[data-view="sam"].mobile-keyboard-open .app {
    height: var(--sam-visual-height, 100dvh);
    min-height: 0;
    max-height: var(--sam-visual-height, 100dvh);
    overflow: hidden;
  }
  body[data-view="sam"].mobile-keyboard-open { overflow: hidden; }
  body[data-view="sam"].mobile-keyboard-open .composer-bar { padding-bottom: var(--s1); }
}

/* -------------------------------------------------------------- EXTRAS --*/
.link-more {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--tap);
  margin-top: var(--s2);
  padding: 0 var(--s2);
  border: 0;
  background: transparent;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  cursor: pointer;
  text-align: center;
}
.link-more:hover { color: var(--ink); }

/* 2.1.1 — `--ink-faint` es el token de PLACEHOLDER; usado en texto de lectura
   medía 2.26:1 en claro-pastel y 2.52 en claro-sobrio, muy por debajo de 4.5.
   Es justo el texto que guía cuando la app está vacía. Pasa a `--ink-muted`,
   que es el token de metadato y ya está medido por encima del mínimo en los
   cuatro cruces. La paleta no se toca. */
.empty-hint { margin-top: var(--s1); color: var(--ink-muted); font-size: 0.8125rem; }
.empty .btn { margin-top: var(--s4); }

.back-btn {
  width: var(--tap); height: var(--tap);
  margin-left: -6px;
  margin-bottom: var(--s2);
  border: 0; background: transparent;
  border-radius: var(--r-full);
  color: var(--ink);
  cursor: pointer;
  display: grid; place-items: center;
}
.back-btn svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; }

/* Agregar en línea: mismo alto que una fila, para que la lista no dé un salto
   visual entre "lo que hay" y "lo que puedo añadir". */
.inline-add {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--tap);
  padding: 10px var(--s4);
  margin-top: 6px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
}
.inline-add input {
  flex: 1; border: 0; background: transparent;
  color: var(--ink); font: inherit; font-size: 0.9375rem; outline: none;
}
/* 2.1.2 — L2: el campo encoge con letra grande. Sin esto, al 200 % el input
   (size=20, min-width:auto) medía 346 px, llegaba a x=429 en un teléfono de
   390 y ensanchaba la página: las hojas quedaban 85 px por debajo. */
.inline-add input { min-width: 0; }
.inline-add input::placeholder { color: var(--ink-faint); }
.inline-add-icon {
  position: relative;
  flex: 0 0 var(--tap);
  width: var(--tap); height: var(--tap); padding: 0;
  margin: -10px 0 -10px -10px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent; color: var(--ink-muted);
  cursor: pointer; display: grid; place-items: center;
}
.inline-add-icon::before {
  content: "";
  position: absolute;
  width: 22px; height: 22px;
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--r-full);
}
.inline-add-icon svg { position: relative; z-index: 1; width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2.2; }

.fab {
  position: fixed;
  right: var(--s5);
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--s4));
  z-index: 30;
  width: 52px; height: 52px;
  border: 0; border-radius: var(--r-full);
  background: var(--action); color: var(--action-ink);
  box-shadow: var(--shadow-lift);
  cursor: pointer; display: grid; place-items: center;
}
.fab svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; }

.row-link { cursor: pointer; }

.screen { display: none; }
.screen.active { display: block; }


/* El toast se ocultaba con translateY(140%), pero como está anclado por
   encima de la tabbar ese desplazamiento no bastaba para sacarlo de la
   pantalla y quedaba una barra oscura visible en reposo. Se oculta de forma
   explícita: así no depende de la relación entre su alto y su separación del
   borde inferior. */
.toast {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: transform .26s var(--ease), opacity .2s var(--ease), visibility .26s;
}
.toast.visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* ============================================================================
   FINANZAS
   ========================================================================== */
.month-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  margin-bottom: var(--s4);
}
.month-arrow {
  width: var(--tap); height: var(--tap);
  border: 0; background: transparent;
  border-radius: var(--r-full);
  color: var(--ink-muted);
  cursor: pointer; display: grid; place-items: center;
}
.month-arrow:hover { background: var(--surface-sunken); color: var(--ink); }
.month-arrow svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.month-label {
  min-width: 148px;
  text-align: center;
  font-size: 0.875rem;
  font-weight: 500;
  padding: var(--s2) var(--s4);
  border-radius: var(--r-full);
  background: var(--surface-sunken);
}

/* Fila de compromiso pendiente. Se distingue de un movimiento a propósito:
   lleva su propia acción y NO representa dinero que ya se movió. Confundir
   ambas cosas es el error que el Gate D vigila. */
.payable-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: var(--tap);
  padding: 10px var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
.payable-row + .payable-row { margin-top: 6px; }
.payable-row.late { background: var(--tint-atencion); border-color: transparent; }
.payable-row.late .row-meta { color: var(--ctx-atencion); }
.payable-row .row-amount { font-size: 0.875rem; }
.payable-row .btn { flex: 0 0 auto; }

/* ============================================================================
   ESTRUCTURA DEL SHELL
   ========================================================================== */
.app-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.sidebar-foot { margin-top: auto; }

/* ============================================================================
   CAPAS — hoja y confirmación
   ----------------------------------------------------------------------------
   Una sola familia de superposición para toda la app. En móvil la hoja sube
   desde abajo, donde alcanza el pulgar; en escritorio se centra.
   ========================================================================== */
.scrim {
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: rgba(10, 18, 28, .38);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease), visibility var(--dur);
  backdrop-filter: blur(2px);
}
/* 2.1.1 — AQUÍ ESTABA LA CAUSA DE QUE EL FOCO NO ENTRARA A LA HOJA.
   `visibility` estaba en la transición de entrada, así que durante los 180 ms
   siguientes a abrir, el scrim seguía calculando `visibility: hidden` —medido
   en Chrome: hidden en el rAF, visible a los 360 ms— y `focus()` sobre algo
   invisible no hace nada y no falla. Al abrir, la visibilidad cambia ya; al
   cerrar sigue esperando al desvanecido, que es lo que evita el parpadeo. */
.scrim.visible {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--dur) var(--ease);
}

.sheet {
  width: 100%;
  max-width: 520px;
  max-height: 85dvh;
  overflow-y: auto;
  padding: var(--s3) var(--s5) calc(var(--safe-bottom) + var(--s6));
  background: var(--surface);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  transform: translateY(100%);
  transition: transform .28s var(--ease);
}
.scrim.visible .sheet { transform: translateY(0); }

/* 2.1.1 — La hoja recibe el foco al abrirse (ver ui.js), así que ahora sí se
   le puede ver el anillo. Va HACIA DENTRO: en móvil la hoja está pegada a los
   bordes de la pantalla y un anillo por fuera se recorta a los lados. */
.sheet:focus-visible,
.confirm:focus-visible { outline-offset: -2px; }

/* 1.17 — La barrita ahora ARRASTRA para cerrar (ver ui.js). Para que el dedo
   la atine, la zona sensible mide 44px de alto aunque la raya siga midiendo 4:
   el relleno es transparente y el trazo se dibuja con ::before. Antes eran 4px
   de blanco imposibles de agarrar.
   `touch-action: none` es imprescindible: sin él, el navegador se queda el
   gesto vertical para desplazar la página y el arrastre nunca llega al código. */
.sheet-handle {
  width: 100%; height: 44px;
  margin: 0 auto calc(var(--s4) - 20px);
  display: grid; place-items: center;
  background: none;
  touch-action: none;
  cursor: grab;
}
.sheet-handle::before {
  content: "";
  width: 38px; height: 4px;
  border-radius: var(--r-full);
  background: var(--line-strong);
}
.sheet-handle:active { cursor: grabbing; }
.sheet-title {
  font-family: var(--font-display);
  font-size: 1.1875rem;
  font-weight: 500;
  color: var(--ink-strong);
  margin-bottom: var(--s3);
}
.sheet-lead { color: var(--ink-muted); font-size: 0.875rem; margin-bottom: var(--s4); }
/* 2.1.2 — El título y la entradilla de la hoja son texto del usuario (nombre
   del pendiente, del hábito, del compromiso). Una palabra larga sin espacios
   —una URL, un folio— se salía de la hoja y la dejaba con scroll horizontal
   (medido: 575 px de título en una hoja de 318). Ahora se parte; con títulos
   normales no cambia nada. */
.sheet-title, .sheet-lead { overflow-wrap: anywhere; }

.sheet-actions { display: flex; flex-direction: column; gap: 6px; }
.sheet-item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: var(--tap);
  padding: 0 var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.9375rem;
  text-align: left;
  cursor: pointer;
}
.sheet-item:hover { background: var(--surface-hover); }
.sheet-item.danger { color: var(--color-red-ink); }

.confirm {
  width: 100%;
  max-width: 400px;
  margin: auto var(--s5);
  padding: var(--s6);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  transform: scale(.96);
  transition: transform var(--dur) var(--ease);
}
.scrim.visible .confirm { transform: scale(1); }
.confirm-title {
  font-family: var(--font-display);
  font-size: 1.1875rem; font-weight: 500;
  color: var(--ink-strong);
}
.confirm-text { margin-top: var(--s2); color: var(--ink-muted); font-size: 0.875rem; }
.confirm-actions { display: flex; gap: var(--s2); margin-top: var(--s5); }
.confirm-actions .btn { flex: 1; }
/* 2.1.2 — A 360 px o menos el diálogo iba de borde a borde y «Cancelar
   compromiso» se salía de su botón: los dos botones flex:1 con nowrap fijaban
   el ancho mínimo del diálogo. Si caben en mitades iguales siguen lado a lado
   como siempre; si no, se apilan a todo lo ancho. El texto largo se parte. */
.confirm { min-width: 0; }
.confirm-title, .confirm-text { overflow-wrap: anywhere; }
.confirm-actions { flex-wrap: wrap; }
.confirm-actions .btn { flex: 1 1 auto; min-width: calc(50% - var(--s1)); white-space: normal; text-align: center; }
/* 2.1.2 — Con la letra al 200 % en un teléfono chico el diálogo, ya con los
   botones apilados, medía más que la pantalla (732 px en una de 568) y el
   botón de confirmar quedaba debajo del borde, sin forma de alcanzarlo: el
   diálogo nunca tuvo desplazamiento. Ahora mide como mucho la pantalla menos
   un margen y se desliza por dentro. El foco inicial (Cancelar) se pone sin
   desplazar, así que abre mostrando el título. Si cabe, no cambia nada. */
.confirm { max-height: calc(100dvh - var(--safe-top) - var(--safe-bottom) - 2 * var(--s4)); overflow-y: auto; overscroll-behavior: contain; }

@media (min-width: 900px) {
  .scrim { align-items: center; }
  .sheet {
    border-radius: var(--r-lg);
    border-bottom: 1px solid var(--line);
    padding-bottom: var(--s6);
    transform: scale(.96);
    max-height: 78dvh;
  }
  .scrim.visible .sheet { transform: scale(1); }
  .sheet-handle { display: none; }
}

/* ============================================================================
   PUERTA DE ACCESO
   ----------------------------------------------------------------------------
   El marcado lo genera auth.js, que se conserva de 0.1.5 sin tocar por ser
   código de seguridad. Aquí solo se le da la identidad nueva.
   ========================================================================== */
.auth-gate {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s5);
  background: var(--paper);
  overflow-y: auto;
}
.auth-gate.hidden { display: none; }

.auth-card {
  width: 100%;
  max-width: 380px;
  padding: var(--s7) var(--s6);
}
.auth-logo {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s7);
}
.auth-logo .brand-mark img { width: 40px; height: 40px; }
.auth-logo strong {
  display: block;
  font-family: var(--font-display);
  font-size: 23px;
  font-weight: 500;
  color: var(--ink-strong);
}
.auth-logo span { font-size: 0.8125rem; color: var(--ink-muted); }

.auth-tabs {
  display: flex;
  gap: var(--s1);
  padding: 4px;
  margin-bottom: var(--s6);
  background: var(--surface-sunken);
  border-radius: var(--r-full);
}
.auth-tabs button {
  flex: 1;
  min-height: 40px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-muted);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.auth-tabs button.active { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-low); }

.auth-form { display: flex; flex-direction: column; gap: var(--s4); }
.auth-form label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--ink-muted);
}
.auth-form input {
  display: block;
  width: 100%;
  min-height: var(--tap);
  margin-top: var(--s2);
  padding: 0 var(--s4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.9375rem;   /* 16px o más evita el zoom automático de iOS al enfocar */
}
.auth-form input:focus { outline: 2px solid var(--focus-ring); outline-offset: 1px; border-color: transparent; }
.auth-register-only.hidden,
.auth-reset-only.hidden,
.auth-forgot.hidden { display: none; }
.auth-submit { width: 100%; margin-top: var(--s2); }

/* 1.23.1 — Ver/ocultar contraseña.
   El botón vive DENTRO del campo, no al lado: así el input conserva su ancho
   completo y la fila no cambia de altura al aparecer. El relleno derecho del
   input se amplía para que el texto nunca pase por debajo del icono. */
.auth-password-wrap { position: relative; display: block; }
.auth-password-wrap input { padding-right: calc(var(--tap) + var(--s2)); }
.auth-password-eye {
  position: absolute;
  right: 0;
  /* El margin-top del input se colapsa a través del contenedor: medido,
     la caja del wrap coincide exacta con la del campo (44px). Estirarlo de
     borde a borde da el area tactil completa y el centrado exacto. */
  top: 0;
  bottom: 0;
  width: var(--tap);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-muted);
  cursor: pointer;
  border-radius: var(--r-sm);
}
.auth-password-eye:hover { color: var(--ink); }
.auth-password-eye:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; color: var(--ink); }
.auth-password-eye .i { width: 20px; height: 20px; }
.auth-error { color: var(--ctx-atencion); font-size: 0.8125rem; min-height: 18px; }
.auth-note {
  margin-top: var(--s6);
  color: var(--ink-faint);
  font-size: 0.75rem;
  text-align: center;
  line-height: 1.5;
}

/* En móvil un input por debajo de 16px hace que iOS haga zoom al enfocarlo y
   el layout queda descolocado al salir del campo. */
@media (max-width: 480px) {
  .auth-form input,
  .inline-add input,
  .composer input,
  .composer textarea,
  .sheet-form input,
  .sheet-form select,
  .sheet-form textarea { font-size: 1rem; }
}

/* ============================================================================
   SAM
   ----------------------------------------------------------------------------
   La conversación ocupa la pantalla y el composer se ancla abajo. Sin cabecera
   de chat, sin avatar por burbuja, sin hora en cada mensaje: ese cromo es lo
   que hace que un chat parezca una herramienta en vez de una conversación.
   ========================================================================== */
/* En Sam la app se ancla al alto exacto de la ventana y el composer queda
   abajo del todo. Se hace con flexbox y no con `height: calc(...)`: restar a
   mano barra superior, tabbar e insets dejaba el composer medio tapado en
   cuanto cualquiera de esos valores cambiaba, y en iOS cambian solos cuando
   la barra del navegador se encoge. */
body[data-view="sam"] .app { height: 100dvh; min-height: 0; }
body[data-view="sam"] .app-main { min-height: 0; }
body[data-view="sam"] .content {
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding-bottom: calc(var(--tabbar-h) + var(--safe-bottom) + var(--s2));
}

#screen-sam.active {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.thread {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--s4);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

.msg { max-width: 84%; }

/* El usuario a la derecha con el tinte cálido del mockup; Sam a la izquierda
   sobre superficie hundida. Ninguna de las dos lleva sombra ni borde: son
   texto agrupado, no tarjetas. */
.msg-user {
  align-self: flex-end;
  padding: 10px var(--s4);
  background: var(--tint-atencion);
  border-radius: var(--r-md) var(--r-md) var(--r-xs) var(--r-md);
}
.msg-sam {
  align-self: flex-start;
  padding: 10px var(--s4);
  background: var(--surface-sunken);
  border-radius: var(--r-md) var(--r-md) var(--r-md) var(--r-xs);
}
.msg-text p { font-size: 0.9375rem; line-height: 1.5; }
.msg-text p + p { margin-top: var(--s2); }
/* 2.1.2 — Palabras largas y URLs parten dentro de la burbuja (también la del
   usuario): antes salían de ella y el hilo se desplazaba a los lados. */
.msg-text { overflow-wrap: anywhere; }

.msg-list { margin: var(--s2) 0 0; padding-left: 1.1em; }
.msg-list li { font-size: 0.9375rem; line-height: 1.5; }
/* Un párrafo que sigue a una lista necesita aire; `p + p` no lo cubría. */
.msg-list + p { margin-top: var(--s3); }

/* Acciones bajo la respuesta: es lo que convierte la conversación en algo que
   ocurre. Van fuera de la burbuja para que se lean como decisiones, no como
   parte del texto de Sam. */
.msg-group { display: flex; flex-direction: column; align-items: flex-start; max-width: 84%; }
.msg-group .msg { max-width: 100%; }

/* Apiladas y no en fila: en 390px una fila de pastillas se parte en dos y
   queda un botón huérfano. Apiladas se leen como una lista de decisiones. */
.msg-actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--s2);
  margin-top: var(--s3);
  width: 100%;
}
.msg-action {
  min-height: 40px;
  padding: 0 var(--s4);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.msg-action:hover { background: var(--action); color: var(--action-ink); border-color: transparent; }

.msg-retry {
  margin-top: var(--s2);
  border: 0; background: transparent;
  color: var(--ctx-atencion);
  font-size: 0.8125rem; font-weight: 500;
  cursor: pointer; padding: var(--s2) 0;
  min-height: 40px;
}

/* Tres puntos, sin texto. "Sam está escribiendo…" es cromo. */
.sam-typing { display: flex; gap: 6px; padding: var(--s1) 0; }
.sam-typing i {
  width: 6px; height: 6px;
  border-radius: var(--r-full);
  background: var(--ink-faint);
  animation: sam-dot 1.2s var(--ease) infinite;
}
.sam-typing i:nth-child(2) { animation-delay: .15s; }
.sam-typing i:nth-child(3) { animation-delay: .3s; }
@keyframes sam-dot { 0%,60%,100% { opacity: .3; } 30% { opacity: 1; } }

/* -------------------------------------------------------------- VACÍO --
   El husky grande aparece SOLO aquí y solo mientras no hay conversación.
   En cuanto se escribe algo, desaparece: Sam acompaña, no decora. */
.sam-hello {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--s6) var(--s4);
}
/* 2.1.2 — El saludo cede su alto y desliza por dentro: con la fila de
   preguntas rápidas empujaba el cuadro de texto bajo la barra inferior en
   pantallas de 667 px de alto o menos. `safe` evita que, al no caber, el
   centrado corte la parte de arriba. */
.sam-hello { min-height: 0; overflow-y: auto; justify-content: safe center; }
/* En pantallas bajas (320×568) el saludo aprieta sus márgenes para que los
   arranques quepan sin desplazar. */
@media (max-width: 899px) and (max-height: 620px) {
  .sam-hello { padding-top: var(--s3); padding-bottom: var(--s3); }
  .sam-hello .sam-mark-lg { width: 44px; height: 44px; }
  .sam-hello .sam-starters { margin-top: var(--s4); }
}
.sam-mark-lg { width: 56px; height: 56px; }
.sam-hello-title {
  margin-top: var(--s4);
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
  color: var(--ink-strong);
}
.sam-hello-sub {
  margin-top: var(--s2);
  max-width: 30ch;
  color: var(--ink-muted);
  font-size: 0.875rem;
  line-height: 1.5;
}
.sam-starters {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  width: 100%;
  max-width: 340px;
  margin-top: var(--s6);
}
.sam-starter {
  min-height: var(--tap);
  padding: 0 var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.875rem;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.sam-starter:hover { background: var(--surface-hover); border-color: var(--line-strong); }

/* 2.1.2 — Fila de preguntas rápidas (antes, estilo en línea en sam.js). Los
   chips toman el borde y la superficie de los arranques de encima. En el
   móvil la fila desliza y se desvanece a la derecha: el desvanecido (40 px)
   cae sobre el chip que sigue, así se lee «hay más» en cualquier ancho; el
   relleno final deja el último chip entero antes del desvanecido. En
   escritorio no hay rueda horizontal: caben todos en renglones centrados. */
.sam-rapidas { display: flex; gap: var(--s2); overflow-x: auto; padding: 0 0 var(--s2); scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.sam-rapidas::-webkit-scrollbar { display: none; }
.sam-rapida { border-color: var(--line); background: var(--surface); color: var(--ink); }
.sam-rapida:hover { background: var(--surface-hover); border-color: var(--line-strong); }
/* En Color, el mismo tono que los arranques (ver [data-color-mode] .sam-starter). */
[data-color-mode="color"] .sam-rapida { border-color: color-mix(in srgb, var(--color-blue) 22%, var(--line)); background: color-mix(in srgb, var(--color-blue) 6%, var(--surface)); }
[data-color-mode="color"] .sam-rapida:hover { border-color: color-mix(in srgb, var(--color-blue) 40%, var(--line)); background: color-mix(in srgb, var(--color-blue) 12%, var(--surface)); }
@media (max-width: 899px) {
  .sam-rapidas {
    padding-right: var(--s8);
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--s8)), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - var(--s8)), transparent);
  }
}
@media (min-width: 900px) {
  .sam-rapidas { flex-wrap: wrap; justify-content: center; overflow: visible; }
}
/* 2.1.2 — Marca «Respuesta instantánea…» (antes en línea): discreta pero
   legible (--ink-muted; con --ink-faint daba 2.5:1) y alineada con la frase
   de arriba. El rayo mide 1em y se centra con el PRIMER renglón: con letra
   grande ya no se encima al texto. */
.sam-quick-mark { display: flex; align-items: flex-start; gap: 4px; margin: var(--s2) 0 0 var(--s1); color: var(--ink-muted); font-size: 0.75rem; line-height: 1.4; }
.sam-quick-mark-icon { display: inline-flex; flex: none; width: 1em; height: 1em; margin-top: .2em; }

.composer-bar { padding-top: var(--s3); background: var(--paper); }

@media (min-width: 900px) {
  body[data-view="sam"] .content { padding-bottom: var(--s6); }
  .msg, .msg-group { max-width: 62%; }
  .msg-actions { flex-direction: row; flex-wrap: wrap; }
  .msg-actions .msg-action { flex: 0 0 auto; }
  .composer-bar { width: 100%; max-width: 780px; margin-inline: auto; }
  .thread { width: 100%; max-width: 780px; margin-inline: auto; }
}

/* El micrófono señala que está escuchando con color, no con un halo. */
.composer-tool.listening { color: var(--ctx-atencion); background: var(--tint-atencion); }

/* ============================================================================
   CALENDARIO · ÁREAS · CONFIGURACIÓN
   ========================================================================== */
.day-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  margin-bottom: var(--s4);
}

.setting-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  min-height: var(--tap);
  padding: var(--s2) 0;
}
.setting-row > span { font-size: 0.9375rem; }

/* 1.31.2 — --ink-faint a 12px daba 2.55:1. Es texto pequeño y de servicio,
   pero es texto: sube a --ink-muted, que es la tinta secundaria de la casa. */
.settings-build {
  margin-top: var(--s4);
  color: var(--ink-muted);
  font-size: 0.75rem;
  text-align: center;
}

/* ============================================================================
   PROPUESTAS DE SAM
   ----------------------------------------------------------------------------
   Una propuesta no es un mensaje ni un botón: es algo que va a pasar y que
   todavía no ha pasado. Por eso tiene superficie propia y muestra los campos
   concretos. Confirmar a ciegas no es confirmar.
   ========================================================================== */
.proposal {
  margin-top: var(--s3);
  padding: var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
  width: 100%;
}

.prop-head { display: flex; flex-direction: column; gap: 2px; }
.prop-verb {
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.prop-title { font-size: 0.9375rem; font-weight: 500; color: var(--ink); }

/* 1.22.0 — Tres campos ocupaban media pantalla de teléfono y no cabía una
   segunda propuesta sin deslizar. El aire entre filas baja de --s2 a 5px: se
   siguen leyendo separadas y entra casi el doble de tarjeta. */
.prop-fields {
  margin-top: var(--s3);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.prop-field { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4); }
.prop-field span { font-size: 0.8125rem; color: var(--ink-muted); flex: 0 0 auto; }
.prop-field b {
  font-size: 0.875rem; font-weight: 500; color: var(--ink);
  text-align: right; min-width: 0; word-break: break-word;
}
.prop-field b.strong {
  font-family: var(--font-num);
  font-size: 1rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.prop-actions { display: flex; gap: var(--s2); margin-top: var(--s4); }
.prop-actions .btn { flex: 1; }

/* Confirmada: la tarjeta se queda como recibo. No desaparece, porque el
   usuario debe poder ver qué autorizó. */
.proposal.done { border-color: transparent; background: var(--tint-finanzas); }
.prop-done {
  display: flex; align-items: center; gap: var(--s2);
  margin-top: var(--s3);
  font-size: 0.8125rem; font-weight: 500;
  color: var(--ctx-finanzas);
}
.prop-done svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.4; }

.proposal.blocked { border-color: transparent; background: var(--tint-atencion); }
.prop-blocked { margin-top: var(--s3); font-size: 0.8125rem; color: var(--ctx-atencion); }

/* Lo que el servidor descartó. Se dice; no se esconde. */
.msg-rejected {
  margin-top: var(--s2);
  padding: var(--s3) var(--s4);
  background: var(--surface-sunken);
  border-radius: var(--r-sm);
  font-size: 0.8125rem;
  color: var(--ink-muted);
}
.msg-rejected strong { color: var(--ink); font-weight: 500; }


/* ==========================================================================
   SAMAPP 1.0.2 · inputs inline y configuración accionable
   ========================================================================== */
.sheet-form { display: flex; flex-direction: column; gap: var(--s4); }
.sheet-form .field { display: flex; flex-direction: column; gap: var(--s2); }
.sheet-form .field label { font-size: 0.8125rem; font-weight: 600; color: var(--ink-muted); }
.sheet-form .field input,
.sheet-form .field select,
.sheet-form .field textarea {
  width: 100%; min-height: var(--tap); padding: 10px 12px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface-sunken); color: var(--ink); font: inherit; outline: none;
}
.sheet-form .field textarea { min-height: 88px; resize: vertical; line-height: 1.5; }
.sheet-form .field input:focus,
.sheet-form .field select:focus,
.sheet-form .field textarea:focus { border-color: var(--ink-muted); }

.manual-task-date {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.manual-task-date legend {
  margin-bottom: var(--s2);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ink-muted);
}
.manual-task-date-options {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
}
.manual-task-date-options label { min-width: 0; cursor: pointer; }
.manual-task-date-options input {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  opacity: 0; pointer-events: none;
}
.manual-task-date-options span {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-full);
  color: var(--ink-muted);
  background: var(--surface);
  font-size: 0.8125rem;
  white-space: nowrap;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.manual-task-date-options input:checked + span {
  background: var(--ink-strong);
  border-color: var(--ink-strong);
  color: var(--paper);
}
.manual-task-date-options input:focus-visible + span {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
.manual-task-custom-date { display: none !important; margin-top: var(--s3); }
.manual-task-form:has(input[name="datePreset"][value="custom"]:checked) .manual-task-custom-date { display: flex !important; }
.field-optional { font-weight: 400; color: var(--ink-faint); }
.manual-task-more {
  border-top: 1px solid var(--line);
  padding-top: var(--s3);
}
.manual-task-more summary {
  color: var(--ink-muted);
  font-size: 0.875rem;
  cursor: pointer;
  list-style: none;
}
.manual-task-more summary::-webkit-details-marker { display: none; }
.manual-task-more summary::after { content: ' +'; color: var(--ink-faint); }
.manual-task-more[open] summary::after { content: ' −'; }
.manual-task-more .field { margin-top: var(--s3); }
.manual-task-actions { display: grid; grid-template-columns: 1fr 1.35fr; gap: var(--s2); }
.sheet:has(.manual-task-form) {
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.sheet:has(.manual-task-form) .sheet-body {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.sheet:has(.manual-task-form) .sheet-foot { flex: 0 0 auto; }
.manual-task-foot {
  padding-top: var(--s3);
  background: var(--surface);
  border-top: 1px solid var(--line);
}

@media (max-width: 360px) {
  /* 2.1.1 — 42 y 40 eran dos formas distintas de quedarse corto: medido a
     320px, «Agregar pendiente», «Nueva lista» y «Nuevo hábito» daban 40x44.
     Cuando el botón se queda en icono es cuando MÁS necesita el objetivo
     completo, porque ya no hay etiqueta que ampliar la zona. */
  .task-add-button { width: var(--tap); min-width: var(--tap); padding: 0; justify-content: center; }
  .task-add-label { display: none; }
  .manual-task-date-options { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* 1.21.7 — Antes esto borraba el texto con `font-size:0` y escribía otro con
   `content:`. Escribir texto desde el CSS lo deja fuera del motor de idiomas
   para siempre; aquí sólo se elige cuál de los dos se enseña. */
.task-add-label .label-short { display: none; }
@media (min-width: 361px) and (max-width: 699px) {
  .task-add-label .label-full { display: none; }
  .task-add-label .label-short { display: inline; font-size: 0.875rem; }
}
.task-add-label b { font-weight: inherit; }

.setting-action {
  width: 100%; min-height: 56px; padding: var(--s3) 0;
  border: 0; border-bottom: 1px solid var(--line); background: transparent;
  color: var(--ink); text-align: left; cursor: pointer;
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
}
.setting-action span { font-size: 0.9375rem; font-weight: 500; }
.setting-action small { font-size: 0.75rem; color: var(--ink-muted); }
/* 1.31.2 · CONTRASTE — El texto destructivo usaba --ctx-atencion, que es un
   TONO (para rellenos y acentos), no una tinta: medido daba 2.04:1 sobre el
   papel, muy por debajo del 4.5 que pide AA para 14.5px. La paleta ya trae la
   variante de tinta con su contraste anotado (--color-red-ink: 6.07 · 5.13 ·
   4.94), y en oscuro se resuelve sola al tono claro. El rojo sigue estando;
   sólo deja de ser ilegible. */
.setting-action.danger-text span { color: var(--color-red-ink); }

.prop-need { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }
.prop-need p { margin: 0 0 var(--s2); font-size: 0.875rem; font-weight: 600; color: var(--ink); }
.prop-account-grid { display: flex; flex-wrap: wrap; gap: var(--s2); }
.account-choice {
  min-height: 38px; padding: 6px 12px; border: 1px solid var(--line-strong);
  border-radius: var(--r-full); background: var(--paper); color: var(--ink);
  font-size: 0.8125rem; cursor: pointer;
}
.account-choice:hover { background: var(--surface-hover); }
.account-choice.account-new { border-style: dashed; color: var(--ink-muted); }

/* Formularios compactos de catálogos financieros. */
.sheet-form .field-check {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 44px;
  font-size: 0.8125rem;
  color: var(--ink);
}
.sheet-form .field-check input { width: 18px; height: 18px; margin: 0; }

/* ============================================================================
   SAMAPP 1.0.3 · acceso rápido a Sam en escritorio
   ----------------------------------------------------------------------------
   Es una acción, no otra sección de navegación. Vive junto al pie del sidebar,
   desaparece dentro de Sam y nunca se muestra en móvil (donde ya existe la
   pestaña central de Sam).
   ========================================================================== */
.quick-sam-desktop { display: none; }

@media (min-width: 900px) {
  .sidebar-foot {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
  }
  .quick-sam-desktop {
    display: flex;
    align-items: center;
    gap: var(--s2);
    min-height: 40px;
    padding: 0 var(--s3);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--ink);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    text-align: left;
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
  }
  .quick-sam-desktop:hover {
    background: var(--surface-sunken);
    border-color: var(--ink-faint);
  }
  body[data-view="sam"] .quick-sam-desktop { display: none; }
}

/* ========================================================================== 
   Samapp 1.0.5 — briefings editoriales dentro de Sam
   --------------------------------------------------------------------------
   Superficie neutra + acento contextual. El color identifica el dominio, no
   rellena la tarjeta completa. Así el resumen se lee como briefing y no como
   un minidashboard incrustado en el chat.
   ======================================================================= */
.msg-group.has-summary { width: min(720px, 94%); max-width: none; }
.msg-summary-intro { margin: 0 0 var(--s3) var(--s1); color: var(--ink-muted); font-size: 0.875rem; line-height: 1.5; }
.msg-summary-intro p { margin: 0; }
.msg-summary-intro strong { color: var(--ink); font-weight: 600; }

.sam-brief {
  --brief-accent: var(--ink-muted);
  --brief-tint: var(--surface-sunken);
  position: relative;
  overflow: hidden;
  padding: var(--s5) var(--s5) var(--s5) calc(var(--s5) + 2px);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-card);
  animation: samBriefIn var(--dur) var(--ease) both;
}
/* La franja de acento es la única señal de color "grande" permitida: marca
   la familia del briefing de un vistazo sin teñir la superficie. Se separa
   del contenido con el padding-left extra de arriba, así el texto nunca
   queda pegado a la línea de color. */
.sam-brief::before {
  content: "";
  position: absolute;
  inset: var(--s2) auto var(--s2) 0;
  width: 3px;
  border-radius: var(--r-full);
  background: var(--brief-accent);
  opacity: .85;
}
[data-theme="dark"] .sam-brief::before { opacity: .7; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .sam-brief::before { opacity: .7; }
}
.sam-brief-calendar { --brief-accent: var(--ctx-calendario); --brief-tint: var(--tint-calendario); }
.sam-brief-tasks    { --brief-accent: var(--ctx-tasks);      --brief-tint: var(--tint-tasks); }
.sam-brief-expenses { --brief-accent: var(--ctx-expense);    --brief-tint: var(--tint-expense); }
.sam-brief-income   { --brief-accent: var(--ctx-income);     --brief-tint: var(--tint-income); }
.sam-brief-finance  { --brief-accent: var(--ctx-finanzas);   --brief-tint: var(--tint-finanzas); }
.sam-brief-list     { --brief-accent: var(--ctx-lists);      --brief-tint: var(--tint-lists); }
.sam-brief-day      { --brief-accent: var(--ctx-calendario); --brief-tint: var(--surface-sunken); }

/* ---------------------------------------------------------------------
   "Ponme al día" es el briefing estrella (§16): no es otra tarjeta de la
   misma familia con distinto color, es la única composición que junta
   atención + hoy + mañana + lista en una sola pieza editorial. Se gana un
   trato tipográfico propio — el título ya NO es una etiqueta de categoría
   ("GASTOS", "INGRESOS"), es el saludo personal que genera U.greeting():
   se lee mejor como un titular en el serif de pantalla que como un eyebrow
   en mayúsculas compitiendo con el resto de tarjetas. Nada de esto toca el
   contrato de datos: mismo HTML, mismas clases, mismo texto que produce
   summaries.js. */
.sam-brief-day { padding-top: var(--s6); padding-bottom: var(--s6); }
.sam-brief-day .sam-brief-head { align-items: flex-start; margin-bottom: var(--s5); }
.sam-brief-day .sam-brief-icon { width: 38px; height: 38px; margin-top: 1px; }
.sam-brief-day .sam-brief-icon .i { width: 19px; height: 19px; }
.sam-brief-day .sam-brief-head-copy strong {
  font-family: var(--font-display);
  color: var(--ink-strong);
  font-size: 1.1875rem;
  font-weight: 500;
  letter-spacing: -.01em;
  text-transform: none;
}
.sam-brief-day .sam-brief-head-copy > span { margin-top: 4px; font-size: 0.8125rem; }

.sam-brief .i { display: inline-grid; place-items: center; flex: none; }
.sam-brief .i svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.sam-brief-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
  margin-bottom: var(--s4);
}
.sam-brief-icon {
  width: 34px; height: 34px;
  display: grid; place-items: center; flex: none;
  border-radius: 10px;
  background: var(--brief-tint);
  color: var(--brief-accent);
}
.sam-brief-icon .i { width: 17px; height: 17px; }
.sam-brief-head-copy { min-width: 0; flex: 1; }
/* La etiqueta de familia (GASTOS, INGRESOS…) es contexto, no protagonista:
   vive en tinta media para que la cifra hero de más abajo sea, sin
   competencia, lo primero que se lee de la tarjeta. */
.sam-brief-head-copy strong {
  display: block;
  color: var(--ink-muted);
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.sam-brief-head-copy > span { display: block; margin-top: 4px; color: var(--ink-muted); font-size: 0.8125rem; }
.sam-brief-head-meta {
  flex: none;
  padding: 6px 10px;
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: 0.6875rem;
  font-weight: 600;
  white-space: nowrap;
}

.sam-brief-metric { display: flex; align-items: baseline; gap: var(--s2); margin: 0 0 var(--s4); }
.sam-brief-metric strong {
  color: var(--ink-strong);
  font-size: 32px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
}
.sam-brief-metric span { color: var(--ink-muted); font-size: 0.8125rem; }
.sam-brief-metric.is-money { display: block; margin-bottom: var(--s4); }
.sam-brief-metric.is-money strong {
  display: block;
  font-size: clamp(30px, 8vw, 36px);
  overflow-wrap: anywhere;
}
.sam-brief-metric.is-money span { display: block; margin-top: 4px; font-size: 0.75rem; }

.sam-brief-pills { display: flex; flex-wrap: wrap; gap: 6px; margin: -2px 0 var(--s4); }
.sam-brief-pill { padding: 6px 10px; border-radius: var(--r-full); background: var(--surface-sunken); color: var(--ink-muted); font-size: 0.6875rem; font-weight: 600; }
.sam-brief-pill.is-today { color: var(--brief-accent); background: var(--brief-tint); }
.sam-brief-pill.is-late { color: var(--ctx-atencion); background: var(--tint-atencion); }

.sam-brief-timeline, .sam-brief-rows, .sam-brief-money-list, .sam-brief-payables, .sam-brief-list, .sam-brief-day-list { display: flex; flex-direction: column; }
.sam-brief-event, .sam-brief-row, .sam-brief-money-row, .sam-brief-check, .sam-brief-day-list > button, .sam-brief-next > button, .sam-brief-attention > button {
  width: 100%;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.sam-brief-event, .sam-brief-row, .sam-brief-money-row {
  min-height: 48px;
  border-top: 1px solid var(--line);
}
.sam-brief-event:first-child, .sam-brief-row:first-child, .sam-brief-money-row:first-child { border-top: 0; }
.sam-brief-event {
  display: grid;
  grid-template-columns: 44px 16px minmax(0, 1fr) 14px;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
}
/* Conector de línea de tiempo (§1 1.0.7): cada evento dibuja su propio
   segmento de línea a través del punto, extendido 1px más allá del borde
   de su propia fila. Como la fila vecina hace lo mismo, los segmentos se
   tocan en el hairline compartido y el conjunto se lee como una sola
   línea continua — sin depender de que todas las filas midan lo mismo. */
.sam-brief-dot-track {
  position: relative;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
}
.sam-brief-dot-track::before {
  content: "";
  position: absolute;
  top: -11px; bottom: -11px;
  left: 50%;
  width: 1px;
  transform: translateX(-50%);
  background: var(--line);
}
.sam-brief-event:first-child .sam-brief-dot-track::before { top: 50%; }
.sam-brief-event:last-child .sam-brief-dot-track::before { bottom: 50%; }
.sam-brief-dot-track .sam-brief-dot { position: relative; z-index: 1; }
.sam-brief-event > .i, .sam-brief-row > .i, .sam-brief-money-row > .i, .sam-brief-day-list > button > .i, .sam-brief-next > button > .i, .sam-brief-attention > button > .i { width: 14px; height: 14px; color: var(--ink-faint); }
.sam-brief-time { color: var(--brief-accent); font-size: 0.75rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.sam-brief-time.is-due { color: var(--ctx-atencion); }
.sam-brief-event-body, .sam-brief-row-main, .sam-brief-money-row > span { min-width: 0; }
.sam-brief-event-body b, .sam-brief-row-main b, .sam-brief-money-row b, .sam-brief-day-list b, .sam-brief-next b, .sam-brief-attention b {
  display: block;
  overflow-wrap: anywhere;
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.35;
}
.sam-brief-event-body small, .sam-brief-row-main small, .sam-brief-money-row small, .sam-brief-payable small, .sam-brief-day-list small, .sam-brief-next small, .sam-brief-attention small {
  display: block;
  margin-top: 4px;
  color: var(--ink-muted);
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.35;
}
.sam-brief-row { display: grid; grid-template-columns: 9px minmax(0,1fr) 14px; align-items: center; gap: 10px; padding: 10px 0; }
.sam-brief-dot { width: 9px; height: 9px; border-radius: var(--r-full); background: var(--brief-accent); flex: none; }
.sam-brief-dot.is-late { background: var(--ctx-atencion); }

.sam-brief-insight {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: -2px 0 var(--s4);
  padding: 10px 10px;
  border-radius: var(--r-sm);
  background: var(--brief-tint);
  color: var(--ink-muted);
  font-size: 0.75rem;
  line-height: 1.35;
}
.sam-brief-insight .i { width: 15px; height: 15px; color: var(--brief-accent); }
.sam-brief-insight b { color: var(--ink); font-weight: 600; }
/* Barras finas y sin eje: comparan proporción, no pretenden ser un chart. */
.sam-brief-bars { display: flex; flex-direction: column; gap: 10px; margin: 0 0 var(--s4); }
.sam-brief-bar-row > div { display: flex; justify-content: space-between; gap: var(--s4); color: var(--ink-muted); font-size: 0.75rem; }
.sam-brief-bar-row b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.sam-brief-bar { display: block; height: 4px; overflow: hidden; margin-top: 6px; border-radius: var(--r-full); background: color-mix(in srgb, var(--ink) 9%, transparent); }
.sam-brief-bar i { display: block; height: 100%; border-radius: inherit; background: var(--brief-accent); opacity: .78; }
.sam-brief-money-row { display: grid; grid-template-columns: minmax(0,1fr) auto 14px; align-items: center; gap: var(--s3); padding: 10px 0; }
.sam-brief-money-row > strong { color: var(--ink); font-size: 0.8125rem; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }

.sam-brief-finance-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0,1fr));
  margin: 0 0 var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}
.sam-brief-finance-strip > div { min-width: 0; padding: 0 var(--s3); }
.sam-brief-finance-strip > div:first-child { padding-left: 0; }
.sam-brief-finance-strip > div + div { border-left: 1px solid var(--line); }
.sam-brief-finance-strip span { display: block; color: var(--ink-muted); font-size: 0.6875rem; }
.sam-brief-finance-strip b { display: block; margin-top: 4px; overflow: hidden; color: var(--ink); font-size: 0.8125rem; font-weight: 600; font-variant-numeric: tabular-nums; text-overflow: ellipsis; white-space: nowrap; }
.sam-brief-section-label { margin: var(--s4) 0 6px; color: var(--ink-muted); font-size: 0.6875rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; }
.sam-brief-payable { display: grid; grid-template-columns: minmax(0,1fr) auto auto; align-items: center; gap: 10px; min-height: 50px; padding: 10px 0; border-top: 1px solid var(--line); }
.sam-brief-payable:first-child { border-top: 0; }
.sam-brief-payable > span { min-width: 0; }
.sam-brief-payable > span b { display: block; overflow-wrap: anywhere; color: var(--ink); font-size: 0.8125rem; font-weight: 500; }
.sam-brief-payable > span small { color: var(--ink-muted); }
.sam-brief-payable > strong { color: var(--ink); font-size: 0.8125rem; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Cierre de "Próximos pagos/cobros" (§5 1.0.7): mismo patrón que la franja
   de Finanzas — hairline arriba, label chico, cifra fuerte a la derecha.
   El total es siempre la suma REAL de todos los compromisos abiertos de
   ese tipo, no solo de las 3 filas visibles (lo calcula summaries.js). */
.sam-brief-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  margin-top: 2px;
  padding: 10px 0 2px;
  border-top: 1px solid var(--line);
}
.sam-brief-total span { color: var(--ink-muted); font-size: 0.75rem; font-weight: 600; }
.sam-brief-total strong { color: var(--ink-strong); font-size: 0.875rem; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }

.sam-brief-list-lead { display: flex; align-items: baseline; gap: 6px; margin-bottom: var(--s3); }
.sam-brief-list-lead strong { color: var(--ink-strong); font-size: 28px; font-weight: 600; line-height: 1; letter-spacing: -.02em; }
.sam-brief-list-lead span { color: var(--ink-muted); font-size: 0.8125rem; }
.sam-brief-progress { display: flex; align-items: stretch; gap: 2px; height: 5px; margin-bottom: var(--s4); }
.sam-brief-progress i { flex: 1; display: block; height: 100%; border-radius: 2px; background: color-mix(in srgb, var(--ink) 9%, transparent); transition: background-color .3s var(--ease); }
.sam-brief-progress i.is-filled { background: var(--brief-accent); }
.sam-brief-progress i:first-child { border-top-left-radius: var(--r-full); border-bottom-left-radius: var(--r-full); }
.sam-brief-progress i:last-child { border-top-right-radius: var(--r-full); border-bottom-right-radius: var(--r-full); }
.sam-brief-check { display: flex; align-items: center; gap: 10px; min-height: 42px; padding: 8px 0; border-top: 1px solid var(--line); font-size: 0.875rem; transition: color var(--dur) var(--ease); }
.sam-brief-check:first-child { border-top: 0; }
.sam-brief-checkbox {
  width: 18px; height: 18px; flex: none;
  border: 1.5px solid var(--brief-accent);
  border-radius: 6px;
  background: var(--surface);
  opacity: .8;
}

.sam-brief-attention { margin-bottom: var(--s4); padding: 10px 12px; border-left: 3px solid var(--ctx-atencion); border-radius: 0 var(--r-sm) var(--r-sm) 0; background: var(--tint-atencion); }
.sam-brief-attention > span { display: block; margin-bottom: 4px; color: var(--ctx-atencion); font-size: 0.625rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.sam-brief-attention > button, .sam-brief-next > button { display: grid; grid-template-columns: minmax(0,1fr) 14px; align-items: center; gap: var(--s2); padding: 0; }
.sam-brief-attention > button > span, .sam-brief-next > button > span { min-width: 0; }
.sam-brief-calm { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s4); padding: 10px 10px; border-radius: var(--r-sm); background: var(--tint-finanzas); color: var(--ctx-finanzas); font-size: 0.8125rem; }
.sam-brief-calm .i { width: 15px; height: 15px; }
.sam-brief-day-list > button { display: grid; grid-template-columns: 9px minmax(0,1fr) 14px; align-items: center; gap: 10px; min-height: 46px; padding: 10px 0; border-top: 1px solid var(--line); border-radius: var(--r-xs); }
.sam-brief-day-list > button:first-child { border-top: 0; }
.sam-brief-quiet { padding: 10px 0; color: var(--ink-muted); font-size: 0.8125rem; }
.sam-brief-next { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }
.sam-brief-next > span { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; color: var(--ink-muted); font-size: 0.6875rem; font-weight: 700; letter-spacing: .045em; text-transform: uppercase; }
.sam-brief-next > span .i { width: 13px; height: 13px; color: var(--brief-accent); }

/* 1.21.9 — 34 px se queda corto para un dedo: el mínimo cómodo es --tap
   (44 px). En escritorio, con ratón, la altura menor no estorba y mantiene la
   tarjeta compacta; el ensanche va en la media query de móvil, más abajo. */
.sam-brief-more { display: inline-flex; align-items: center; gap: 4px; min-height: 34px; padding: var(--s2) 0 0; border: 0; background: transparent; color: var(--brief-accent); font: inherit; font-size: 0.75rem; font-weight: 600; cursor: pointer; transition: gap var(--dur) var(--ease); }
.sam-brief-more:hover { gap: 6px; }
.sam-brief-more .i { width: 13px; height: 13px; }
.sam-brief-actions { display: flex; justify-content: flex-end; }
.sam-brief-hidden { padding-top: 6px; color: var(--ink-faint); font-size: 0.6875rem; }
.sam-brief-empty { display: flex; flex-direction: column; gap: 4px; padding: var(--s3) 0; color: var(--ink-muted); font-size: 0.8125rem; }
.sam-brief-empty b { color: var(--ink); font-size: 0.875rem; font-weight: 600; }

/* 2.1.2 — Tarjetas de las preguntas rápidas («Hoy», «Tu semana», «Pagos
   pendientes»). Mismas piezas que los demás briefings; sólo se añade la ficha
   de hora o día a la derecha de la fila, el acento de lo vencido y el pie con
   los botones de la respuesta (lleva también .msg-actions). */
.sam-brief-qrow.has-chip { grid-template-columns: 9px minmax(0,1fr) auto 14px; }
/* Títulos a dos renglones con elipsis: con títulos de 120 letras cada fila
   medía seis. El texto completo sigue en la fila (lector de pantalla) y el
   toque abre el pendiente. */
.sam-brief-qrow .sam-brief-row-main b { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.sam-brief-qchip {
  justify-self: end;
  padding: 3px 8px;
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--ink);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
[data-color-mode="color"] .sam-brief-quick .sam-brief-qchip { background: var(--surface); }
[data-color-mode="color"] .sam-brief-quick .sam-brief-qrow { border-radius: var(--r-sm); padding-inline: var(--s3); background: var(--ctx-tint, var(--brief-tint)); border-top-color: transparent; }
[data-color-mode="color"] .sam-brief-quick .sam-brief-qrow + .sam-brief-qrow { margin-top: 6px; }
.sam-brief-section-label.is-late, .sam-brief-quick .is-late { color: color-mix(in srgb, var(--ctx-atencion) 40%, var(--ink-strong)); }
/* La píldora de acento («243 hoy», «N días de racha») tomaba el tono puro
   sobre su propio tinte: 1.48:1 con los pasteles del tema claro. Mezclado con
   la tinta fuerte conserva el matiz y se lee. */
.sam-brief-pill.is-today { color: color-mix(in srgb, var(--brief-accent) 40%, var(--ink-strong)); }
.sam-brief-quick, .sam-brief-expenses, .sam-brief-income { align-self: stretch; }
/* Separador recto entre filas (con el radio de la fila, sus puntas se curvaban). */
.sam-brief-quick .sam-brief-qrow, .sam-brief-quick .sam-brief-qmoney { border-top-left-radius: 0; border-top-right-radius: 0; }
/* 2.1.2 — La tarjeta de gastos/ingresos sigue el mismo patrón que las otras
   cuatro: separador recto y el enlace de la pantalla a la derecha. */
.sam-brief-expenses .sam-brief-money-row, .sam-brief-income .sam-brief-money-row { border-top-left-radius: 0; border-top-right-radius: 0; }
.sam-brief-expenses > .sam-brief-more, .sam-brief-income > .sam-brief-more { display: flex; width: fit-content; margin-left: auto; }
.sam-brief-qrow small { overflow-wrap: anywhere; }
[data-color-mode="color"] .sam-brief-quick .sam-brief-qrow small { color: color-mix(in srgb, var(--ink-muted) 60%, var(--ink)); }
/* Filas angostas (320 px, o letra grande): la ficha y el importe bajan bajo
   el título en vez de apretarlo o de ensanchar la tarjeta (con miles de
   millones y letra al 200 % el gasto aún desplazaba el hilo 8 px a 320).
   `rem` sigue a la escala de letra. */
.sam-brief-quick .sam-brief-rows, .sam-brief-quick .sam-brief-money-list,
.sam-brief-expenses .sam-brief-money-list, .sam-brief-income .sam-brief-money-list { container-type: inline-size; }
@container (max-width: 15.5rem) {
  .sam-brief-qrow.has-chip { grid-template-columns: 9px minmax(0,1fr) 14px; row-gap: 6px; }
  .sam-brief-qrow.has-chip > .sam-brief-qchip { grid-column: 2; grid-row: 2; justify-self: start; }
  .sam-brief-qrow.has-chip > .sam-brief-dot { grid-row: 1 / span 2; }
  .sam-brief-qrow.has-chip > .i { grid-column: 3; grid-row: 1 / span 2; }
  .sam-brief-money-row { grid-template-columns: minmax(0,1fr) 14px; row-gap: 2px; }
  .sam-brief-money-row > strong { grid-column: 1; grid-row: 2; white-space: normal; overflow-wrap: anywhere; }
  .sam-brief-money-row > .i { grid-column: 2; grid-row: 1 / span 2; }
  .sam-brief-money-list .sam-brief-money-row > span:first-child > b { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; white-space: normal; overflow-wrap: anywhere; }
}
.sam-brief-quick-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; column-gap: var(--s3); margin-top: var(--s2); }
.sam-brief-quick-note { margin-right: auto; padding-top: var(--s2); color: var(--ink-muted); font-size: 0.75rem; }
.sam-brief .msg-actions.sam-brief-actions { flex-direction: row; flex-wrap: wrap; justify-content: flex-end; align-items: center; column-gap: var(--s4); row-gap: 0; width: auto; margin-top: 0; }
/* Con letra grande el total baja de renglón en vez de ensanchar la tarjeta. */
.sam-brief-total { flex-wrap: wrap; }
.sam-brief-total strong { margin-left: auto; }

/* 2.1.2 — Con letra grande la cifra baja bajo la categoría y la píldora de la
   cabecera baja de renglón, en vez de ensanchar la tarjeta y desplazar el hilo
   a los lados. Con letra normal no cambia nada. */
.sam-brief-bar-row > div { flex-wrap: wrap; }
.sam-brief-bar-row > div > span { flex: 1 1 8rem; min-width: 0; overflow-wrap: anywhere; }
.sam-brief-bar-row > div > b { margin-left: auto; }
.sam-brief-head { flex-wrap: wrap; }
.sam-brief-head-copy { flex: 1 1 6rem; }

/* Toda fila interactiva de un briefing comparte el mismo lenguaje táctil:
   fondo hundido al pasar el mouse, ligero desplazamiento al presionar. Nada
   de esto anima en `prefers-reduced-motion` — el catálogo global de tokens
   ya vacía las duraciones de transición para ese caso. */
.sam-brief button:hover { color: var(--ink); }
.sam-brief-event, .sam-brief-row, .sam-brief-money-row, .sam-brief-check, .sam-brief-day-list > button, .sam-brief-next > button, .sam-brief-attention > button {
  border-radius: var(--r-xs);
  transition: background-color var(--dur) var(--ease), transform .12s var(--ease);
}
.sam-brief-event:hover, .sam-brief-row:hover, .sam-brief-money-row:hover, .sam-brief-check:hover, .sam-brief-day-list > button:hover, .sam-brief-next > button:hover, .sam-brief-attention > button:hover { background: var(--surface-sunken); }
.sam-brief-event:active, .sam-brief-row:active, .sam-brief-money-row:active, .sam-brief-day-list > button:active, .sam-brief-next > button:active, .sam-brief-attention > button:active { transform: translateX(1px) scale(.997); }
.sam-brief button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: var(--r-xs); }

@keyframes samBriefIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
  .sam-brief { animation: none; }
}

@media (max-width: 520px) {
  .msg-group.has-summary { width: calc(100% - 4px); max-width: none; }
  .sam-brief { padding: var(--s4) var(--s4) var(--s4) calc(var(--s4) + 2px); border-radius: var(--r-md); }
  .sam-brief-head { margin-bottom: var(--s3); }
  .sam-brief-metric strong { font-size: 29px; }
  .sam-brief-metric.is-money strong { font-size: 30px; }
  .sam-brief-event { grid-template-columns: 40px 14px minmax(0,1fr) 12px; gap: 8px; }
  .sam-brief-payable { grid-template-columns: minmax(0,1fr) auto; }
  .sam-brief-payable .btn { grid-column: 1 / -1; justify-self: start; }
  .sam-brief-day { padding-top: var(--s5); padding-bottom: var(--s5); }
  .sam-brief-day .sam-brief-head-copy strong { font-size: 1.125rem; }
}

@media (max-width: 380px) {
  .sam-brief-finance-strip { grid-template-columns: 1fr 1fr; }
  .sam-brief-finance-strip > div:nth-child(3) { grid-column: 1 / -1; padding-left: 0; border-top: 1px solid var(--line); border-left: 0; padding-top: var(--s2); }
  .sam-brief-head-meta { display: none; }
}

/* --------------------------------------------------------- ESCRITORIO --
   El briefing puede ser más ancho que una respuesta de texto normal (§17),
   pero sigue sin llenar el thread completo: 600px lee cómodo sin que la
   vista tenga que viajar de un lado a otro entre etiqueta y cifra. */
@media (min-width: 900px) {
  .msg-group.has-summary { width: min(600px, 100%); }
}


/* ============================================================================
   SAMAPP 1.0.8 · FINANZAS + CALENDARIO
   ----------------------------------------------------------------------------
   Unifica las pantallas nativas con el lenguaje editorial de los briefings.
   No cambia datos ni reglas de dominio: solo composición y navegación visual.
   ========================================================================== */

/* 1.14 — Aqui vivia `.finance-overview`, el hero navy con el saldo y una
   tira de tres cifras (Ingresos / Gastos / Balance). Lo sustituyo la banda
   "Disponible" de finanzas.css y ningun archivo lo genera ya: comprobado
   contra todos los .js, .html y .php de public_html. Se van sus 30 reglas
   y las 7 de su media query de 480px. */

/* --------------------------------------------------------------- CALENDARIO */
.calendar-screen-head {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}
.calendar-view-tabs {
  display: inline-grid;
  grid-template-columns: repeat(3, 1fr);
  align-self: flex-start;
  padding: 4px;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
.calendar-view-tab {
  min-width: 70px;
  min-height: var(--tap);
  padding: 0 var(--s3);
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
  transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.calendar-view-tab[aria-selected="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-low);
}
.calendar-nav {
  display: grid;
  grid-template-columns: var(--tap) minmax(0, auto) var(--tap);
  align-items: center;
  justify-content: start;
  gap: var(--s2);
  margin-bottom: var(--s5);
}
.calendar-nav-arrow {
  width: var(--tap);
  height: var(--tap);
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
  display: grid;
  place-items: center;
}
.calendar-nav-arrow:hover { background: var(--surface-sunken); color: var(--ink); }
.calendar-nav-arrow svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.calendar-nav-current {
  min-height: var(--tap);
  max-width: min(520px, calc(100vw - 150px));
  padding: 0 var(--s4);
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 500;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}
.calendar-stage { min-width: 0; }

/* Día: la línea de tiempo del briefing crece a página completa sin convertirse
   en una tarjeta gigante. */
.calendar-day-layout { display: block; }
.calendar-day-main { min-width: 0; }
.calendar-day-aside { margin-top: var(--s6); }
/* 1.32.0 — LOS CUATRO ELEMENTOS DE UNA FILA DE AGENDA SE ALINEAN ENTRE SÍ.
   Medido en el DOM antes de tocar nada: en una fila de 64px los centros
   caían en cuatro alturas distintas —hora 288, punto 294, título 299,
   «...» 305—, 17px de dispersión dentro de la MISMA fila. Nadie sabe
   nombrarlo al verlo, pero es exactamente lo que hace que una pantalla
   parezca sin terminar.

   La causa es de rejilla, no de gusto: la fila es `align-items: start`, así
   que cada celda se apoya arriba con su propio desfase (padding de la hora,
   margen del punto, margen+padding del cuerpo), mientras que el botón usaba
   `align-self: center` y se centraba en la fila COMPLETA, no en la tarjeta.

   Ahora todo se registra contra una sola referencia: la primera línea del
   título. Se declara una vez y las cuatro celdas la usan, de modo que si
   mañana cambia el cuerpo del título no hay que recalcular a mano. */
.calendar-agenda {
  /* 2.1.2 — La columna de la hora crece con la letra del sistema: con letra
     grande «10:00 a.m.» se montaba sobre el riel y la tarjeta. El em se
     resuelve en la fila (15 px), así que al 100 % sigue en 68 px. */
  --calendar-time-width: max(68px, 4.5em);
  /* Dónde empieza la caja del título dentro de la fila: el cuerpo lleva
     6px de margen + 9px de relleno superior. */
  --agenda-top: 15px;
  /* Media caja de línea del título (15px x 1.35 / 2), que es la mitad de su
     primer renglón: sumada a --agenda-top da la línea óptica de la fila. */
  --agenda-medio: calc(var(--agenda-top) + (15px * 1.35) / 2);
  width: 100%;
}
.calendar-agenda-row {
  position: relative;
  display: grid;
  grid-template-columns: var(--calendar-time-width) 22px minmax(0, 1fr) var(--tap);
  align-items: start;
  min-height: 64px;
}
.calendar-agenda-time {
  /* La hora comparte la CAJA de línea del título (20px), no su tamaño, para
     que los dos primeros renglones caigan sobre la misma línea óptica. */
  padding: var(--agenda-top) var(--s3) 0 0;
  line-height: 20px;
  color: var(--ink-muted);
  font-family: var(--font-num);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.calendar-agenda-track {
  position: relative;
  align-self: stretch;
  display: flex;
  justify-content: center;
}
.calendar-agenda-track::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  transform: translateX(-50%);
  background: var(--line-strong);
}
.calendar-agenda-row:first-child .calendar-agenda-track::before { top: 19px; }
.calendar-agenda-row:last-child .calendar-agenda-track::before { bottom: calc(100% - 20px); }
.calendar-agenda-track span {
  position: relative;
  z-index: 1;
  width: 9px;
  height: 9px;
  /* centro del punto = línea óptica; 4.5px es su propio radio */
  margin-top: calc(var(--agenda-medio) - 4.5px);
  border: 2px solid var(--surface);
  border-radius: var(--r-full);
  background: var(--ctx, var(--ctx-calendario));
  box-shadow: 0 0 0 1px var(--ctx, var(--ctx-calendario));
}
.calendar-agenda-body {
  min-width: 0;
  margin: 6px 0;
  padding: 9px var(--s4) 10px;
  border-bottom: 1px solid var(--line);
}
.calendar-agenda-row:last-child .calendar-agenda-body { border-bottom-color: transparent; }
/* 1.31.3 — Este título no controlaba el desbordamiento y su hermana
   `.row-title` sí. Con un título de una sola palabra larga —pegar una URL
   basta— el documento entero se ensanchaba: medido, `scrollWidth` 783px con la
   ventana en 320 y en 390, y al desplazar a la derecha la pantalla quedaba EN
   BLANCO, sólo con la barra inferior. Afectaba a las vistas Día y Semana.
   Aquí se parte la palabra en vez de recortarla con puntos suspensivos: en la
   agenda el título es el contenido principal de la fila y leerlo entero en dos
   renglones es mejor que perderlo. */
.calendar-agenda-title {
  min-width: 0;
  color: var(--ink);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.35;
  overflow-wrap: anywhere;
}
.calendar-agenda-meta {
  margin-top: 2px;
  color: var(--ink-muted);
  font-size: 0.8125rem;
}
.calendar-agenda-meta .ctx { color: var(--ctx, var(--ink-muted)); font-weight: 500; }
.calendar-agenda-more { align-self: start; margin-top: calc(var(--agenda-medio) - var(--tap) / 2); margin-right: 0; }
.calendar-empty {
  max-width: 620px;
  padding: var(--s6) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.calendar-empty p { color: var(--ink); font-size: 0.9375rem; font-weight: 500; }
.calendar-empty span { display: block; margin-top: 2px; color: var(--ink-muted); font-size: 0.8125rem; }
.calendar-inline-date {
  margin: var(--s4) 0 var(--s2);
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 600;
}

/* Semana desktop: una sola superficie con 7 columnas, no siete tarjetas. */
.calendar-week-grid {
  display: none;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}
.calendar-week-day {
  min-width: 0;
  min-height: 430px;
  background: var(--surface);
}
.calendar-week-day + .calendar-week-day { border-left: 1px solid var(--line); }
.calendar-week-day.selected { background: color-mix(in srgb, var(--tint-calendario) 52%, var(--surface)); }
.calendar-week-head {
  width: 100%;
  min-height: 66px;
  padding: var(--s3) var(--s2);
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  text-align: center;
}
.calendar-week-head span {
  display: block;
  color: var(--ink-muted);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.calendar-week-head strong {
  display: inline-block;
  margin-top: 4px;
  font-family: var(--font-num);
  font-size: 1.125rem;
  font-weight: 500;
  line-height: 1.2;
}
.calendar-week-day.today .calendar-week-head strong {
  color: var(--ctx-calendario);
  border-bottom: 2px solid var(--ctx-calendario);
}
.calendar-week-events { padding: var(--s2); }
.calendar-week-event {
  position: relative;
  display: block;
  width: 100%;
  margin-bottom: 6px;
  padding: 6px 6px 6px 10px;
  overflow: hidden;
  border: 0;
  border-left: 2px solid var(--ctx, var(--ctx-calendario));
  border-radius: 0 var(--r-xs) var(--r-xs) 0;
  background: color-mix(in srgb, var(--ctx-tint, var(--tint-calendario)) 65%, transparent);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.calendar-week-event-time {
  display: block;
  color: var(--ink-muted);
  font-family: var(--font-num);
  font-size: 0.625rem;
  white-space: nowrap;
}
.calendar-week-event-title {
  display: -webkit-box;
  margin-top: 1px;
  overflow: hidden;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.2;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.calendar-week-empty,
.calendar-more-count {
  display: block;
  width: 100%;
  padding: var(--s2);
  border: 0;
  background: transparent;
  color: var(--ink-faint);
  font-size: 0.6875rem;
  text-align: left;
}
.calendar-more-count { color: var(--ctx-calendario); cursor: pointer; font-weight: 500; }

/* Semana móvil: selector compacto de siete días y agenda del seleccionado. */
.calendar-week-strip {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}
.calendar-week-strip-day {
  position: relative;
  min-width: 0;
  min-height: 68px;
  padding: 6px 2px 6px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-muted);
  cursor: pointer;
}
.calendar-week-strip-day[aria-selected="true"] { background: var(--tint-calendario); color: var(--ink); }
.calendar-week-strip-day span { display: block; font-size: 0.625rem; text-transform: uppercase; }
.calendar-week-strip-day strong { display: block; margin-top: 2px; font-family: var(--font-num); font-size: 0.9375rem; font-weight: 500; }
.calendar-week-strip-day.today strong { color: var(--ctx-calendario); }
.calendar-week-strip-day i {
  display: grid;
  place-items: center;
  width: 16px;
  height: 16px;
  margin: 3px auto 0;
  border-radius: var(--r-full);
  color: var(--ink-muted);
  font-size: 0.625rem;
  font-style: normal;
}
.calendar-week-strip-day[aria-selected="true"] i { color: var(--ctx-calendario); }
.calendar-week-mobile-agenda { display: block; }

/* Mes: rejilla real, un único plano editorial. */
.calendar-month {
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
}
.calendar-month-weekdays {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  min-height: 36px;
  align-items: center;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--surface-sunken) 58%, var(--surface));
}
.calendar-month-weekdays span {
  color: var(--ink-muted);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-align: center;
  text-transform: uppercase;
}
.calendar-month-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.calendar-month-day {
  position: relative;
  min-width: 0;
  min-height: 62px;
  padding: 6px 6px;
  overflow: hidden;
  border: 0;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.calendar-month-day:nth-child(7n) { border-right: 0; }
.calendar-month-day.outside { background: color-mix(in srgb, var(--surface-sunken) 40%, var(--surface)); color: var(--ink-faint); }
.calendar-month-day.selected { background: color-mix(in srgb, var(--tint-calendario) 56%, var(--surface)); }
.calendar-month-number {
  display: inline-block;
  min-width: 18px;
  font-family: var(--font-num);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.35;
  text-align: center;
}
.calendar-month-day.today .calendar-month-number {
  color: var(--ctx-calendario);
  border-bottom: 2px solid var(--ctx-calendario);
}
.calendar-month-events { display: none; }
.calendar-month-mobile-count {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  color: var(--ink-muted);
  font-size: 0.625rem;
}
.calendar-month-mobile-count i {
  width: 5px;
  height: 5px;
  border-radius: var(--r-full);
  background: var(--ctx-calendario);
}
.calendar-month-mobile-count b { font-weight: 500; }
.calendar-month-more { display: none; }

@media (max-width: 480px) {
  .calendar-view-tabs { width: 100%; }
  .calendar-view-tab { min-width: 0; }
  .calendar-nav { width: 100%; grid-template-columns: var(--tap) minmax(0, 1fr) var(--tap); justify-content: stretch; }
  .calendar-nav-current { width: 100%; max-width: none; }
  .calendar-agenda { --calendar-time-width: max(58px, 3.85em); } /* 2.1.2 — crece con la letra; al 100 % sigue en 58 px */
  .calendar-agenda-row { grid-template-columns: var(--calendar-time-width) 18px minmax(0, 1fr) var(--tap); }
  .calendar-agenda-time { padding-right: var(--s2); font-size: 0.6875rem; }
  .calendar-agenda-body { padding-inline: var(--s3); }
  .calendar-agenda-more { width: var(--tap); height: var(--tap); }
  .calendar-month-weekdays span { font-size: 0.625rem; letter-spacing: 0; }
}

@media (min-width: 700px) {
  .calendar-screen-head { flex-direction: row; align-items: flex-end; justify-content: space-between; }
  .calendar-view-tabs { align-self: auto; }
  .calendar-month-day { min-height: 104px; padding: 8px; }
  .calendar-month-number { font-size: 0.8125rem; }
  .calendar-month-mobile-count { display: none; }
  .calendar-month-events { display: block; margin-top: var(--s2); }
  .calendar-month-event {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    min-width: 0;
    margin-top: 4px;
  }
  .calendar-month-event i {
    flex: 0 0 auto;
    width: 5px;
    height: 5px;
    margin-top: 6px;
    border-radius: var(--r-full);
    background: var(--ctx, var(--ctx-calendario));
  }
  .calendar-month-event b {
    display: block;
    min-width: 0;
    overflow: hidden;
    color: var(--ink-muted);
    font-size: 0.6875rem;
    font-weight: 500;
    line-height: 1.35;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .calendar-month-more { display: block; margin-top: 6px; color: var(--ctx-calendario); font-size: 0.625rem; }
}

@media (min-width: 900px) {
  .calendar-week-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
  .calendar-week-strip,
  .calendar-week-mobile-agenda { display: none; }
  .calendar-month-day { min-height: 122px; padding: 10px; }
}

@media (min-width: 1100px) {
  /* Anula la cuadrícula antigua específica de Calendario: ahora solo la vista
     Día necesita dos columnas; Semana y Mes usan todo el ancho útil. */
  #screen-calendar.active {
    display: block;
  }
  #screen-calendar.active > .screen-head,
  #screen-calendar.active > .calendar-nav,
  #screen-calendar.active > .calendar-stage { width: 100%; }
  .calendar-day-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.65fr) minmax(280px, .75fr);
    gap: var(--s8);
    align-items: start;
  }
  .calendar-day-aside { margin-top: 0; }

}

@media (min-width: 1600px) {
  .calendar-month-day { min-height: 132px; }
  .calendar-week-day { min-height: 470px; }
}

/* 2.1.2 — Calendario con muchas citas: que se lea lo que agrupa y que el Mes
   no baile. Medido en Chrome con 70 citas en el día.

   Contraste en claro con color: --ctx-calendario es el magenta PASTEL y como
   tinta de texto daba 1.48–1.65:1 justo en «+N más» (el aviso que agrupa las
   citas que no caben) y en el número de hoy. Se usa la tinta del área,
   --area-calendar-ink, la misma de la pestaña activa y de la píldora de
   navegación: 5.4–6.0:1. Los títulos de eventos del Mes mezclaban 66 % de
   pastel (2.67–3.85:1); en claro bajan al 30 % (6.4–8.2:1). Oscuro y los
   dos Sobrios ya pasaban y no cambian. El bloque @media es para quien nunca
   eligió tema y hereda el claro del sistema. */
[data-theme="light"][data-color-mode="color"] .calendar-more-count,
[data-theme="light"][data-color-mode="color"] .calendar-month-more,
[data-theme="light"][data-color-mode="color"] .calendar-week-strip-day.today strong,
[data-theme="light"][data-color-mode="color"] .calendar-week-strip-day[aria-selected="true"] i,
[data-theme="light"][data-color-mode="color"] .calendar-week-day.today .calendar-week-head strong { color: var(--area-calendar-ink); }
[data-theme="light"][data-color-mode="color"] .calendar-month-event[data-ctx]:not([data-ctx="neutro"]) b { color: color-mix(in srgb, var(--ctx) 30%, var(--ink)); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme])[data-color-mode="color"] .calendar-more-count,
  :root:not([data-theme])[data-color-mode="color"] .calendar-month-more,
  :root:not([data-theme])[data-color-mode="color"] .calendar-week-strip-day.today strong,
  :root:not([data-theme])[data-color-mode="color"] .calendar-week-strip-day[aria-selected="true"] i,
  :root:not([data-theme])[data-color-mode="color"] .calendar-week-day.today .calendar-week-head strong { color: var(--area-calendar-ink); }
  :root:not([data-theme])[data-color-mode="color"] .calendar-month-event[data-ctx]:not([data-ctx="neutro"]) b { color: color-mix(in srgb, var(--ctx) 30%, var(--ink)); }
}

/* 2.1.2 — Mes: el número del día arriba en todas las celdas. La celda es un
   <button>, que centra su contenido en vertical: sin eventos el número
   flotaba más abajo que en la celda de al lado (7 px en móvil, hasta 20 px
   en escritorio, dentro de la MISMA fila). */
.calendar-month-day { display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-start; }
.calendar-month-day > .calendar-month-events,
.calendar-month-day > .calendar-month-mobile-count,
.calendar-month-day > .calendar-month-more { align-self: stretch; }

/* 2.1.2 — A 360 el título de la semana («21–27 de septiembre de 2026») se
   cortaba en «…de 2…» (222 px de texto en 214). Con 8 px de relleno cabe. */
@media (max-width: 400px) {
  .calendar-nav-current { padding-inline: var(--s2); }
}


/* 1.0.9 · confirmación de reinicio operativo ------------------------------ */
.reset-keep-note{
  display:flex;flex-direction:column;gap:4px;
  margin:0 0 var(--s4);padding:12px 14px;
  border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface-sunken);color:var(--ink-muted);
}
.reset-keep-note strong{color:var(--ink);font-size: 0.8125rem}
.reset-keep-note span{font-size: 0.75rem;line-height:1.5}

/* ==========================================================================
   SAMAPP 1.0.12 · DIRECCIÓN VISUAL MAESTRA
   Claude Design se usa como especificación, no como markup. 90% neutro / 10%
   contexto; light/dark comparten jerarquía y componentes.
   ========================================================================== */


/* ==========================================================================
   SAMAPP 1.1.0 · NAVEGACIÓN MÓVIL SIMPLIFICADA
   La navegación agrupa destinos, no datos:
     Hoy · Pendientes/Listas · Sam · Calendario · Finanzas.
   ========================================================================== */

/* Calendario tiene un nombre más largo que el antiguo "Más". La etiqueta
   conserva una sola línea incluso a 320px sin reducir el área táctil. */
.tab {
  min-width: 0;
  font-size: clamp(var(--t-nano), 2.5vw, var(--t-micro));
}
.tab > span:last-child {
  max-width: 100%;
  white-space: nowrap;
}

/* Pendientes y Listas son dominios distintos pero comparten un punto de
   entrada móvil. En desktop estos tabs desaparecen y manda el sidebar. */
.mobile-work-tabs {
  display: none;
}
.list-screen-head {
  display: flex;
  align-items: center;
  gap: var(--s2);
}
.list-screen-head .screen-title { margin-right: auto; }
.list-add-button {
  display: inline-flex;
}
.list-add-button svg,
.task-add-button svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Hoy es un briefing. El acceso al calendario es un enlace contextual, no
   otro botón protagonista compitiendo con "Hablar con Sam". */
.today-calendar-link {
  margin-top: var(--s3);
}
.today-calendar-link-empty {
  display: flex;
  margin-inline: auto;
  width: max-content;
}

/* Menú personal: el mismo contenido es bottom sheet en móvil y popover
   compacto bajo RT en desktop. */
.user-menu-body {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}
.user-menu-identity {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 2px 2px var(--s2);
}
.user-menu-identity strong {
  color: var(--ink-strong);
  font-size: 0.9375rem;
  font-weight: 600;
}
.user-menu-identity span,
.user-menu-label {
  color: var(--ink-muted);
  font-size: 0.75rem;
}
.user-menu-actions .sheet-item {
  gap: var(--s3);
}
.user-menu-section-label { display: block; margin: 0 2px var(--s2); }
.user-menu-item svg {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.user-theme-block {
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}
.user-theme-toggle {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  margin-top: var(--s2);
  padding: 4px;
  background: var(--surface-sunken);
  border-radius: var(--r-full);
}
.user-theme-button {
  min-height: var(--tap);
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
}
.user-theme-button[aria-pressed="true"] {
  background: var(--action);
  color: var(--action-ink);
}
.user-menu-logout {
  margin-top: var(--s1);
  color: var(--ctx-atencion);
}

@media (max-width: 899px) {
  /* MOBILE SYSTEM / HARDENING 1 — targets y lectura. Esta capa sólo
     formaliza reglas móviles compartidas; no redefine layouts desktop. */
  .btn-sm, .chip { min-height: var(--tap); }
  .tab { font-size: var(--t-micro); min-height: var(--tap); }
  .settings-choice-row .chip { min-height: var(--tap); }
  #screen-today .row-title,
  #screen-tasks .row-title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    white-space: normal;
    line-height: 1.2;
  }
  #screen-finance .finance-head .screen-title { font-size: 27px; line-height: 1; }
  #screen-finance .finance-card-link,
  #screen-finance .finance-text-action { min-height: var(--tap); }
  #screen-finance .finance-card-link,
  #screen-finance .finance-text-action { display: inline-flex; align-items: center; }
  #screen-finance .row-more { width: var(--tap); height: var(--tap); min-height: var(--tap); }
  #screen-sam .composer { padding-top: var(--s1); padding-bottom: var(--s1); }
  #screen-sam .composer input { min-height: var(--tap); }
  .sam-brief-more { min-height: var(--tap); }
  .btn:active, .tab:active, .chip:active, .mobile-work-tab:active,
  .row-more:active, .calendar-view-tab:active, .calendar-nav-arrow:active,
  .month-arrow:active, .user-theme-button:active, .composer-tool:active {
    transform: scale(.97);
  }

  .mobile-work-tabs {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin: 0 0 var(--s4);
    padding: 4px;
    background: var(--surface-sunken);
    border-radius: var(--r-full);
  }
  .mobile-work-tab {
    min-height: var(--tap);
    padding: 0 var(--s4);
    border: 0;
    border-radius: var(--r-full);
    background: transparent;
    color: var(--ink-muted);
    font-size: 0.8125rem;
    font-weight: 500;
    cursor: pointer;
  }
  .mobile-work-tab[aria-selected="true"] {
    background: var(--action);
    color: var(--action-ink);
  }

  /* La quinta etiqueta cabe completa sin convertir "Calendario" en dos
     líneas ni invadir al botón central de Sam. */
  .tab {
    padding-inline: 1px;
  }
}

@media (max-width: 480px) {
  /* Debe vivir después de los estilos base de .sheet-form: una regla histórica
     anterior era sobrescrita por `font: inherit` y Safari seguía viendo 15px. */
  .sheet-form .field input,
  .sheet-form .field select,
  .sheet-form .field textarea { font-size: 1rem; }
}

@media (max-width: 899px) and (orientation: landscape) and (max-height: 480px) {
  :root { --tabbar-h: 54px; }
  .appbar { padding-top: calc(var(--safe-top) + var(--s2)); padding-bottom: var(--s1); }
  .content { padding-top: var(--s1); }
  .screen-head { margin-bottom: var(--s3); }
  .tab-sam .tab-sam-disc { width: 48px; height: 48px; top: -16px; }
  .tab-sam > span:last-child { margin-top: 26px; }
}

@media (max-width: 360px) {
  /* 2.1.1 — Los tres botones de la barra pasaron de 41 a 44 de ancho (9px en
     total) y a 320px la marca se quedaba sin sitio: salía «Sama…». El hueco
     entre piezas cede esos píxeles —medido: la caja del texto pasa de 66 a 75,
     que es justo lo que mide «Samapp»— y el margen lateral se queda en 20 para
     que el logotipo siga alineado con la columna de contenido. */
  .appbar { gap: var(--s1h); }

  .task-add-label,
  .list-add-label { display: none; }

  .task-add-button,
  .list-add-button {
    width: var(--tap);        /* 2.1.1 — era 40 */
    min-width: var(--tap);
    padding-inline: 0;
    justify-content: center;
  }

  .mobile-work-tab {
    padding-inline: var(--s3);
  }
}

@media (min-width: 900px) {
  /* El scrim del menú personal no oscurece toda la app: se comporta como un
     popover anclado visualmente a RT. Los demás sheets siguen centrados. */
  #sheetScrim[data-sheet-kind="user-menu"] {
    align-items: flex-start;
    justify-content: flex-end;
    padding: 76px var(--s6) 0 0;
    background: transparent;
    backdrop-filter: none;
  }
  #sheetScrim[data-sheet-kind="user-menu"] .sheet {
    width: 286px;
    max-width: 286px;
    max-height: calc(100dvh - 96px);
    margin: 0;
    padding: var(--s5);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-lift);
    transform: translateY(-6px) scale(.985);
    transform-origin: top right;
  }
  #sheetScrim[data-sheet-kind="user-menu"].visible .sheet {
    transform: translateY(0) scale(1);
  }
  #sheetScrim[data-sheet-kind="user-menu"] .sheet-title {
    margin-bottom: var(--s4);
  }
}


/* ============================================================================
   SAMAPP 1.1.0 · SOBRIO / COLOR
   ========================================================================== */
/* Identificador pequeño de área: suficientemente expresivo en Sobrio y más
   gráfico en Color sin convertirse en un icono de widget. */
.area-initial {
  width: 34px; height: 34px; flex: 0 0 auto;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--ctx, var(--ink-muted));
  color: var(--hero-navy);
  font-size: 0.875rem; font-weight: 700;
}
[data-ctx="incubadora"] .area-initial,
[data-ctx="oscuro"] .area-initial { color: #fff; }
.area-row .row-meta { color: var(--ctx, var(--ink-muted)); }
/* 2.1.2 — T1b/A2: en el detalle de un área, «+ Nuevo proyecto» va pegado a la
   derecha del rótulo «Proyectos». `.link-more` mide el 100 % de su fila y el
   texto flotaba a media fila (x=726–833 a 1280, 90 px de hueco por lado a 390).
   Y las flechas «→» de «Ver sus N pendientes» y «Editar, personas y actividad»
   iban pegadas al texto (0 px): el espacio se pierde porque `.link-more` es
   flex. Conserva los 44 px de alto de toque; mismo criterio que
   `.home-habits-all` en habitos.css. */
#screen-areas .section-label-row > .link-more { width: auto; flex: 0 0 auto; margin: 0; padding: 0; white-space: nowrap; }
#screen-areas .section-label-row:has(> .link-more) > .section-label { min-width: 0; overflow-wrap: anywhere; }
#screen-areas .link-more > span[aria-hidden="true"] { margin-left: 4px; }

/* La paleta firma checkboxes en ambos estilos. */
.row[data-ctx]:not([data-ctx="neutro"]) .check::before {
  border-color: color-mix(in srgb, var(--ctx) 68%, var(--line-strong));
}
.row[data-ctx]:not([data-ctx="neutro"]) .check[aria-checked="true"]::before {
  background: var(--ctx); border-color: var(--ctx);
}

/* COLOR permite superficies completas tintadas. El markup no cambia: el mismo
   data-ctx que ya explica el área decide la superficie. */
[data-color-mode="color"] .row[data-ctx]:not([data-ctx="neutro"]),
[data-color-mode="color"] .row.tinted[data-ctx]:not([data-ctx="neutro"]) {
  background: var(--ctx-tint);
  border-color: color-mix(in srgb, var(--ctx) 12%, var(--line));
}
[data-color-mode="color"] .row[data-ctx]:not([data-ctx="neutro"]):hover {
  /* 1.15.0 — contra --tone-canvas, como el resto de rellenos teñidos: mezclar
     contra la superficie arrastraba el matiz en oscuro (ver 4270). */
  background: color-mix(in srgb, var(--ctx) 38%, var(--tone-canvas));
}

/* Agenda: en Color el cuerpo se convierte en bloque contextual; el timeline
   sigue mostrando hora + conector y por eso el color nunca es la única señal. */
[data-color-mode="color"] .calendar-agenda-row[data-ctx]:not([data-ctx="neutro"]) .calendar-agenda-body {
  margin-block: 6px;
  border-bottom-color: transparent;
  border-radius: var(--r-sm);
  background: var(--ctx-tint);
}
[data-color-mode="color"] .calendar-week-event[data-ctx]:not([data-ctx="neutro"]) {
  border-left-color: var(--ctx);
  background: var(--ctx-tint);
}
[data-color-mode="color"] .calendar-month-event[data-ctx]:not([data-ctx="neutro"]) b {
  color: color-mix(in srgb, var(--ctx) 66%, var(--ink));
}

/* Briefings: la familia mantiene su semántica, pero Color da presencia real a
   las filas/secciones sin convertir la tarjeta completa en una mancha pastel. */
[data-color-mode="color"] .sam-brief-calendar .sam-brief-event,
[data-color-mode="color"] .sam-brief-tasks .sam-brief-row,
[data-color-mode="color"] .sam-brief-list .sam-brief-check,
[data-color-mode="color"] .sam-brief-day .sam-brief-day-list > button {
  border-radius: var(--r-sm);
}
[data-color-mode="color"] .sam-brief-calendar .sam-brief-event,
[data-color-mode="color"] .sam-brief-tasks .sam-brief-row,
[data-color-mode="color"] .sam-brief-list .sam-brief-check {
  padding-inline: var(--s3);
  background: var(--ctx-tint, var(--brief-tint));
  border-top-color: transparent;
}
[data-color-mode="color"] .sam-brief-calendar .sam-brief-event + .sam-brief-event,
[data-color-mode="color"] .sam-brief-tasks .sam-brief-row + .sam-brief-row,
[data-color-mode="color"] .sam-brief-list .sam-brief-check + .sam-brief-check { margin-top: 6px; }
[data-color-mode="color"] .sam-brief-day .sam-brief-day-list > button[data-ctx]:not([data-ctx="neutro"]) {
  margin-top: 6px;
  padding: 9px var(--s3);
  background: var(--ctx-tint);
}

/* Finanzas conserva el hero navy en las cuatro combinaciones. */
[data-color-mode="color"] .payable-row.late { background: var(--tint-atencion); border-color: transparent; }

.user-style-block { margin-top: var(--s3); }

/* 1.14.2 — Estilo pasó de dos opciones a tres (Sobrio · Pastel · Vivo) y
   .user-theme-toggle tiene `grid-template-columns: 1fr 1fr` fijo, así que el
   tercer botón se caía a una segunda fila: medido, el grupo pasaba de 245x44
   a 245x97 con "Vivo" solo debajo. Apariencia (Claro/Oscuro) sigue siendo de
   dos, por eso la columna extra se pide aquí y no en la regla compartida.
   A 245px de panel salen tres botones de ~79px: caben "Sobrio", "Pastel" y
   "Vivo" sin partir palabra. */
/* 1.16.4 — De vuelta a DOS columnas. Al retirar "Vivo" en 1.15.3 se quitó la
   opción pero no esta rejilla, así que el bloque Estilo seguía repartiendo el
   ancho entre tres y dejaba 124px de pista vacía a la derecha, justo debajo de
   Apariencia, que sí ocupa todo. Medido: 118.2px por botón contra los 178.8 de
   Apariencia. Se ve como un escalón. */
.user-style-block .user-theme-toggle { grid-template-columns: repeat(2, 1fr); }
.user-style-block .user-style-button { padding-left: 4px; padding-right: 4px; }

/* Cambiar Sobrio ↔ Color se siente inmediato pero no abrupto. */
.row, .calendar-agenda-body, .calendar-week-event, .calendar-month-day,
.sam-brief-event, .sam-brief-row, .sam-brief-check, .area-initial {
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

/* ==========================================================================
   SAMAPP 1.1.1 · CONFIGURACIÓN COMPACTA + RITMO DE PENDIENTES
   --------------------------------------------------------------------------
   Configuración deja de ser un catálogo abierto de punta a punta. Conserva
   todas las funciones, pero muestra primero decisiones y resúmenes; los
   catálogos financieros se expanden bajo demanda y las acciones destructivas
   viven dentro de "Acciones avanzadas".
   ========================================================================== */

/* Pendientes: navegación de sección y rango temporal son dos niveles distintos.
   El stack fija un ritmo vertical deliberado y deja aire antes de la primera
   fila, tanto en móvil como en escritorio. */
.task-filter-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  margin-bottom: var(--s5);
}
.task-filter-stack > .chips,
.task-filter-stack > .mobile-work-tabs { margin: 0; }
/* 1.30 — A 320 px "Después" se salía de la pantalla y la página entera cogía
   scroll horizontal (343 px medidos sobre 320). La fila de chips YA tenía
   `overflow-x:auto`, pero nada la limitaba al ancho disponible: dentro de un
   flex, un hijo se dimensiona por su contenido salvo que se le diga otra cosa,
   así que crecía y no había nada que desplazar. Se limita LA CADENA entera
   —la fila intermedia incluida—, que era lo que faltaba: sin ella, el arreglo
   sobre `.chips` no llegaba a aplicarse porque el desbordamiento nacía un
   nivel más arriba. */
.task-filter-stack > .chips,
.task-filter-stack > .mobile-work-tabs,
.task-filter-stack > .task-range-line {
  align-self: stretch;
  width: 100%;
  max-width: 100%;
  min-width: 0;
}
.task-range-line > .chips {
  min-width: 0;
  max-width: 100%;
  flex: 1 1 auto;
}
/* 2.1.2 — Aviso «Filtrando · Área/Proyecto»: «Ver todos» a su tamaño y
   pegado a la derecha. Heredaba de .link-more el ancho completo, así que
   flotaba a media fila y partía el título; con una palabra larga en el
   nombre del proyecto (o una URL) ensanchaba la página y sacaba el botón de
   la pantalla. El título se parte donde haga falta en vez de empujar. */
.section-label-row > .link-more[data-act="task-filter-clear"] { width: auto; flex: 0 0 auto; margin-top: 0; white-space: nowrap; }
.section-label-row:has(> .link-more[data-act="task-filter-clear"]) > .section-label { min-width: 0; overflow-wrap: anywhere; }
/* 2.1.2 — Fusión (estructura global + Pendientes + Áreas). La red global deja
   bajar el enlace de una fila de sección al renglón siguiente
   (.section-label-row{flex-wrap:wrap}); estos dos enlaces ya miden lo que su
   texto, así que al bajar quedaban a la IZQUIERDA. Con el margen automático
   siguen pegados a la derecha, bajen o no; en su renglón no cambia nada (el
   margen se come el mismo hueco que dejaba space-between). */
.section-label-row > .link-more[data-act="task-filter-clear"],
#screen-areas .section-label-row > .link-more { margin-left: auto; }
/* 2.1.2 — Los 5 filtros necesitan 410 px (381 en inglés) y la barra de
   desplazamiento está oculta: a 360 «Hechos» no asomaba nada y «Después» se
   veía completa, así que nada decía que había más. Un desvanecido en el borde
   y 28 px libres al final (para que el último filtro salga entero al
   desplazar). Hasta 419 px, donde desbordan en los dos idiomas: a 420–430 en
   inglés caben justos y el relleno los haría desplazables sin necesidad. La
   máscara sólo usa el alfa del degradado; #000 no pinta ningún color. */
@media (max-width: 419px) {
  #screen-tasks .task-range-line > .chips {
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
            mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
    padding-right: 28px;
  }
}
/* 2.1.2 — Con letra al 200 % «+ Pendiente» salía 25 px de la pantalla: el
   botón baja de renglón en vez de ensanchar la página. Sólo Pendientes (la
   cabecera de Finanzas comparte la clase .task-screen-head y no cambia). */
#screen-tasks > .task-screen-head { flex-wrap: wrap; row-gap: var(--s2); }

/* Configuración */
.settings-screen-head {
  margin-bottom: var(--s5);
}
.settings-layout {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s5);
  align-items: start;
}
.settings-primary {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}
.settings-secondary { min-width: 0; }
.settings-card {
  min-width: 0;
  overflow: hidden;
  padding: var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-low);
}
.settings-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  margin-bottom: var(--s4);
}
.settings-card-head h2 {
  margin: 0;
  color: var(--ink-strong);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.35;
}
.settings-card-head p {
  max-width: 620px;
  margin: 4px 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.5;
}
.settings-card-count {
  flex: 0 0 auto;
  min-width: 30px;
  height: 30px;
  padding: 0 10px;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}
.settings-account-summary {
  min-height: 58px;
  padding: var(--s3) var(--s4);
  display: flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
}
.settings-account-summary > div {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.settings-account-summary strong {
  overflow: hidden;
  color: var(--ink);
  font-size: 0.9375rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.settings-account-summary small {
  overflow: hidden;
  color: var(--ink-muted);
  font-size: 0.75rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.settings-choice-row {
  min-height: 58px;
  padding: var(--s3) 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  border-bottom: 1px solid var(--line);
}
.settings-account-summary + .settings-choice-row { margin-top: var(--s2); }
.settings-choice-row:last-child { padding-bottom: 0; border-bottom: 0; }
.settings-choice-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.settings-choice-copy strong {
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 500;
}
.settings-choice-copy small {
  color: var(--ink-muted);
  font-size: 0.75rem;
  line-height: 1.35;
}
.settings-choice-row .chips {
  flex: 0 0 auto;
  overflow: visible;
}
.settings-choice-row .chip {
  min-height: var(--tap);
  padding-inline: var(--s3);
  font-size: 0.8125rem;
}
/* 2.1.2 — C3: Configuración → Eliminar mi cuenta. «Eliminar permanentemente»
   no cabía en media columna de teléfono: salía de su botón 23 px por lado a
   320, 12 a 360 y 4 a 390. En teléfono ese pie va a una columna y, con letra
   grande, el texto puede partir en dos renglones dentro del botón. Sólo ese
   pie lleva `.btn-danger` dentro de `.edit-form-actions`: los demás no cambian. */
.edit-form-actions:has(> .btn-danger) .btn { white-space: normal; line-height: 1.2; text-align: center; }
@media (max-width: 420px) { .edit-form-actions:has(> .btn-danger) { grid-template-columns: 1fr; } }
/* 2.1.2 — C5: Configuración → Repeticiones. La fila usaba `.prop-field`, que es
   una rejilla etiqueta/valor de 2 columnas: «Pausar/Reanudar» quedaba aplastado
   en 44 px con el texto fuera (19 de 25 filas a 390, y 2 de 3 con títulos
   normales) y cada fila tenía botones de ancho distinto. Ahora el título va
   arriba y las dos acciones a partes iguales debajo. No se quita ninguna. */
.prop-field.recurrence-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.prop-field.recurrence-row > .recurrence-copy { flex: 1 1 100%; min-width: 0; }
.prop-field.recurrence-row > .btn { flex: 1 1 0; }
/* 2.1.2 — Con letra al 200 % en el teléfono, media fila ya no alcanza para
   «Reanudar»: el texto se metía 15 px en el relleno del botón. Por debajo de
   10rem de fila (sólo pasa con letra muy grande) cada acción ocupa su propio
   renglón; siguen las dos, del mismo ancho. */
.prop-field.recurrence-row { container-type: inline-size; }
@container (max-width: 10rem) {
  .prop-field.recurrence-row > .btn { flex-basis: 100%; }
}

.settings-disclosure {
  border-top: 1px solid var(--line);
}
.settings-card-head + .settings-disclosure { border-top: 0; }
.settings-disclosure-summary {
  position: relative;
  min-height: 58px;
  padding: var(--s3) 30px var(--s3) 0;
  display: flex;
  align-items: center;
  cursor: pointer;
  list-style: none;
  color: var(--ink);
  user-select: none;
}
.settings-disclosure-summary::-webkit-details-marker { display: none; }
.settings-disclosure-summary::after {
  content: "";
  position: absolute;
  right: 4px;
  top: 50%;
  width: 8px;
  height: 8px;
  border-right: 1.5px solid var(--ink-faint);
  border-bottom: 1.5px solid var(--ink-faint);
  transform: translateY(-65%) rotate(45deg);
  transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.settings-disclosure[open] > .settings-disclosure-summary::after {
  transform: translateY(-35%) rotate(225deg);
  border-color: var(--ink-muted);
}
.settings-disclosure-copy {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.settings-disclosure-copy strong {
  color: var(--ink);
  font-size: 0.875rem;
  font-weight: 500;
}
.settings-disclosure-copy small {
  color: var(--ink-muted);
  font-size: 0.75rem;
}
.settings-disclosure-body {
  padding: 0 0 var(--s4);
}
.settings-disclosure-body .row {
  max-width: none;
  background: var(--surface-sunken);
}
.settings-add-control { margin-top: var(--s2); }
.settings-action-list .setting-action:first-child { border-top: 1px solid var(--line); }
.settings-disclosure-danger { margin-top: var(--s2); }
.settings-disclosure-danger .settings-disclosure-summary strong { color: var(--ink-muted); }
.settings-disclosure-danger[open] .settings-disclosure-summary strong { color: var(--color-red-ink); }
.settings-disclosure-danger .settings-disclosure-body {
  padding: 0 0 var(--s2);
}
.settings-session-card .btn { margin-top: var(--s1); }
.settings-session-card .settings-build { margin-bottom: 0; }

/* La antigua cuadrícula de Configuración repartía catálogos interminables a
   la izquierda y dejaba una columna vacía enorme. 1.1.1 usa un layout propio. */
@media (min-width: 1100px) {
  #screen-settings.active { display: block; }
  #screen-settings.active > .screen-head { width: 100%; }
  .settings-layout {
    grid-template-columns: minmax(0, 1fr) 280px;
    gap: var(--s7);
  }
  .settings-secondary {
    position: sticky;
    top: var(--s6);
  }
}

@media (max-width: 699px) {
  .settings-screen-head { margin-bottom: var(--s4); }
  .settings-layout,
  .settings-primary { gap: var(--s4); }
  .settings-card {
    padding: var(--s4);
    border-radius: var(--r-md);
  }
  .settings-card-head { margin-bottom: var(--s3); }
  .settings-choice-row {
    align-items: flex-start;
    flex-direction: column;
    gap: var(--s2);
  }
  .settings-choice-row .chips { width: 100%; }
  .settings-choice-row .chip { flex: 1 1 0; }
  .settings-disclosure-summary { min-height: 56px; }
}

@media (max-width: 899px) {
  /* Se anula el margen histórico del tab móvil porque el espaciado ahora lo
     gobierna task-filter-stack; evita que Pendientes/Listas toque o empuje a
     Hoy/Mañana/Después. */
  .task-filter-stack .mobile-work-tabs { margin-bottom: 0; }
}

/* ========================================================================
   Samapp 1.2.0 · Notificaciones
   ====================================================================== */
.notification-sheet{display:grid;gap:var(--s4)}
.notification-status{display:flex;align-items:flex-start;gap:12px;padding:14px 16px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface)}
.notification-status>span{width:9px;height:9px;margin-top: 6px;border-radius:50%;background:var(--ink-faint);flex:none}
.notification-status.is-on>span{background:var(--ctx-finanzas)}
.notification-status strong,.notification-status small{display:block}.notification-status small{margin-top:2px;color:var(--ink-muted);line-height:1.35}
.notification-types{display:grid;gap:6px}
.notification-toggle{display:flex;align-items:center;justify-content:space-between;min-height:44px;padding:0 14px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);color:var(--ink);font:inherit;text-align:left}
.notification-toggle i{position:relative;width:34px;height:20px;border-radius:999px;background:var(--surface-sunken);border:1px solid var(--line-strong);transition:background-color .18s ease,border-color .18s ease}
.notification-toggle i::after{content:"";position:absolute;width:14px;height:14px;left:2px;top:2px;border-radius:50%;background:var(--ink-muted);transition:transform .18s ease,background-color .18s ease}
.notification-toggle[aria-pressed="true"] i{background:color-mix(in srgb,var(--ctx-finanzas) 24%,var(--surface));border-color:var(--ctx-finanzas)}
.notification-toggle[aria-pressed="true"] i::after{transform:translateX(14px);background:var(--ctx-finanzas)}
.notification-lead{padding-top:2px}.notification-lead>strong{display:block;margin-bottom: 10px;font-size:var(--t-meta)}.notification-lead>small{display:block;margin-top:8px;color:var(--ink-muted);font-size:var(--t-micro);line-height:1.35}
.notification-device-note{padding-top:10px;border-top:1px solid var(--line);color:var(--ink-muted);font-size:var(--t-micro)}
.notification-guidance{padding:18px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-sunken)}
.notification-guidance strong{display:block;margin-bottom: 6px}.notification-guidance p{margin:0;color:var(--ink-muted)}
.notification-permission-intro{text-align:center;padding:10px 4px}.notification-permission-intro .sam-mark{display:block;width:38px;height:38px;margin:0 auto 14px}.notification-permission-intro p{max-width:390px;margin:0 auto 8px;font-size: 0.9375rem}.notification-permission-intro small{color:var(--ink-muted)}
@media (prefers-reduced-motion:reduce){.notification-toggle i,.notification-toggle i::after{transition:none}}

/* ========================================================================== 
   SAMAPP 1.5.0 · EDICIÓN UNIVERSAL
   Formularios compactos reutilizables; no cambia la dirección visual general.
   ========================================================================== */
.edit-form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3);
}
.edit-form-actions,
.edit-detail-foot {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  gap: var(--s2);
  width: 100%;
}
.edit-readonly-note {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.5;
}
.edit-form-hint {
  display: block;
  color: var(--ink-faint);
  font-size: 0.75rem;
  line-height: 1.35;
}
.payable-more,
.list-detail-more { flex: 0 0 auto; }
.screen-head-main { min-width: 0; }
#screen-lists .screen-head:has(.list-detail-more) {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--s3);
}
#screen-lists .screen-head:has(.list-detail-more) .back-btn { grid-column: 1; }
#screen-lists .screen-head:has(.list-detail-more) .screen-head-main { grid-column: 2; }
#screen-lists .screen-head:has(.list-detail-more) .list-detail-more { grid-column: 3; align-self: center; margin: 0; }
/* 2.1.2 — L1: títulos de usuario con palabras largas. Un fragmento sin
   espacios de ~35 caracteres o más (una URL, una referencia) en el título de
   una lista llevaba la página a 932 px en un teléfono de 360: la barra inferior
   y la hoja «•••» quedaban fuera de la vista. `anywhere` sólo parte la palabra
   que no cabe; los títulos normales no cambian. #sheetTitle y #confirmTitle son
   el título de esa hoja (list-menu usa el de la lista); si el arreglo global de
   hojas ya lo aplica, esta parte queda repetida y es inocua. */
#screen-lists .screen-title, #sheetTitle, #confirmTitle { overflow-wrap: anywhere; }

@media (max-width: 520px) {
  .edit-form-grid { grid-template-columns: 1fr; }
  .edit-form-actions,
  .edit-detail-foot { grid-template-columns: 1fr 1.2fr; }
}
/* 2.1.2 — Pies de hoja con letra grande: «Cancelar compromiso», «Agregar
   pendiente» y «Guardar cambios» no se partían (.btn es nowrap) y salían de
   su botón (medido: 4 px a 320 con letra normal, 16 px al 130 % y hasta 81 px
   al 200 %, con la hoja desplazándose a los lados). Es el mismo arreglo que
   .confirm-actions: el texto se parte dentro del botón. Si ni así cabe la
   palabra más larga (letra al 150-200 % en el teléfono: el pie mide menos de
   14rem), los botones se apilan a todo lo ancho; `anywhere` queda de último
   recurso. `rem` sigue a la escala de letra. Con letra normal a 360 px o más
   no cambia nada. Vale para los tres pies de dos columnas: el de detalle,
   el de alta y el de edición (.manual-task-actions / .edit-form-actions). */
.edit-detail-foot, .manual-task-actions, .edit-form-actions { container-type: inline-size; }
.edit-detail-foot .btn, .manual-task-actions .btn, .edit-form-actions .btn { white-space: normal; text-align: center; line-height: 1.2; overflow-wrap: anywhere; }
@container (max-width: 14rem) {
  .edit-detail-foot > *, .manual-task-actions > *, .edit-form-actions > * { grid-column: 1 / -1; }
}

/* ========================================================================
   SAMAPP 1.5.1 · FINANZAS COMPACTAS · REFINAMIENTO
   Conserva la arquitectura aprobada y usa UN solo sistema de temas:
     data-theme="light|dark"
     data-color-mode="sober|color"
   Sobrio es el fallback por defecto para que la ausencia del atributo no
   convierta Finanzas accidentalmente en modo Color.
   ====================================================================== */
:root {
  --finance-positive: var(--ctx-income);
  --finance-negative: var(--ctx-expense);
  --finance-transfer: var(--ink-muted);

  /* --------------------------------------------- 1.15.0 · PAREJA "INK" ---
     POR QUÉ HACEN FALTA. Los tokens de arriba son TONOS de relleno: sirven
     para el punto de 6px, el chip de tendencia y los gajos de la dona, que es
     donde la política de finanzas.css quiere el color. Pero el CSS los estaba
     usando también como color de TEXTO —el importe de un ingreso, el enlace
     "Ver todas"— y ahí un tono de relleno no llega ni de lejos: medido sobre
     blanco, el verde daba 1.44 y el teal 1.41 con esta paleta, y 2.1 / 1.9 con
     la anterior. Era un fallo de AA anterior a este encargo.

     La pareja -ink es el mismo matiz resuelto para texto, y como el tier -ink
     se invierte solo en oscuro (ver tokens.css), una sola línea sirve para los
     dos temas. Medido: verde 5.99 / 6.30 sobre blanco (Pastel / Vivo) y
     11.61 / 11.58 sobre la superficie oscura.

     NO cambia la política: el color sigue marcando la excepción y sigue
     habiendo tres elementos teñidos sobre el pliegue. Sólo deja de ser
     ilegible el que ya estaba teñido. */
  --finance-positive-ink: var(--ink);
  --finance-negative-ink: var(--ink);
  --finance-accent-ink: var(--ink-muted);
  --finance-card-bg: var(--surface);
  --finance-card-border: var(--line);
  --finance-grid: var(--line);
  --finance-muted: var(--ink-muted);

  /* Fallback NEUTRO / SOBRIO. */
  --finance-chart-1: color-mix(in srgb, var(--ctx-finanzas) 68%, var(--ink-muted));
  --finance-chart-2: color-mix(in srgb, var(--ink-muted) 78%, var(--surface));
  --finance-chart-3: color-mix(in srgb, var(--ink) 38%, var(--surface));
  --finance-chart-4: color-mix(in srgb, var(--ctx-finanzas) 45%, var(--surface));
  --finance-chart-5: color-mix(in srgb, var(--ink-faint) 60%, var(--surface));
}
[data-color-mode="sober"] {
  --finance-chart-1: color-mix(in srgb, var(--ctx-finanzas) 68%, var(--ink-muted));
  --finance-chart-2: color-mix(in srgb, var(--ink-muted) 78%, var(--surface));
  --finance-chart-3: color-mix(in srgb, var(--ink) 38%, var(--surface));
  --finance-chart-4: color-mix(in srgb, var(--ctx-finanzas) 45%, var(--surface));
  --finance-chart-5: color-mix(in srgb, var(--ink-faint) 60%, var(--surface));
}
[data-color-mode="color"] {
  --finance-chart-1: var(--ctx-income);
  --finance-chart-2: var(--ctx-calendario);
  --finance-chart-3: var(--ctx-sillasmx);
  --finance-chart-4: var(--ctx-personal);
  --finance-chart-5: var(--ctx-lists);
}
[data-theme="dark"] {
  --finance-card-border: color-mix(in srgb, var(--line-strong) 82%, transparent);
  --finance-grid: color-mix(in srgb, var(--line-strong) 72%, transparent);
}

.finance-head { margin-bottom:var(--s3); }
.finance-head .screen-title { font-size:clamp(28px,2.1vw,36px); line-height:1; letter-spacing:-.025em; }
.finance-month-nav { margin-bottom:var(--s3); }
.finance-month-nav .month-arrow { transition:background var(--dur) var(--ease),color var(--dur) var(--ease),border-color var(--dur) var(--ease); }
.finance-month-nav .month-arrow:hover { background:var(--surface-hover); color:var(--ink); }
.finance-panel {
  background:var(--finance-card-bg); border:1px solid var(--finance-card-border); border-radius:var(--r-md); box-shadow:var(--shadow-card);
}
.finance-eyebrow { display:block; color:var(--finance-muted); font-size: 0.625rem; font-weight:600; letter-spacing:.012em; }

/* ----------------------------------------------------------------------------
   1.14 · AQUÍ VIVÍAN LOS GRÁFICOS DEL TABLERO ANTIGUO. YA NO EXISTEN.
   ----------------------------------------------------------------------------
   Se retiraron con el rediseño y ningún archivo de public_html los genera —
   comprobado clase por clase contra todos los .js, .html y .php del proyecto:
     .finance-kpi-value/-sub/-income/-expense/-available/-net/-empty
     .finance-spark (línea de tendencia)   .finance-bars/.finance-bar/-wrap
     .finance-chart-empty/-line/-bars      .finance-mini-stats
     .finance-net-breakdown/-status        .finance-donut-wrap/-wide

   ATENCIÓN, EL MOTIVO DE FONDO: entre esas reglas iba escondido esto —

       .finance-kpi-value[data-kind="income"], [data-kind="income"] { … }

   La segunda mitad de cada selector no llevaba clase. Era un `[data-kind]`
   SUELTO, o sea "cualquier elemento del documento que tenga data-kind", y
   pintaba de verde o de coral cosas que no tienen nada que ver con el dinero:
   las entradas del menú "Saldar con…" y los resultados del buscador global
   (`.sheet-item[data-kind]`, app.js:1611/1619/2518) salían teñidas porque
   `.sheet-item` se declara antes y pierde el desempate de especificidad.
   Medido en el navegador antes de borrar: verde rgb(117,214,154) y coral
   rgb(255,146,127) donde tocaba tinta normal rgb(16,27,42).
   Contradecía de lleno la política de color de finanzas.css: el color marca
   la excepción, no el dato.
   -------------------------------------------------------------------------- */

.finance-donut { position:relative; flex:0 0 70px; width:70px; height:70px; border-radius:50%; }
.finance-donut>span { position:absolute; inset:15px; border-radius:50%; background:var(--finance-card-bg); box-shadow:0 0 0 1px color-mix(in srgb,var(--finance-card-border) 80%,transparent); }
.finance-donut.is-empty { background:var(--surface-sunken); border:1px solid var(--finance-card-border); }
.finance-donut[data-kind="income"] {
  --finance-chart-1:var(--finance-positive);
  --finance-chart-2:color-mix(in srgb,var(--finance-positive) 65%,var(--ctx-calendario));
  --finance-chart-3:color-mix(in srgb,var(--finance-positive) 48%,var(--ink-muted));
  --finance-chart-4:color-mix(in srgb,var(--finance-positive) 34%,var(--surface));
  --finance-chart-5:color-mix(in srgb,var(--finance-positive) 24%,var(--ink));
}
.finance-donut[data-kind="expense"] {
  --finance-chart-1:var(--finance-negative);
  --finance-chart-2:color-mix(in srgb,var(--finance-negative) 72%,var(--ctx-sillasmx));
  --finance-chart-3:color-mix(in srgb,var(--finance-negative) 52%,var(--ink-muted));
  --finance-chart-4:color-mix(in srgb,var(--finance-negative) 38%,var(--surface));
  --finance-chart-5:color-mix(in srgb,var(--finance-negative) 26%,var(--ink));
}
[data-color-mode="color"] .finance-donut[data-kind="income"] {
  --finance-chart-1:var(--ctx-income); --finance-chart-2:var(--ctx-kam); --finance-chart-3:var(--ctx-calendario); --finance-chart-4:var(--ctx-finanzas); --finance-chart-5:var(--ctx-menta);
}
[data-color-mode="color"] .finance-donut[data-kind="expense"] {
  --finance-chart-1:var(--ctx-expense); --finance-chart-2:var(--ctx-sillasmx); --finance-chart-3:var(--ctx-rojo); --finance-chart-4:var(--ctx-arena); --finance-chart-5:var(--ctx-hoc);
}
/* 1.14 — Aquí vivía `.finance-legend` (9 reglas). Su único generador era la
   función legend() de screens.js, que se quedó sin llamadas al retirarse
   financeBreakdown en 1.14.0. La leyenda VIVA del tablero es otra:
   `.finance-compact-legend`, que pinta financeCompactBreakdown. No confundir:
   son clases distintas, y los tokens --finance-chart-1..5 siguen en uso por
   donut(), que sí está vivo. */
.finance-panel { min-width:0; padding:10px; margin-bottom:var(--s3); }
.finance-panel-head { min-height:30px; display:flex; align-items:flex-start; justify-content:space-between; gap:var(--s3); margin-bottom:6px; }
.finance-panel-head h2 { margin:1px 0 0; color:var(--ink); font:600 12.5px/1.25 var(--font-text); }
.finance-text-action { display:inline-flex; align-items:center; justify-content:center; min-height:var(--tap); border:0; background:transparent; color:var(--finance-accent-ink); font:600 11.5px/1 var(--font-text); cursor:pointer; padding:0 6px; transition:opacity var(--dur) var(--ease),color var(--dur) var(--ease); }
.finance-text-action:hover { opacity:.78; }
[data-color-mode="sober"] .finance-text-action,
:root:not([data-color-mode]) .finance-text-action { color:var(--ink-muted); }
.finance-account-list,.finance-tx-list,.finance-commitment-list { border:1px solid var(--finance-grid); border-radius:var(--r-sm); overflow:hidden; }
.finance-account-row,.finance-tx-row,.finance-commitment-row { display:flex; align-items:center; min-height:46px; gap: 6px; padding: 6px 8px; background:var(--surface); transition:background var(--dur) var(--ease),border-color var(--dur) var(--ease); }
.finance-account-row+.finance-account-row,.finance-tx-row+.finance-tx-row,.finance-commitment-row+.finance-commitment-row{border-top:1px solid var(--finance-grid)}
.finance-account-row:hover,.finance-tx-row:hover,.finance-commitment-row:hover{background:var(--surface-hover)}
.finance-account-icon,.finance-tx-icon { display:grid; place-items:center; flex:0 0 28px; width:28px; height:28px; border-radius:50%; background:var(--surface-sunken); color:var(--ctx-finanzas); font-size: 0.6875rem; font-weight:700; }
.finance-tx-icon svg { width:15px; height:15px; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.finance-account-icon[data-type="credit"],.finance-account-icon[data-type="loan"]{color:var(--finance-negative);background:var(--tint-expense)}
.finance-tx-icon[data-kind="income"]{background:var(--tint-income);color:var(--finance-positive)}
.finance-tx-icon[data-kind="expense"]{background:var(--tint-expense);color:var(--finance-negative)}
.finance-tx-icon[data-kind="transfer"]{background:var(--surface-sunken);color:var(--finance-transfer)}
.finance-tx-icon[data-kind="debt-payment"]{background:var(--tint-expense);color:var(--finance-negative)}
[data-color-mode="color"] .finance-tx-icon[data-kind="transfer"]{background:var(--tint-calendario);color:var(--ctx-calendario)}
.finance-account-body,.finance-tx-body { flex:1; min-width:0; }
.finance-account-name,.finance-tx-title { overflow:hidden; color:var(--ink); font-size: 0.6875rem; font-weight:600; white-space:nowrap; text-overflow:ellipsis; }
.finance-account-type,.finance-tx-meta { margin-top:1px; overflow:hidden; color:var(--finance-muted); font-size: 0.625rem; white-space:nowrap; text-overflow:ellipsis; }
.finance-account-balance,.finance-tx-amount { flex:0 0 auto; color:var(--ink); font-size: 0.6875rem; font-weight:600; font-variant-numeric:tabular-nums; white-space:nowrap; }
/* 1.15.0 — tinta, no tono de relleno: ver la nota de --finance-*-ink. */
.finance-account-balance[data-kind="debt"],.finance-account-balance[data-kind="negative"]{color:var(--finance-negative-ink)}
.finance-account-balance[data-kind="positive"],.finance-tx-amount[data-kind="income"]{color:var(--finance-positive-ink)}
.finance-tx-amount[data-kind="expense"]{color:var(--finance-negative-ink)}
.finance-tx-amount[data-kind="transfer"]{color:var(--ink-muted)}
.finance-tx-amount[data-kind="debt-payment"]{color:var(--finance-negative-ink)}
.finance-account-row .row-more,.finance-tx-row .row-more,.finance-commitment-row .row-more{flex:0 0 var(--tap);width:var(--tap);height:var(--tap);min-height:var(--tap)}
.finance-empty-inline { padding:16px; color:var(--finance-muted); font-size: 0.6875rem; text-align:center; border:1px dashed var(--line-strong); border-radius:var(--r-sm); }
.finance-commitments { margin-top:0; }
.finance-commitment-row>div:first-child{flex:1;min-width:0}
.finance-commitment-row>div:first-child strong,.finance-commitment-row>div:first-child span{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
.finance-commitment-row>div:first-child strong{font-size: 0.6875rem;color:var(--ink)}
.finance-commitment-row>div:first-child span{margin-top:1px;font-size: 0.625rem;color:var(--finance-muted)}
.finance-commitment-row>b{font-size: 0.6875rem;font-variant-numeric:tabular-nums}
@media (max-width:760px){
  .finance-panel{padding:8px}
  .finance-account-row,.finance-tx-row,.finance-commitment-row{min-height:44px;padding: 6px 6px}
  .finance-account-icon,.finance-tx-icon{flex-basis:27px;width:27px;height:27px}
  .finance-commitment-row .btn{padding-inline: 10px}
}
@media (max-width:430px){
  .finance-head .screen-title{font-size:28px}
  .finance-donut{flex-basis:62px;width:62px;height:62px}
  .finance-donut>span{inset:14px}
  .finance-account-balance,.finance-tx-amount{font-size: 0.625rem}
}

/* La composición financiera 1.5.1 ya no usa el grid legacy main/aside. */
#screen-finance.active { display:block; }
#screen-finance.active > .finance-head,
#screen-finance.active > .finance-month-nav,
#screen-finance.active > .finance-panel { width:100%; }

/* ========================================================================
   SAMAPP 1.5.1 · SOBRIO REAL · GARANTÍAS DE COMPONENTE
   La neutralidad viene de tokens.css. Estas reglas eliminan los pocos casos
   donde la semántica visual de un componente asignaba color directamente.
   ====================================================================== */
[data-color-mode="sober"] .finance-account-balance[data-kind="debt"],
[data-color-mode="sober"] .finance-account-balance[data-kind="negative"],
[data-color-mode="sober"] .finance-account-balance[data-kind="positive"],
[data-color-mode="sober"] .finance-tx-amount[data-kind="income"],
[data-color-mode="sober"] .finance-tx-amount[data-kind="expense"],
[data-color-mode="sober"] .finance-tx-amount[data-kind="debt-payment"] {
  color: var(--ink);
}

[data-color-mode="sober"] .finance-account-icon,
[data-color-mode="sober"] .finance-account-icon[data-type="credit"],
[data-color-mode="sober"] .finance-account-icon[data-type="loan"],
[data-color-mode="sober"] .finance-tx-icon,
[data-color-mode="sober"] .finance-tx-icon[data-kind="income"],
[data-color-mode="sober"] .finance-tx-icon[data-kind="expense"],
[data-color-mode="sober"] .finance-tx-icon[data-kind="transfer"],
[data-color-mode="sober"] .finance-tx-icon[data-kind="debt-payment"] {
  color: var(--ink-muted);
  background: var(--surface-sunken);
}

/* 1.14 — Los sobrios de `.finance-spark` (línea de tendencia) y `.finance-bar`
   (barras del mes) se van con sus componentes: ninguna pantalla los dibuja. */

[data-color-mode="sober"] .finance-donut[data-kind="income"],
[data-color-mode="sober"] .finance-donut[data-kind="expense"] {
  --finance-chart-1: var(--ink);
  --finance-chart-2: color-mix(in srgb, var(--ink) 70%, var(--surface));
  --finance-chart-3: var(--ink-muted);
  --finance-chart-4: color-mix(in srgb, var(--ink-muted) 60%, var(--surface));
  --finance-chart-5: var(--ink-faint);
}

/* Áreas: en Sobrio no hay "tarjeta de color", ni icono/meta coloreados. */
[data-color-mode="sober"] .area-row,
[data-color-mode="sober"] .row.tinted[data-ctx] {
  background: var(--surface);
  border-color: var(--line);
}
[data-color-mode="sober"] .area-initial {
  background: var(--surface-sunken);
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--line);
}
[data-color-mode="sober"] [data-ctx="incubadora"] .area-initial,
[data-color-mode="sober"] [data-ctx="oscuro"] .area-initial {
  color: var(--ink);
}
[data-color-mode="sober"] .area-row .row-meta {
  color: var(--ink-muted);
}

/* Calendario: selección y marcadores contextuales mantienen forma, no color. */
[data-color-mode="sober"] .calendar-week-day.selected,
[data-color-mode="sober"] .calendar-week-strip-day[aria-selected="true"],
[data-color-mode="sober"] .calendar-month-day.selected {
  background: var(--surface-sunken);
}
[data-color-mode="sober"] .calendar-week-strip-day.today strong,
[data-color-mode="sober"] .calendar-week-strip-day[aria-selected="true"] i,
[data-color-mode="sober"] .calendar-more-count,
[data-color-mode="sober"] .calendar-month-more {
  color: var(--ink-muted);
}

/* Sobrio no introduce color al pasar el mouse o al seleccionar navegación. */
[data-color-mode="sober"] .nav-link.active,
[data-color-mode="sober"] .nav-link:hover,
[data-color-mode="sober"] .tab-button[aria-selected="true"],
[data-color-mode="sober"] .segment-button[aria-selected="true"] {
  color: var(--ink);
}

/* 2.1.2 — En Claro + Sobrio las cuatro fichas de Hoy y la burbuja de Sam eran
   #F5F5F5 sobre #FAFAFA (1.045:1), sin borde ni sombra: casi no se veían. Se
   les pone el mismo borde fino que ya llevan en Color y el resto de tarjetas
   de Sobrio. Sin color nuevo: es --line del propio tema.
   2.1.2 — Fusión: las fichas de Hoy ya llevan su canto en el bloque de Hoy
   («Canto de 1 px en las fichas, SÓLO en claro»), con --line-strong en Sobrio
   (1.42:1 medido). Esta regla pesa más (0,3,0) y lo bajaba a --line (1.19:1),
   así que aquí sólo queda la burbuja de Sam. */
[data-theme="light"][data-color-mode="sober"] .msg-sam { border: 1px solid var(--line); }

/* ============================================================================
   SAMAPP 1.5.2 · FINANZAS MOBILE REFINADA
   --------------------------------------------------------------------------
   Mantiene la estructura aprobada y sustituye la paleta verde/coral por una
   dirección más premium: turquesa sobrio + índigo/lila controlado.
   ========================================================================== */
#screen-finance {
  --finance-positive-soft: color-mix(in srgb, var(--finance-positive) 14%, transparent);
  --finance-negative-soft: color-mix(in srgb, var(--finance-negative) 14%, transparent);
  --finance-accent: var(--ctx-finanzas);
  --finance-accent-soft: color-mix(in srgb, var(--finance-accent) 12%, transparent);
  /* 1.15.0 — El 8% era un navy suelto (#0b1220) de antes de los tokens y en
     oscuro metía un tinte azul sobre superficies que ahora son grises neutras.
     Se sustituye por --paper, que es el mismo gesto (un punto más hundida que
     la superficie) resuelto con el neutro de cada paleta. */
  --finance-card-bg: color-mix(in srgb, var(--surface) 92%, var(--paper) 8%);
  --finance-card-border: color-mix(in srgb, var(--line-strong) 86%, transparent);
  --finance-grid: color-mix(in srgb, var(--line-strong) 60%, transparent);
  /* 1.15.0 — Mezclaba --ink-muted con un azul-gris CLARO fijo, así que en modo
     claro ACLARABA el gris secundario y los rótulos (.finance-eyebrow, 10px)
     caían a 4.02, por debajo de AA en las seis combinaciones. El #d7e0ea era
     además un hex suelto con matiz propio, de la época anterior a los tokens.
     Ahora sigue a --ink-muted sin más: 5.87 en claro, 6.67 en oscuro. */
  --finance-muted: var(--ink-muted);
}
/* 1.9 — El dinero habla el idioma de siempre: lo que entra es verde, lo que
   sale es coral cálido. La paleta anterior (positivo cian / negativo MORADO)
   era una cuarta paleta paralela que además contradecía la intuición. Todo
   sale ahora de los tokens 1.7, así dark/light se resuelven solos. */
[data-color-mode="color"] #screen-finance {
  --finance-positive: var(--color-green);
  --finance-negative: var(--color-coral);
  --finance-positive-ink: var(--color-green-ink);
  --finance-negative-ink: var(--color-coral-ink);
  --finance-accent-ink: var(--color-teal-ink);
  --finance-positive-soft: color-mix(in srgb, var(--finance-positive) 14%, transparent);
  --finance-negative-soft: color-mix(in srgb, var(--finance-negative) 14%, transparent);
  --finance-accent: var(--color-teal);
  --finance-accent-soft: color-mix(in srgb, var(--color-teal) 12%, transparent);
}
[data-theme="light"] #screen-finance {
  --finance-card-bg: var(--surface);
  --finance-card-border: color-mix(in srgb, var(--line-strong) 80%, transparent);
  --finance-grid: color-mix(in srgb, var(--line-strong) 55%, transparent);
  /* 1.15.0 — Iban atados a --color-green/--color-coral sin guardia de modo,
     así que en SOBRIO el fondo de estos elementos cambiaba al cambiar de
     paleta: 5 de 538 elementos medidos. Ahora siguen a --finance-positive /
     --finance-negative, que en Sobrio ya resuelven a tinta neutra. */
  --finance-positive-soft: color-mix(in srgb, var(--finance-positive) 10%, transparent);
  --finance-negative-soft: color-mix(in srgb, var(--finance-negative) 10%, transparent);
  --finance-accent-soft: color-mix(in srgb, var(--finance-accent) 8%, transparent);
}

#screen-finance.active {
  display: block;
}
#screen-finance.active > .finance-head,
#screen-finance.active > .finance-month-nav,
#screen-finance.active > .finance-card {
  width: 100%;
}
#screen-finance .finance-head { margin-bottom: var(--s3); }
#screen-finance .finance-head .screen-title {
  font-size: clamp(31px, 6vw, 42px);
  line-height: 1;
  letter-spacing: -.03em;
}
#screen-finance .finance-month-nav {
  margin-bottom: var(--s4);
  justify-content: center;
}
#screen-finance .finance-month-nav .month-label {
  min-width: 200px;
  text-align: center;
}

#screen-finance .finance-card,
#screen-finance .finance-panel {
  background: var(--finance-card-bg);
  border: 1px solid var(--finance-card-border);
  border-radius: calc(var(--r-md) + 2px);
  box-shadow: var(--shadow-card);
}
#screen-finance .finance-card + .finance-card { margin-top: var(--s3); }
#screen-finance .finance-balance-card {
  padding: 16px 16px 14px;
  margin-bottom: var(--s3);
}
#screen-finance .finance-card-head,
#screen-finance .finance-panel-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
#screen-finance .finance-card-head h2,
#screen-finance .finance-panel-head h2 {
  margin: 0;
  color: var(--ink);
  font: 600 14px/1.2 var(--font-text);
}
#screen-finance .finance-eyebrow {
  display: block;
  margin-bottom: 6px;
  color: var(--finance-muted);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: .01em;
}
#screen-finance .finance-account-balance[data-kind="positive"],
#screen-finance .finance-tx-amount[data-kind="income"] {
  color: var(--finance-positive);
}
#screen-finance .finance-account-balance[data-kind="negative"],
#screen-finance .finance-tx-amount[data-kind="expense"],
#screen-finance .finance-tx-amount[data-kind="debt-payment"] {
  color: var(--finance-negative);
}

#screen-finance .finance-donut {
  flex: 0 0 86px;
  width: 86px;
  height: 86px;
}
#screen-finance .finance-donut > span {
  inset: 18px;
  background: var(--finance-card-bg);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--finance-card-border) 84%, transparent);
}
#screen-finance .finance-donut[data-kind="income"] {
  --finance-chart-1: var(--finance-positive);
  --finance-chart-2: color-mix(in srgb, var(--finance-positive) 64%, white);
  --finance-chart-3: color-mix(in srgb, var(--finance-positive) 42%, var(--surface));
  --finance-chart-4: color-mix(in srgb, var(--finance-positive) 24%, var(--surface));
  --finance-chart-5: color-mix(in srgb, var(--finance-positive) 16%, var(--surface));
}
#screen-finance .finance-donut[data-kind="expense"] {
  --finance-chart-1: var(--finance-negative);
  --finance-chart-2: color-mix(in srgb, var(--finance-negative) 64%, white);
  --finance-chart-3: color-mix(in srgb, var(--finance-negative) 42%, var(--surface));
  --finance-chart-4: color-mix(in srgb, var(--finance-negative) 24%, var(--surface));
  --finance-chart-5: color-mix(in srgb, var(--finance-negative) 16%, var(--surface));
}
/* 1.14 — Retiradas las 5 reglas de `#screen-finance .finance-legend`: sin
   generador desde que murió legend(). Ver la nota junto a .finance-panel. */

#screen-finance .finance-text-action,
#screen-finance .finance-card-link {
  color: var(--finance-accent-ink);
  font-size: 0.75rem;
  font-weight: 600;
}
#screen-finance .finance-card-link { white-space: nowrap; }
/* 2.1.2 — Con letra al 200 % a 320 px «Ver todos (70)» no cabía junto a
   «Próximos pagos y cobros» y salía 20 px de su panel. Si no cabe al lado del
   título, baja al renglón siguiente, a la derecha, como .section-label-row.
   Con letra normal no cambia nada. */
#screen-finance .finance-panel-head { flex-wrap: wrap; }
#screen-finance .finance-panel-head > .finance-card-link { margin-left: auto; }
#screen-finance .finance-account-list,
#screen-finance .finance-tx-list {
  border: 1px solid var(--finance-grid);
  border-radius: calc(var(--r-sm) + 2px);
  overflow: hidden;
}
#screen-finance .finance-account-row,
#screen-finance .finance-tx-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 60px;
  padding: 10px 12px;
  background: color-mix(in srgb, var(--surface) 66%, transparent);
}
#screen-finance .finance-account-row + .finance-account-row,
#screen-finance .finance-tx-row + .finance-tx-row {
  border-top: 1px solid var(--finance-grid);
}
#screen-finance .finance-account-icon,
#screen-finance .finance-tx-icon {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  border-radius: 14px;
  background: color-mix(in srgb, var(--surface-sunken) 72%, transparent);
  color: var(--finance-accent);
  border: 1px solid color-mix(in srgb, var(--finance-grid) 84%, transparent);
  font-size: 1rem;
  font-weight: 700;
}
#screen-finance .finance-account-icon[data-type="credit"],
#screen-finance .finance-account-icon[data-type="loan"] {
  color: var(--finance-negative);
  background: var(--finance-negative-soft);
}
#screen-finance .finance-tx-icon[data-kind="income"] {
  color: var(--finance-positive);
  background: var(--finance-positive-soft);
}
#screen-finance .finance-tx-icon[data-kind="expense"],
#screen-finance .finance-tx-icon[data-kind="debt-payment"] {
  color: var(--finance-negative);
  background: var(--finance-negative-soft);
}
#screen-finance .finance-tx-icon[data-kind="transfer"] {
  color: var(--finance-accent);
  background: var(--finance-accent-soft);
}
#screen-finance .finance-account-name,
#screen-finance .finance-tx-title {
  color: var(--ink);
  font-size: 1rem;
  font-weight: 600;
}
#screen-finance .finance-account-type,
#screen-finance .finance-tx-meta {
  margin-top: 2px;
  color: var(--finance-muted);
  font-size: 0.8125rem;
}
#screen-finance .finance-account-balance,
#screen-finance .finance-tx-amount {
  font-size: 0.9375rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
#screen-finance .finance-empty-inline {
  padding: 18px;
  color: var(--finance-muted);
  font-size: 0.8125rem;
  text-align: center;
  border: 1px dashed var(--finance-grid);
  border-radius: calc(var(--r-sm) + 2px);
}

@media (max-width: 640px) {
  #screen-finance .finance-month-nav { margin-bottom: 12px; }
  #screen-finance .finance-month-nav .month-label { min-width: 176px; }
  #screen-finance .finance-balance-card { padding: 14px; }
  #screen-finance .finance-account-row,
  #screen-finance .finance-tx-row { min-height: 56px; padding: 10px 10px; }
  #screen-finance .finance-account-name,
  #screen-finance .finance-tx-title { font-size: 0.9375rem; }
  /* 1.14 — Salieron de esta lista `.finance-legend span/strong` (clase muerta);
     los dos selectores que quedan sí se generan. */
  #screen-finance .finance-account-type,
  #screen-finance .finance-tx-meta { font-size: 0.75rem; }
}
@media (max-width: 420px) {
  #screen-finance .finance-donut {
    flex-basis: 72px;
    width: 72px;
    height: 72px;
  }
  #screen-finance .finance-donut > span { inset: 16px; }
}

/* ============================================================================
   SAMAPP · FINANZAS DENSIDAD 1.5.2
   Compacta Ingresos/Gastos y coloca Cuentas + Movimientos en 2 columnas.
   No cambia la lógica financiera ni la paleta definida por el tema.
   ========================================================================== */

#screen-finance .finance-compact-breakdown {
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 72px;
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px solid var(--finance-grid);
}
#screen-finance .finance-compact-breakdown .finance-donut {
  flex: 0 0 62px;
  width: 62px;
  height: 62px;
}
#screen-finance .finance-compact-breakdown .finance-donut > span {
  inset: 14px;
}
#screen-finance .finance-compact-legend {
  display: grid;
  gap: 6px;
  flex: 1;
  min-width: 0;
}
#screen-finance .finance-compact-legend-row {
  display: grid;
  grid-template-columns: 8px minmax(0,1fr) auto;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
#screen-finance .finance-compact-legend-row i {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--finance-chart-1);
}
#screen-finance .finance-compact-legend-row i[data-i="1"] { background: var(--finance-chart-2); }
#screen-finance .finance-compact-legend-row i[data-i="2"] { background: var(--finance-chart-3); }
#screen-finance .finance-compact-legend-row span {
  overflow: hidden;
  color: var(--finance-muted);
  font-size: 0.6875rem;
  line-height: 1.2;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#screen-finance .finance-compact-legend-row strong {
  color: var(--ink);
  font-size: 0.6875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
#screen-finance .finance-compact-empty {
  display: flex;
  align-items: center;
  min-height: 72px;
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px solid var(--finance-grid);
  color: var(--finance-muted);
  font-size: 0.6875rem;
}

/* 1.14 — `.finance-info-grid` era el contenedor de dos columnas que envolvía
   cuentas y movimientos antes del tablero de doce columnas. Ningún archivo lo
   genera desde entonces; sus 16 reglas (y su media query de 900px) se van. */

/* Sobrio sigue siendo realmente neutral en los nuevos componentes. */
[data-color-mode="sober"] #screen-finance .finance-account-balance[data-kind="positive"],
[data-color-mode="sober"] #screen-finance .finance-account-balance[data-kind="negative"],
[data-color-mode="sober"] #screen-finance .finance-tx-amount[data-kind="income"],
[data-color-mode="sober"] #screen-finance .finance-tx-amount[data-kind="expense"],
[data-color-mode="sober"] #screen-finance .finance-tx-amount[data-kind="debt-payment"] {
  color: var(--ink);
}
[data-color-mode="sober"] #screen-finance .finance-donut[data-kind="income"],
[data-color-mode="sober"] #screen-finance .finance-donut[data-kind="expense"] {
  --finance-chart-1: var(--ink);
  --finance-chart-2: color-mix(in srgb, var(--ink) 72%, var(--surface));
  --finance-chart-3: var(--ink-muted);
  --finance-chart-4: color-mix(in srgb, var(--ink-muted) 58%, var(--surface));
  --finance-chart-5: var(--ink-faint);
}
[data-color-mode="sober"] #screen-finance .finance-compact-legend-row i {
  background: var(--ink);
}
[data-color-mode="sober"] #screen-finance .finance-compact-legend-row i[data-i="1"] { background: var(--ink-muted); }
[data-color-mode="sober"] #screen-finance .finance-compact-legend-row i[data-i="2"] { background: var(--ink-faint); }

@media (min-width: 1024px) {
  #screen-finance .finance-balance-card {
    padding-top: 14px;
    padding-bottom: 12px;
  }
  #screen-finance .finance-balance-card {
    margin-bottom: 12px;
  }
}

@media (max-width: 640px) {
  #screen-finance .finance-compact-breakdown {
    gap: 10px;
    min-height: 64px;
  }
  #screen-finance .finance-compact-breakdown .finance-donut {
    flex-basis: 54px;
    width: 54px;
    height: 54px;
  }
  #screen-finance .finance-compact-breakdown .finance-donut > span {
    inset: 12px;
  }
  #screen-finance .finance-compact-legend-row span,
  #screen-finance .finance-compact-legend-row strong {
    font-size: 0.625rem;
  }
}

@media (max-width: 430px) {
  #screen-finance .finance-compact-breakdown {
    display: grid;
    grid-template-columns: 46px minmax(0,1fr);
    gap: 6px;
    min-height: 60px;
    padding-top: 6px;
  }
  #screen-finance .finance-compact-breakdown .finance-donut {
    flex-basis: 46px;
    width: 46px;
    height: 46px;
  }
  #screen-finance .finance-compact-breakdown .finance-donut > span {
    inset: 10px;
  }
  #screen-finance .finance-compact-legend-row {
    grid-template-columns: 6px minmax(0,1fr) auto;
    gap: 6px;
  }
  #screen-finance .finance-compact-legend-row i {
    width: 6px;
    height: 6px;
  }
  #screen-finance .finance-compact-legend-row span,
  #screen-finance .finance-compact-legend-row strong {
    font-size: 0.625rem;
  }
}


/* ============================================================================
   SAMAPP 1.5.2 · SISTEMA DESKTOP CANÓNICO
   ----------------------------------------------------------------------------
   Una sola gramática espacial. La página define el ancho; los componentes
   dejan de decidirlo por su cuenta. Mobile conserva su flujo original.
   ========================================================================== */
:root {
  --page-reading: 1080px;
  --page-standard: 1280px;
  --page-wide: 1500px;
  --page-gutter-desktop: 32px;
  --page-gutter-large: 40px;
}

.page-shell { width: 100%; margin-inline: auto; }

@media (min-width: 900px) {
  .appbar {
    min-height: 56px;
    padding: 14px var(--page-gutter-desktop) 0;
  }
  .content {
    width: 100%;
    max-width: none;
    padding: 18px var(--page-gutter-desktop) 40px;
  }
  .page-reading { max-width: var(--page-reading); }
  .page-standard { max-width: var(--page-standard); }
  .page-wide { max-width: var(--page-wide); }

  .screen-head { margin-bottom: 22px; }
  .greeting, .screen-title { font-size: 34px; }
  .screen-sub { font-size: 0.8125rem; }

  /* La página gobierna el ancho. Una fila ya no impone 940px por sí misma. */
  .row { max-width: none; }

  /* Headers de página: título/contexto y CTA dentro del mismo rail. */
  .task-screen-head,
  .list-screen-head,
  .areas-screen-head {
    width: 100%;
    max-width: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
  }
  .areas-screen-head .screen-head-main { min-width: 0; }
  /* 2.1.2 — A1: el título ocupa el hueco central y queda pegado a la flecha,
     como en Pendientes y Listas. Con `flex: 0 1 auto` y `space-between` el
     «Áreas y proyectos» flotaba al centro de la cabecera (x=554 a 1280). */
  .areas-screen-head .screen-head-main { flex: 1 1 auto; }
  .areas-add-button { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
  .areas-add-button svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; }

  /* Los estados vacíos pertenecen al rail de contenido, no al centro del viewport. */
  #screen-tasks > .empty,
  #screen-lists > .empty,
  #screen-areas > .empty {
    width: min(100%, 520px);
    margin-left: 0;
    margin-right: 0;
  }

  /* HOY: saludo, tira de resumen a todo lo ancho, y dos columnas.
     1.9 — `.today-color-strip` no tenía área asignada: caía a una fila
     implícita bajo las dos columnas, sólo en la columna izquierda, aunque en
     el DOM va arriba. Ahora es la segunda fila del grid, completa. */
  #screen-today.active {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(320px, .9fr);
    grid-template-areas: "head head" "strip strip" "main aside";
    column-gap: 32px;
    row-gap: 0;
    align-content: start;
  }
  #screen-today.active > .screen-head { grid-area: head; }
  #screen-today.active > .today-color-strip { grid-area: strip; margin-bottom: 26px; }
  #screen-today.active > .col-main { grid-area: main; min-width: 0; }
  #screen-today.active > .col-aside { grid-area: aside; min-width: 0; align-self: start; }
  #screen-today .col-main > .empty { margin-left: 0; margin-right: 0; width: min(100%, 520px); }
  #screen-today .today-calendar-link-empty { margin-left: 0; }
  /* 1.16 — Esta regla se escribió para el Sam DISCRETO: sin fondo propio, su
     única separación es un filete arriba, y arriba del todo de la columna ese
     filete y su respiro sobran. Pero también estaba cayendo sobre
     [data-tone="atencion"], que NO es un apunte al margen sino una tarjeta
     teñida con `padding: var(--s4)` en los cuatro lados. Al anularle el
     padding-top la tarjeta quedaba con 0 arriba y 16px abajo: el contenido
     pegado al borde superior del recuadro rosa. Eso es lo que se veía
     descentrado. Ahora la regla sólo alcanza a la variante sin tono; la
     teñida conserva su caja y sólo pierde el margen exterior. */
  #screen-today .col-aside > .sam-note:first-child:not([data-tone]) { border-top: 0; padding-top: 0; margin-top: 0; }
  #screen-today .col-aside > .sam-note[data-tone]:first-child { margin-top: 0; }
  #screen-today .col-aside .section-label-row:first-child { margin-top: 0; }

  /* Pendientes y Listas: filtros/CTA y filas comparten exactamente el mismo rail. */
  #screen-tasks .task-filter-stack { width: 100%; margin-bottom: 20px; }
  #screen-lists > .sam-note { width: 100%; max-width: 720px; }

  /* Áreas: el CTA visible de escritorio sustituye al FAB móvil. */
  #screen-areas .fab { display: none; }

  /* CALENDARIO: controles próximos entre sí; Día usa un rail standard,
     Semana/Mes conservan el ancho amplio que sí necesitan. */
  #screen-calendar.active { display: block; }
  #screen-calendar .calendar-screen-head,
  #screen-calendar .calendar-nav {
    width: min(100%, 1120px);
  }
  #screen-calendar .calendar-screen-head {
    display: flex;
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: 24px;
  }
  #screen-calendar .calendar-stage[data-calendar-view="day"] { width: min(100%, 1120px); }
  #screen-calendar .calendar-stage[data-calendar-view="week"],
  #screen-calendar .calendar-stage[data-calendar-view="month"] { width: 100%; }
  #screen-calendar .calendar-day-layout { display: block; }
  #screen-calendar .calendar-day-layout.has-aside {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(280px, .75fr);
    gap: 32px;
    align-items: start;
  }
  #screen-calendar .calendar-day-main { max-width: none; }
  #screen-calendar .calendar-day-aside { margin-top: 0; max-width: none; }

  /* CONFIGURACIÓN entra al shell global; conserva su composición 2 columnas. */
  #screen-settings.active { display: block; }
  .settings-screen-head,
  .settings-layout { width: 100%; max-width: none; }
  .settings-layout { grid-template-columns: minmax(0, 1fr) 300px; gap: 32px; }
  .settings-secondary { top: 20px; }

  /* FINANZAS: composición informativa, no igualdad geométrica. */
  #screen-finance.active { display: block; }
  #screen-finance.active > .finance-head,
  #screen-finance.active > .finance-month-nav,
  #screen-finance.active > .finance-dashboard { width: 100%; margin-inline: 0; }
  #screen-finance .finance-month-nav { justify-content: flex-start; margin-bottom: 14px; }
  #screen-finance .finance-dashboard {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 14px;
    align-items: start;
  }
  #screen-finance .finance-dashboard > .finance-module {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    height: auto;
    min-height: 0;
    max-height: none;
    margin: 0 !important;
    overflow: visible;
  }
  #screen-finance .finance-balance-card { grid-column: span 6; }
  #screen-finance .finance-accounts-card { grid-column: span 5; }
  #screen-finance .finance-movements-card { grid-column: span 7; }
  #screen-finance .finance-card-link,
  #screen-finance .finance-text-action {
    border: 0;
    background: transparent;
    font: inherit;
    cursor: pointer;
  }
  #screen-finance .finance-card-link,
  #screen-finance .finance-text-action { min-height: 40px; padding-inline: 8px; }
  #screen-finance .row-more { width: var(--tap); height: var(--tap); }   /* 2.1.1 — era 40x40 */

  .fab { display: none; }
}

@media (min-width: 1440px) {
  .appbar { padding-left: var(--page-gutter-large); padding-right: var(--page-gutter-large); }
  .content { padding-left: var(--page-gutter-large); padding-right: var(--page-gutter-large); }
}

@media (min-width: 900px) and (max-width: 1179px) {
  :root {
    --page-reading: 980px;
    --page-standard: 1040px;
    --page-wide: 1120px;
  }
  #screen-today.active { grid-template-columns: minmax(0, 1.4fr) minmax(290px, .9fr); gap: 24px; }
  #screen-finance .finance-dashboard { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  #screen-finance .finance-balance-card { grid-column: span 6; }
  #screen-finance .finance-accounts-card,
  #screen-finance .finance-movements-card { grid-column: span 3; }
  .settings-layout { grid-template-columns: minmax(0, 1fr) 270px; gap: 24px; }
}

@media (max-width: 899px) {
  .areas-add-button { display: none; }
  /* 2.1.2 — A3: reservar el alto del FAB para que la última área se pueda
     tocar. Al final del scroll el «+» tapaba el 77 % del «•••» de la última
     área y el toque abría «Crear área». El FAB no se quita. */
  #screen-areas.active { padding-bottom: calc(52px + var(--s4)); }
  /* En móvil el sistema global desaparece: cada pantalla conserva su flujo
     táctil, bottom navigation y safe areas originales. */
  .page-shell { max-width: none; }
  #screen-finance .finance-dashboard {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }
  #screen-finance .finance-dashboard > .finance-module { min-width: 0; margin: 0 !important; }
  #screen-finance .finance-balance-card,
  #screen-finance .finance-accounts-card,
  #screen-finance .finance-movements-card { grid-column: 1 / -1; }
  #screen-finance .finance-card-link { border: 0; background: transparent; }
}

@media (max-width: 360px) {
  #screen-finance .finance-dashboard { grid-template-columns: 1fr; }
}

/* 1.9 — Ajuste final de rails: el lienzo de trabajo se CENTRA en el viewport.
   El anclaje izquierdo dejaba hasta 700px de papel muerto a la derecha en
   monitores anchos; centrado, el mismo rail respira igual por ambos lados y
   la app deja de verse como una columna pegada al sidebar. La cabecera y la
   navegación del calendario comparten rail con su propia rejilla: antes la
   cabecera se cortaba a 920px mientras la rejilla corría hasta 1500px y la
   desalineación se notaba a simple vista. */
@media (min-width: 900px) {
  .page-shell { margin-left: auto; margin-right: auto; }
  #screen-calendar .calendar-screen-head,
  #screen-calendar .calendar-nav,
  #screen-calendar.active > .calendar-screen-head,
  #screen-calendar.active > .calendar-nav { width: 100%; }
  #screen-calendar .calendar-stage[data-calendar-view="day"] { width: min(100%, 1240px); }
}

/* 1.9 — Ultrawide: por encima de 1600px los rails crecen un paso y el
   sidebar acompaña. Antes no había NINGUNA adaptación de composición
   después de 1440px. */
@media (min-width: 1600px) {
  :root {
    --page-reading: 1160px;
    --page-standard: 1400px;
    --page-wide: 1680px;
    --page-gutter-large: 48px;
  }
  .sidebar { flex-basis: 240px; }
}

/* ========================================================================== 
   Shared Tasks 1.0 — colaboración humana
   --------------------------------------------------------------------------
   La colaboración añade contexto, no decoración: misma fila, misma tipografía,
   misma densidad. Los avatares son neutros y el color nunca identifica por sí
   solo a una persona o permiso.
   ========================================================================== */
.sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
.person-avatar,.avatar-mini{display:inline-grid;place-items:center;flex:0 0 auto;border:1px solid var(--line);background:var(--surface-sunken);color:var(--ink);font-size: 0.6875rem;font-weight:700;letter-spacing:.02em;border-radius:999px}
.person-avatar{width:28px;height:28px}.person-avatar.large{width:42px;height:42px;font-size: 0.8125rem}.avatar-mini{width:30px;height:30px}
.row-assignee{display:inline-grid;place-items:center;min-width:28px;height:28px;border:1px solid var(--line);border-radius:999px;background:var(--surface-sunken);color:var(--ink);font-size: 0.625rem;font-weight:700;letter-spacing:.02em;flex:0 0 auto}
.assignee-field{position:relative}.assignee-trigger{width:100%;min-height:52px;display:flex;align-items:center;gap:12px;padding: 10px 12px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);color:var(--ink);font:inherit;text-align:left;cursor:pointer}.assignee-trigger:hover{background:var(--surface-hover)}.assignee-trigger:focus-visible,.people-option:focus-visible,.people-invite-link:focus-visible,.people-filter-button:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
.assignee-trigger-copy{min-width:0;display:flex;flex:1;flex-direction:column;gap:2px}.assignee-trigger-copy strong{font-size: 0.875rem;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.assignee-trigger-copy small{font-size: 0.75rem;color:var(--ink-muted)}.assignee-chevron{font-size:22px;color:var(--ink-muted)}
.people-picker-panel{margin-top:8px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);box-shadow:var(--shadow-lift);overflow:hidden}.people-search-wrap{padding:10px;border-bottom:1px solid var(--line)}.people-search{width:100%;min-height:44px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-sunken);color:var(--ink);padding: 10px 12px;font:inherit;font-size: 0.9375rem}.people-options{max-height:264px;overflow:auto;overscroll-behavior:contain}.people-option{width:100%;min-height:52px;display:flex;align-items:center;gap: 10px;padding:8px 12px;border:0;border-bottom:1px solid var(--line);background:transparent;color:var(--ink);font:inherit;text-align:left;cursor:pointer}.people-option:hover,.people-option.selected{background:var(--surface-hover)}.people-option-copy{min-width:0;flex:1;display:flex;flex-direction:column}.people-option-copy strong{font-size: 0.875rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.people-option-copy small{font-size: 0.75rem;color:var(--ink-muted)}.people-option-check{opacity:0;color:var(--ink);font-weight:700}.people-option.selected .people-option-check{opacity:1}
.people-invite-link{width:100%;min-height:46px;padding: 10px 12px;border:0;background:transparent;color:var(--ink);font:inherit;font-weight:600;text-align:left;cursor:pointer}.people-invite-link:hover{background:var(--surface-hover)}.people-invite-inline{padding:12px;border-top:1px solid var(--line);background:var(--surface-sunken)}.people-invite-access{display:grid;gap:8px}.people-invite-access label{min-height:52px;display:flex;align-items:flex-start;gap:10px;padding:10px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);cursor:pointer}.people-invite-access input{margin-top:4px}.people-invite-access span{display:flex;min-width:0;flex-direction:column;gap:2px}.people-invite-access strong{font-size: 0.875rem}.people-invite-access small{font-size: 0.75rem;color:var(--ink-muted);line-height:1.35}
.task-range-line{display:flex;align-items:center;gap:8px;min-width:0}.task-range-line .segmented,.task-range-line .pills{min-width:0;flex:1}.people-filter-button{min-width:44px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;gap:6px;padding: 0 10px;border:1px solid var(--line);border-radius:var(--r-full);background:var(--surface);color:var(--ink);font:inherit;font-size: 0.8125rem;cursor:pointer;white-space:nowrap}.people-filter-button.active{background:var(--surface-sunken);border-color:var(--line-strong)}
.task-assignment-summary{display:flex;align-items:center;gap:12px;padding:12px 0 14px}
/* 1.21.6 — Los datos que uno viene a mirar en la hoja de Asignación: quién te
   pasó el pendiente y para cuándo. Antes sólo estaba el responsable y la hoja
   se veía medio vacía. */
.task-assignment-meta{margin:0 0 16px;color:var(--ink-muted);font-size: 0.8125rem}.task-assignment-summary>div{min-width:0;display:flex;flex-direction:column;gap: 4px}.task-assignment-summary strong{font-size: 1rem}.task-assignment-summary small{color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pending-invites,.pending-area-invites{margin-top:20px;padding-top:16px;border-top:1px solid var(--line)}.pending-invites h3,.pending-area-invites h3,.share-add h3{margin:0 0 10px;font-size: 0.875rem}.pending-invite{display:flex;align-items:center;gap:10px;min-height:48px;padding: 6px 0}.pending-invite>span{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px;overflow-wrap:anywhere}.pending-invite small{color:var(--ink-muted);font-size: 0.75rem}
.share-owner{display:flex;align-items:center;gap: 10px;padding:10px 0 14px}.share-owner>div{display:flex;flex-direction:column}.share-owner strong{font-size: 0.875rem}.share-owner span{font-size: 0.75rem;color:var(--ink-muted)}.share-members{border-top:1px solid var(--line)}.share-member-row{align-items:center;gap:10px}.member-role-control select{min-height:40px;max-width:160px;border:1px solid var(--line);border-radius:var(--r-xs);background:var(--surface);color:var(--ink);font:inherit;font-size: 0.75rem;padding:6px 8px}.member-remove{min-height:40px}.share-add{display:grid;gap:12px;margin-top:22px;padding-top:20px;border-top:1px solid var(--line)}.share-add .field{display:grid;gap: 6px;margin:0}.share-add .field label{font-size: 0.8125rem;font-weight:600;color:var(--ink-muted)}.share-add .field input,.share-add .field select{width:100%;min-height:44px;padding: 10px 12px;border:1px solid var(--line-strong);border-radius:var(--r-sm);background:var(--surface-sunken);color:var(--ink);font:inherit;font-size: 0.875rem;outline:none}.share-add .field input:focus,.share-add .field select:focus{border-color:var(--ink-muted);box-shadow:0 0 0 1px var(--line-strong)}.share-add .field-help{margin:-2px 0 2px;color:var(--ink-muted);font-size: 0.75rem;line-height:1.5}.share-add>.btn{justify-self:start;min-height:44px;padding-inline:18px}.share-add #shareResult{min-width:0}.pending-area-invites{display:grid;gap:6px}.pending-area-invites .pending-invite{padding: 6px 0}
.invite-result{text-align:center;padding:8px 0}.invite-result-mark{width:42px;height:42px;display:grid;place-items:center;margin:0 auto 10px;border:1px solid var(--line-strong);border-radius:999px;background:var(--surface-sunken);font-weight:700}.invite-result h3{margin:0 0 6px;font-size: 1.125rem}.invite-result p{margin:0 auto 14px;max-width:38ch;color:var(--ink-muted);font-size: 0.8125rem}.invite-url input{width:100%;min-height:44px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-sunken);color:var(--ink);padding: 10px 10px;font:inherit;font-size: 0.8125rem}.invite-actions{display:flex;justify-content:center;gap:8px;margin-top:10px}
.waiting-others{margin-top:var(--s5)}.waiting-others .section-head{margin-bottom:var(--s2)}
@media(max-width:899px){.people-search{font-size: 1rem}.people-option,.people-invite-link{min-height:52px}.member-role-control{width:100%}.share-member-row{flex-wrap:wrap}.share-member-row .row-body{min-width:calc(100% - 44px)}.member-role-control select{width:100%;max-width:none;min-height:44px}.member-remove{min-height:44px}.invite-actions{flex-direction:column}.invite-actions .btn{width:100%;min-height:44px}.task-range-line{align-items:stretch}.people-filter-button{min-height:44px}}
@media(max-width:380px){.people-filter-button span.people-filter-label{display:none}.row-assignee{width:26px;height:26px;min-width:26px}}
.invite-review{display:flex;flex-direction:column;align-items:center;text-align:center;padding:8px 0 4px}.invite-review>p{margin:10px 0 4px;color:var(--ink-muted);font-size: 0.8125rem}.invite-review>p strong{color:var(--ink)}.invite-review h3{margin:4px 0;font-family:var(--font-display);font-size:21px;line-height:1.2}.invite-review>small{color:var(--ink-muted)}.invite-access-note{margin-top:14px;max-width:34ch;padding:10px 12px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface-sunken);color:var(--ink-muted);font-size: 0.75rem;line-height:1.35}.invite-review-actions{display:flex;justify-content:flex-end;gap:8px;width:100%}@media(max-width:899px){.invite-review-actions .btn{min-height:44px;flex:1}}

/* ============================================================================
   SAMAPP 1.7.0 · COLOR SYSTEM — CAPA VISUAL
   ========================================================================== */
/* 1.9 — El papel es papel. Los halos radiales de color en el fondo eran el
   primer delator de "plantilla de IA"; la profundidad ahora la dan los
   propios valores de la paleta navy, no un glow decorativo. */
[data-color-mode="color"] body { background: var(--paper); }

/* ------------------------------- Navegación con identidad por sección ---- */
.sidebar { border-right-color:color-mix(in srgb,var(--line) 72%,transparent); }
.nav-link, .tab { position:relative; transition:background var(--dur) var(--ease),color var(--dur) var(--ease),transform var(--dur) var(--ease); }
.sidebar .nav-link[data-view="today"]{--nav-tone:var(--color-coral)}
.sidebar .nav-link[data-view="tasks"]{--nav-tone:var(--area-tasks)}
.sidebar .nav-link[data-view="calendar"]{--nav-tone:var(--area-calendar)}
.sidebar .nav-link[data-view="finance"]{--nav-tone:var(--area-finance)}
.sidebar .nav-link[data-view="lists"]{--nav-tone:var(--area-lists)}
.sidebar .nav-link[data-view="settings"]{--nav-tone:var(--color-indigo)}
/* 1.9 — Disciplina de acento: la navegación en reposo es neutra; el color de
   sección aparece únicamente donde ESTÁS (activo) o donde vas (hover). Seis
   iconos de seis colores encendidos a la vez leían como semáforo. */
[data-color-mode="color"] .sidebar .nav-link svg { color:var(--ink-muted); }
[data-color-mode="color"] .sidebar .nav-link[data-nav-active] svg,
[data-color-mode="color"] .sidebar .nav-link:hover svg { color:var(--nav-tone,var(--ink)); }
[data-color-mode="color"] .sidebar .nav-link[data-nav-active] {
  color:var(--ink-strong);
  background:color-mix(in srgb,var(--nav-tone) 14%,var(--surface-sunken));
  box-shadow:inset 3px 0 0 var(--nav-tone);
}
[data-color-mode="color"] .sidebar .nav-link:hover { background:var(--surface-sunken); }
[data-color-mode="color"] .quick-sam-desktop {
  background:color-mix(in srgb,var(--color-blue) 14%,var(--surface));
  border-color:color-mix(in srgb,var(--color-blue) 28%,var(--line));
}

.tab[data-view="today"]{--nav-tone:var(--color-coral)}
.tab[data-view="tasks"]{--nav-tone:var(--area-tasks)}
.tab[data-view="calendar"]{--nav-tone:var(--area-calendar)}
.tab[data-view="finance"]{--nav-tone:var(--area-finance)}
.tab[data-view="habits"]{--nav-tone:var(--ctx-finanzas)}
/* 1.9.4 — El tab activo se marca con una pastilla suave detrás del icono,
   no con una barrita pegada al borde superior (se veía embarrada contra el
   filo de la barra). */
[data-color-mode="color"] .tab[data-nav-active] { color:var(--nav-tone,var(--ink)); }
[data-color-mode="color"] .tab[data-nav-active]::before {
  content:""; position:absolute; top:2px; left:50%; transform:translateX(-50%);
  width:46px; height:27px; border-radius:var(--r-full);
  background:color-mix(in srgb,var(--nav-tone) 20%,var(--surface));
}
.tab svg { position:relative; }
[data-color-mode="color"] .tab[data-nav-active] span { font-weight:600; }
[data-color-mode="color"] .tab-sam .tab-sam-disc {
  background:color-mix(in srgb,var(--color-blue) 42%,var(--surface));
  border-color:color-mix(in srgb,var(--color-blue) 46%,var(--line));
}

/* ------------------------------------------ Encabezados / acentos -------- */
[data-color-mode="color"] #screen-tasks .screen-title::after,
[data-color-mode="color"] #screen-calendar .screen-title::after,
[data-color-mode="color"] #screen-finance .screen-title::after,
[data-color-mode="color"] #screen-lists .screen-title::after,
[data-color-mode="color"] #screen-settings .screen-title::after {
  content:""; display:inline-block; width:8px; height:8px; margin-left: 10px; border-radius:50%; vertical-align:.2em;
}
[data-color-mode="color"] #screen-tasks .screen-title::after{background:var(--area-tasks-ink)}
[data-color-mode="color"] #screen-calendar .screen-title::after{background:var(--area-calendar-ink)}
[data-color-mode="color"] #screen-finance .screen-title::after{background:var(--area-finance-ink)}
[data-color-mode="color"] #screen-lists .screen-title::after{background:var(--area-lists-ink)}
[data-color-mode="color"] #screen-settings .screen-title::after{background:var(--color-indigo)}

/* -------------------------------------------------- Hoy / dashboard ------- */
.today-hero { position:relative; align-items:center; }
.today-orb {
  width:44px; height:44px; margin-left:auto; border-radius:50%;
  background:radial-gradient(circle at 35% 35%,var(--color-yellow-soft),var(--color-orange) 48%,color-mix(in srgb,var(--color-coral) 75%,transparent));
  box-shadow:0 0 30px color-mix(in srgb,var(--color-orange) 22%,transparent);
  opacity:.86;
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .today-orb{opacity:.72}}
[data-theme="dark"] .today-orb { opacity:.72; }
.today-color-strip { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:10px; margin:0 0 var(--s5); }

/* ----------------------------------------------------------------------------
   1.14.1 · LAS CUATRO FICHAS DE HOY
   ----------------------------------------------------------------------------
   Qué estaba mal, medido:

   1. EL RELLENO AL 9% NO IDENTIFICABA, SÓLO ENSUCIABA. Un tono al 9% sobre
      blanco llega a #F5FBFE: contra el papel eso es una diferencia
      que no se ve, pero sí basta para que la tarjeta deje de leerse limpia.
      En oscuro las cuatro colapsaban al mismo pizarra. Ahora el relleno sube
      al 22% — un pastel de verdad, con el matiz reconocible a un metro.

   2. DE CUATRO COLORES SE PERCIBÍAN TRES. Pendientes en cian (198°) y
      Finanzas en teal (171°): 27° de separación, 0.06 de luminosidad. El
      reparto nuevo vive en --area-* (ver tokens.css) y la separación mínima
      pasa de 27° a 51°.

   3. NO HABÍA JERARQUÍA: "Pagos 0" pesaba exactamente lo mismo que
      "Pendientes 11", porque ambos usaban --ink-strong sobre el mismo tinte.
      Un cero es lo menos interesante de la pantalla. Ahora una ficha en cero
      renuncia al color y a la tinta fuerte (ver [data-count="0"] abajo): el
      contraste de su cifra cae de 15.6:1 a 4.4:1 y la mirada deja de
      gastarse en ella. La jerarquía la produce el DATO, no una decisión
      estética congelada.

   4. TODO TENÍA LA MISMA FORMA. Fichas y filas compartían radio (--r-md),
      borde de 1px y sombra baja: ocho rectángulos idénticos. Las fichas
      pasan a ser placas — radio --r-lg, SIN borde y SIN sombra, relleno
      plano. Las filas conservan borde y radio de fila. Ahora el ojo
      distingue "resumen" de "contenido" antes de leer una palabra.

   El color va bajo [data-color-mode="color"]: en 1.7 estas reglas no tenían
   guardia de modo y Sobrio seguía mostrando cuatro fichas de colores.
   ------------------------------------------------------------------------- */
.today-mini-card {
  --mini-tone:var(--area-tasks); --mini-ink:var(--area-tasks-ink);
  /* 1.14.2 — El acorchete lateral pasa a variable para que el acento de
     Vivo pueda sumarse a él sin pisar el valor reducido de móvil. Los valores
     resueltos son los de siempre (14px, 11px en <=760px): Pastel no se mueve. */
  --card-pad:14px;
  min-width:0; min-height:72px; padding:12px var(--card-pad); border:0;
  border-radius:var(--r-lg); background:var(--surface-sunken);
  color:var(--ink); text-align:left; cursor:pointer;
  transition:transform var(--dur) var(--ease),background var(--dur) var(--ease);
}
.today-mini-card[data-tone="tasks"]   { --mini-tone:var(--area-tasks);    --mini-ink:var(--area-tasks-ink) }
.today-mini-card[data-tone="calendar"]{ --mini-tone:var(--area-calendar); --mini-ink:var(--area-calendar-ink) }
.today-mini-card[data-tone="finance"] { --mini-tone:var(--area-finance);  --mini-ink:var(--area-finance-ink) }
.today-mini-card[data-tone="lists"]   { --mini-tone:var(--area-lists);    --mini-ink:var(--area-lists-ink) }

/* Etiqueta en la tinta del área; cifra en tinta neutra fuerte. Teñir también
   la cifra sería repetir el error de Finanzas: dos elementos gritando lo
   mismo. La etiqueta dice DE QUÉ es la ficha, la cifra dice CUÁNTO. */
.today-mini-card span { display:block; color:var(--mini-ink); font-size: 0.75rem; font-weight:600; letter-spacing:.01em; }
.today-mini-card strong { display:block; margin-top:6px; color:var(--ink-strong); font:600 24px/1 var(--font-num); letter-spacing:-.02em; }
.today-mini-card:hover { transform:translateY(-1px); background:var(--surface-hover); }

/* Relleno real, sólo en modo color, y mezclado contra --tone-canvas en vez de
   --surface: en oscuro el navy rotaba el ámbar 77° hacia el verde y dejaba
   las cuatro fichas a 22° unas de otras (ver tokens.css). Contra el lienzo
   neutro la deriva máxima es 14° y la separación real sube a 63°.

   24% medido. Etiqueta -ink sobre este fondo: 4.80–5.18 en claro,
   5.19–5.95 en oscuro. Cifra: 15.3–17.0 y 7.56–8.96. AA con margen en las
   cuatro áreas y los dos temas. Por encima del 28% la etiqueta cae de 4.5 y
   deja de cumplir; por eso el hover sube el tono pero no el relleno. */
/* 1.14.2 — La dosis pasa a token (--mix-card, ver tokens.css). Pastel sigue en
   24% y no se mueve; Vivo la sube a 46%, que es donde el relleno deja de verse
   pastel (saturación media 31 frente a 9) sin que la etiqueta pierda AA. */
[data-color-mode="color"] .today-mini-card { background:color-mix(in srgb,var(--mini-tone) var(--mix-card),var(--tone-canvas)); }
[data-color-mode="color"] .today-mini-card:hover { background:color-mix(in srgb,var(--mini-tone) var(--mix-card-hover),var(--tone-canvas)); }

/* 1.15.3 — Acento de tono PLENO en la ficha, ahora en TEMA OSCURO (antes era
   exclusivo del estilo Vivo, ya retirado).

   Ricardo, con la app en oscuro: "casi no se ve el pastel en oscuro", "se ve
   muy bajito hay que mejorarlo". Se probó la vía obvia —subir la dosis del
   relleno— y se descartó MIDIÉNDOLA: barrido de 28% a 58%, el techo de
   contraste está en 34% y ahí la saturación de la ficha apenas llega a 9.4
   sobre 100. Los pasteles son de poco croma por definición; diluidos no
   pueden verse vivos, y forzarlo se come todo el margen de legibilidad.

   El color hay que ponerlo donde SÍ puede ir a tope. Esta barra no lleva
   texto encima, así que no tiene ninguna restricción de contraste, y sobre un
   fondo casi negro un pastel a tono pleno se ve perfectamente. Mismo lenguaje
   que el marcador de área de las filas. */
[data-theme="dark"][data-color-mode="color"] .today-mini-card { position:relative; overflow:hidden; }
[data-theme="dark"][data-color-mode="color"] .today-mini-card::before {
  content:""; position:absolute; inset:10px auto 10px 0; width:var(--rule-w);
  border-radius:0 var(--r-full) var(--r-full) 0; background:var(--mini-tone);
}
[data-theme="dark"][data-color-mode="color"] .today-mini-card { padding-left:calc(var(--card-pad) + var(--rule-w)); }
/* La ficha en cero renuncia al color, también al acento: si no, seguiría
   gritando justo la ficha que no tiene nada que contar. */
[data-theme="dark"][data-color-mode="color"] .today-mini-card[data-count="0"]::before { display:none; }
[data-theme="dark"][data-color-mode="color"] .today-mini-card[data-count="0"] { padding-left:var(--card-pad); }

/* Mismo acento para quien nunca eligió tema y hereda el oscuro del sistema. */
@media (prefers-color-scheme:dark) {
  :root:not([data-theme="light"])[data-color-mode="color"] .today-mini-card { position:relative; overflow:hidden; padding-left:calc(var(--card-pad) + var(--rule-w)); }
  :root:not([data-theme="light"])[data-color-mode="color"] .today-mini-card::before {
    content:""; position:absolute; inset:10px auto 10px 0; width:var(--rule-w);
    border-radius:0 var(--r-full) var(--r-full) 0; background:var(--mini-tone);
  }
  :root:not([data-theme="light"])[data-color-mode="color"] .today-mini-card[data-count="0"]::before { display:none; }
  :root:not([data-theme="light"])[data-color-mode="color"] .today-mini-card[data-count="0"] { padding-left:var(--card-pad); }
}

/* El precio de subir la dosis de las filas: el metadato va en --ink-muted y ya
   fallaba AA sobre el tinte del 15% de Pastel (4.07 medido, AA pide 4.5). Al
   30% de Vivo caería a 3.4, que ya no es un roce sino ilegible.
   La corrección no toca --ink-muted global —eso movería Pastel— sino sólo el
   metadato de las filas teñidas, y lo hace con una mezcla contra --ink, que
   se resuelve sola en los dos temas: en claro --ink es casi negro y oscurece
   el gris; en oscuro es casi blanco y lo aclara. Sin dejar de leerse como
   texto secundario.

   El selector va por [data-ctx] y NO por .tinted a propósito: el primer
   intento sólo cubrió .row.tinted y dejó fuera las filas de "Esta semana",
   que se tiñen por otra regla y se quedaron en 3.45. Cualquier fila con área
   acaba teñida en Vivo, así que la condición correcta es tener área. */
/* 1.15.0 — La corrección deja de ser exclusiva de Vivo: medido en el DOM,
   la hora de una fila teñida en Pastel OSCURO se quedaba en 4.01, y eso ya
   pasaba antes de esta paleta. La condición correcta es "la fila tiene área",
   no "la paleta es Vivo". En Pastel claro el efecto es de una décima larga y
   siempre hacia arriba. */
[data-color-mode="color"] .row[data-ctx]:not([data-ctx="neutro"]) .row-meta,
[data-color-mode="color"] .row[data-ctx]:not([data-ctx="neutro"]) .time-col {
  color:color-mix(in srgb,var(--ink) 42%,var(--ink-muted));
}

/* Cero = ninguna noticia. Se apaga entero: sin color y con la cifra en
   --ink-muted (4.44 en claro, 7.26 en oscuro — pasa el umbral 3.0 de texto
   grande con holgura, así que sigue siendo legible, sólo deja de competir).
   Va DESPUÉS del bloque de color para ganarle por orden a igual
   especificidad (0,2,0). */
.today-mini-card[data-count="0"],
[data-color-mode="color"] .today-mini-card[data-count="0"] { background:var(--surface-sunken); }
.today-mini-card[data-count="0"]:hover,
[data-color-mode="color"] .today-mini-card[data-count="0"]:hover { background:var(--surface-hover); }
/* La etiqueta de la ficha en cero NO puede ser --ink-muted a secas: medida en
   vivo sobre la superficie hundida da 4.44 en claro (4.43 en sobrio) y AA pide
   4.5 para 11.5px. Un 10% de --ink la sube a ~5.1 sin dejar de leerse
   apagada — la jerarquía la sigue haciendo la CIFRA (15.6:1 viva contra
   4.44:1 en cero), no la etiqueta. */
.today-mini-card[data-count="0"] span { color:color-mix(in srgb,var(--ink) 10%,var(--ink-muted)); }
.today-mini-card[data-count="0"] strong { color:var(--ink-muted); font-weight:600; }

/* 2.1.2 — Canto de 1 px en las fichas, SÓLO en claro. Revisa la decisión
   «placas SIN borde» (punto 4 de arriba): en claro-sobrio las fichas eran
   #f5f5f5 sobre #fafafa (1.045:1, ΔE 1.5) y en claro con color la de Listas
   quedaba en ΔE 1.7: no se distinguían del fondo. Sin sombra, para que sigan
   siendo placas y no se confundan con las filas. Con el canto pasan a
   1.2–1.4:1 (ΔE 9–13). En oscuro ya se distinguían (ΔE 6.6–23) y no cambian.
   En color, el canto es el mismo tono de la ficha contra el lienzo neutro
   (--tone-canvas, no --surface, para no rotar el matiz); la ficha en cero,
   que renuncia al color, lleva el canto neutro de Sobrio y así las cuatro
   siguen teniendo orilla. */
[data-theme="light"] .today-mini-card { border:1px solid var(--line-strong); }
[data-theme="light"][data-color-mode="color"] .today-mini-card:not([data-count="0"]) { border-color:color-mix(in srgb,var(--mini-tone) 70%,var(--tone-canvas)); }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .today-mini-card { border:1px solid var(--line-strong); }
  :root:not([data-theme])[data-color-mode="color"] .today-mini-card:not([data-count="0"]) { border-color:color-mix(in srgb,var(--mini-tone) 70%,var(--tone-canvas)); }
}

/* ------------------------------------------------ Pendientes ------------- */
[data-color-mode="color"] #screen-tasks .task-range-line .chip { border-color:color-mix(in srgb,var(--line-strong) 82%,transparent); }
[data-color-mode="color"] #screen-tasks .task-range-line .chip:nth-child(1){--chip-tone:var(--color-blue)}
[data-color-mode="color"] #screen-tasks .task-range-line .chip:nth-child(2){--chip-tone:var(--color-cyan)}
[data-color-mode="color"] #screen-tasks .task-range-line .chip:nth-child(3){--chip-tone:var(--color-purple)}
[data-color-mode="color"] #screen-tasks .task-range-line .chip:nth-child(4){--chip-tone:var(--color-indigo)}
[data-color-mode="color"] #screen-tasks .task-range-line .chip:hover { border-color:color-mix(in srgb,var(--chip-tone) 42%,var(--line)); background:color-mix(in srgb,var(--chip-tone) 9%,var(--surface)); }
[data-color-mode="color"] #screen-tasks .task-range-line .chip[aria-selected="true"] {
  /* 1.15.0 — 72+6 = 78, y color-mix con porcentajes que no suman 100 devuelve
     un color con ALFA .78. En claro no se notaba (debajo hay blanco); en
     oscuro el relleno se mezclaba con la superficie y la tinta caía a 4.04.
     92+8 = 100: mismo color en claro, opaco en los dos temas, 5.46 en el peor
     caso (morado). */
  background:color-mix(in srgb,var(--chip-tone) 92%,#fff 8%); color:var(--ink-on-color); border-color:transparent;
}
/* 1.15.0 — EL RELLENO DE LA FILA SE MEZCLA CONTRA --tone-canvas, NO CONTRA
   --surface. Esta línea es el origen literal de la queja de Ricardo: "las
   filas salen verde oliva y rojo vino". En claro no se notaba porque
   --surface es blanco y mezclar con un neutro conserva el matiz exacto; en
   oscuro --surface era el navy #1A2F44, que tiene matiz propio y se lo comía
   todo. Medido con la dosis del 22% sobre ese navy:

       ámbar   #E7B510  46° -> 76°   y saturación 87 -> 14   (= verde oliva)
       rojo    #EA5057 357° -> 300°  y saturación 79 -> 14   (= rojo vino)
       magenta #D271E6 290° -> 246°                          (= azul violeta)

   Contra el lienzo neutro los mismos tonos conservan el matiz (±2°) y la
   saturación se queda en 39-61. Las fichas de Hoy ya se mezclaban así desde
   1.14.1; las filas se habían quedado fuera. */
[data-color-mode="color"] #screen-tasks .row[data-ctx]:not([data-ctx="neutro"]),
[data-color-mode="color"] #screen-lists .row[data-ctx]:not([data-ctx="neutro"]) {
  overflow:hidden; position:relative;
  background:color-mix(in srgb,var(--ctx) var(--mix-row),var(--tone-canvas));
  border-color:color-mix(in srgb,var(--ctx) var(--mix-row-border),var(--line));
}
[data-color-mode="color"] #screen-tasks .row[data-ctx]:not([data-ctx="neutro"])::before,
[data-color-mode="color"] #screen-lists .row[data-ctx]:not([data-ctx="neutro"])::before {
  content:""; position:absolute; inset:8px auto 8px 0; width:3px; border-radius:0 3px 3px 0; background:var(--ctx);
}
[data-color-mode="color"] #screen-tasks .row[data-ctx]:not([data-ctx="neutro"]):hover,
[data-color-mode="color"] #screen-lists .row[data-ctx]:not([data-ctx="neutro"]):hover {
  transform:translateY(-1px); background:color-mix(in srgb,var(--ctx) var(--mix-row-hover),var(--tone-canvas));
  border-color:color-mix(in srgb,var(--ctx) var(--mix-row-border-hover),var(--line)); box-shadow:var(--shadow-low);
}

/* Avatares asignados, determinísticos. */
/* 1.16 — Cada tono trae ADEMÁS su tinta legible. El relleno usa el tono
   pastel; las iniciales usan `--color-*-ink`, la familia que tokens.css creó
   justo para esto (con sus contrastes medidos anotados línea por línea) y que
   en oscuro se resuelve al `-soft`.

   Antes las iniciales iban en el mismo pastel del relleno: medido sobre el
   avatar de una fila en claro/color daba 1.21:1 — invisible. Daba igual
   mientras el avatar sólo repetía tus propias iniciales, pero desde 1.16 el
   avatar identifica a OTRA persona y esas dos letras hay que leerlas. */
.person-avatar[data-avatar-tone="azul"]{--avatar-tone:var(--color-blue);--avatar-ink:var(--color-blue-ink)}
.person-avatar[data-avatar-tone="cielo"]{--avatar-tone:var(--color-cyan);--avatar-ink:var(--color-cyan-ink)}
.person-avatar[data-avatar-tone="menta"]{--avatar-tone:var(--color-teal);--avatar-ink:var(--color-teal-ink)}
.person-avatar[data-avatar-tone="verde"]{--avatar-tone:var(--color-green);--avatar-ink:var(--color-green-ink)}
.person-avatar[data-avatar-tone="naranja"]{--avatar-tone:var(--color-orange);--avatar-ink:var(--color-orange-ink)}
.person-avatar[data-avatar-tone="coral"]{--avatar-tone:var(--color-coral);--avatar-ink:var(--color-coral-ink)}
.person-avatar[data-avatar-tone="rosa"]{--avatar-tone:var(--color-pink);--avatar-ink:var(--color-pink-ink)}
.person-avatar[data-avatar-tone="magenta"]{--avatar-tone:var(--color-magenta);--avatar-ink:var(--color-magenta-ink)}
.person-avatar[data-avatar-tone="morado"]{--avatar-tone:var(--color-purple);--avatar-ink:var(--color-purple-ink)}
[data-color-mode="color"] .person-avatar[data-avatar-tone] { background:color-mix(in srgb,var(--avatar-tone) 20%,var(--surface-sunken)); color:var(--avatar-ink,var(--ink)); border:1px solid color-mix(in srgb,var(--avatar-tone) 36%,var(--line)); }

/* -------------------------------------------------- Calendario ----------- */
[data-color-mode="color"] .calendar-view-tabs { background:color-mix(in srgb,var(--area-calendar) 7%,var(--surface-sunken)); border-color:color-mix(in srgb,var(--area-calendar) 18%,var(--line)); }
[data-color-mode="color"] .calendar-view-tab[aria-selected="true"] { background:color-mix(in srgb,var(--area-calendar) 22%,var(--surface)); color:var(--area-calendar-ink); box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--area-calendar) 22%,transparent); }
[data-color-mode="color"] .calendar-nav-current { color:var(--area-calendar-ink); background:color-mix(in srgb,var(--area-calendar) 11%,var(--surface-sunken)); border-color:color-mix(in srgb,var(--area-calendar) 22%,var(--line)); }
[data-color-mode="color"] .calendar-agenda-track span { background:var(--ctx,var(--area-calendar)); box-shadow:0 0 0 4px color-mix(in srgb,var(--ctx,var(--area-calendar)) 13%,transparent); }
[data-color-mode="color"] .calendar-agenda-row[data-ctx]:not([data-ctx="neutro"]) .calendar-agenda-body {
  background:color-mix(in srgb,var(--ctx) 9%,var(--tone-canvas));
  border:1px solid color-mix(in srgb,var(--ctx) 20%,var(--line)); padding-inline:var(--s4);
}
[data-color-mode="color"] .calendar-week-event[data-ctx]:not([data-ctx="neutro"]) { border-left-width:3px; border-color:color-mix(in srgb,var(--ctx) 25%,var(--line)); border-left-color:var(--ctx); background:color-mix(in srgb,var(--ctx) 12%,var(--tone-canvas)); }
[data-color-mode="color"] .calendar-month-event[data-ctx]:not([data-ctx="neutro"]) i,
[data-color-mode="color"] .calendar-month-mobile-count i { background:var(--ctx,var(--area-calendar)); }
[data-color-mode="color"] .calendar-month-day.today .calendar-month-number { background:var(--area-calendar); color:var(--ink-on-color); }
[data-color-mode="color"] .calendar-empty { position:relative; padding-left:64px; min-height:72px; }
[data-color-mode="color"] .calendar-empty::before {
  content:""; position:absolute; left:4px; top:9px; width:40px; height:38px; border-radius:10px;
  border:1px solid color-mix(in srgb,var(--area-calendar) 40%,var(--line));
  background:linear-gradient(to bottom,var(--area-calendar) 0 9px,color-mix(in srgb,var(--area-calendar) 9%,var(--surface)) 9px 100%);
}

/* -------------------------------------------------------- Listas ---------- */
/* 1.16.4 — La pestaña activa de Listas NO se distinguía de la inactiva.
   Medido en claro·pastel: el relleno se separaba de la pista 4 puntos de RGB
   (ΔE 4.36, contraste 1.11) y la tinta del activo sólo mejoraba 0.73 sobre la
   del inactivo. Con los tonos pastel, un relleno al 12% no alcanza para marcar
   un estado; en Sobrio el mismo control funciona porque la TINTA salta 3.5x.
   La solución es la misma: que el estado lo lleve la tinta, no el fondo.
   El `border-color` que había aquí no pintaba nada — `.mobile-work-tab` lleva
   `border: 0`. */
[data-color-mode="color"] #screen-lists .mobile-work-tab[aria-selected="true"],
[data-color-mode="color"] #screen-tasks .mobile-work-tab[aria-selected="true"] {
  color: var(--ink-strong);
  font-weight: 600;
  background: var(--surface);
  box-shadow: var(--shadow-low);
}
[data-color-mode="color"] #screen-lists .inline-add { border-color:color-mix(in srgb,var(--area-lists) 22%,var(--line)); background:color-mix(in srgb,var(--area-lists) 6%,var(--surface-sunken)); }
[data-color-mode="color"] #screen-lists .inline-add-icon { color:var(--area-lists-ink); }

/* ---------------------------------------------------------- Sam ---------- */
/* 1.9 — Las burbujas del chat son superficies planas: la del usuario firma
   con UN tono cálido, la de Sam es neutra. Los starters comparten un solo
   tono — cuatro colores repartidos por posición no significaban nada. */
[data-color-mode="color"] .msg-user { background:color-mix(in srgb,var(--color-coral) 22%,var(--surface)); border:1px solid color-mix(in srgb,var(--color-coral) 26%,var(--line)); }
[data-color-mode="color"] .msg-sam { background:var(--surface-sunken); border:1px solid var(--line); }
[data-color-mode="color"] .composer { border-color:color-mix(in srgb,var(--color-blue) 24%,var(--line-strong)); box-shadow:0 10px 30px rgba(0,0,0,.12); }
[data-color-mode="color"] .composer:focus-within { border-color:color-mix(in srgb,var(--color-cyan) 60%,var(--line)); box-shadow:0 0 0 3px color-mix(in srgb,var(--color-cyan) 10%,transparent); }
[data-color-mode="color"] .composer-send:not(:disabled) { color:var(--color-cyan); }
[data-color-mode="color"] .sam-starter { --starter-tone:var(--color-blue); border-color:color-mix(in srgb,var(--starter-tone) 22%,var(--line)); background:color-mix(in srgb,var(--starter-tone) 6%,var(--surface)); }
[data-color-mode="color"] .sam-starter:hover { background:color-mix(in srgb,var(--starter-tone) 12%,var(--surface)); border-color:color-mix(in srgb,var(--starter-tone) 40%,var(--line)); }

.proposal[data-tone="task"]{--proposal-tone:var(--area-tasks)}
.proposal[data-tone="calendar"]{--proposal-tone:var(--area-calendar)}
.proposal[data-tone="expense"]{--proposal-tone:var(--color-coral)}
.proposal[data-tone="income"]{--proposal-tone:var(--color-green)}
.proposal[data-tone="commitment"]{--proposal-tone:var(--color-orange)}
.proposal[data-tone="list"]{--proposal-tone:var(--area-lists)}
.proposal[data-tone="info"]{--proposal-tone:var(--color-blue)}
/* 1.22.0 — Faltaba el tono de lo irreversible. "Eliminar pendiente" salía con
   el mismo azul que "Crear pendiente": confirmabas un borrado con el mismo
   gesto y el mismo aspecto que un alta. El tono va por lo que la acción HACE,
   no por la entidad que toca. */
.proposal[data-tone="danger"]{--proposal-tone:var(--ctx-atencion)}
[data-color-mode="color"] .proposal[data-tone] { position:relative; overflow:hidden; border-color:color-mix(in srgb,var(--proposal-tone) 28%,var(--line)); background:color-mix(in srgb,var(--proposal-tone) 9%,var(--surface)); }
[data-color-mode="color"] .proposal[data-tone]::before { content:""; position:absolute; left:0; top:0; bottom:0; width:4px; background:var(--proposal-tone); }
/* 1.22.0 — Esta regla pisaba la de más abajo, que sí mezclaba el tono con la
   tinta, y pintaba la etiqueta con el tono PURO. En el tema oscuro no se
   notaba (6:1 o más), pero en el claro los tonos son colores pensados para
   fondo oscuro y el contraste caía a 1.47:1 — la etiqueta que dice si vas a
   CREAR o a ELIMINAR era casi invisible justo donde más importa: antes de
   confirmar. Medido tono a tono en los dos temas: al 38 % los siete pasan de
   4.5:1 (4.94 el peor) y conservan su matiz. */
[data-color-mode="color"] .proposal[data-tone] .prop-verb {
  color: color-mix(in srgb, var(--proposal-tone) 38%, var(--ink));
}
[data-color-mode="color"] .proposal[data-tone] .btn-primary { background:color-mix(in srgb,var(--proposal-tone) 92%,#fff 8%); color:var(--ink-on-color); }
[data-color-mode="color"] .proposal.done[data-tone] { background:color-mix(in srgb,var(--color-green) 11%,var(--surface)); border-color:color-mix(in srgb,var(--color-green) 30%,var(--line)); }

/* -------------------------------------------------------- Finanzas ------- */
/* 1.9 — Los KPI se tiñen plano y sólo por significado (verde entra, coral
   sale, teal disponible). Los paneles pierden el arcoíris por posición:
   Finanzas ES teal; el color de cada cuenta ya lo pone el usuario. */
/* 1.14 — Las cuatro tarjetas KPI (`.finance-kpi-*`) se retiraron con el
   rediseño del tablero: repetían Ingresos, Gastos y Saldo que la tarjeta de
   resumen ya imprime. Sus reglas de tinte se borran con ellas. */
[data-color-mode="color"] .finance-panel { --panel-tone:var(--color-teal); border-color:color-mix(in srgb,var(--panel-tone) 14%,var(--finance-card-border)); }

/* ------------------------------------------------ Configuración ---------- */
/* 1.9 — Configuración es índigo, una sola voz: tres tarjetas de tres colores
   por orden de aparición era decoración sin significado. */
[data-color-mode="color"] .settings-card { --settings-tone:var(--color-indigo); border-color:color-mix(in srgb,var(--settings-tone) 14%,var(--line)); background:var(--surface); }
[data-color-mode="color"] .settings-card-head h2 { color:var(--ink-strong); }

/* ------------------------------------------------ Empty states ----------- */
[data-color-mode="color"] .empty { position:relative; overflow:hidden; border-color:color-mix(in srgb,var(--empty-tone,var(--color-blue)) 18%,var(--line)); background:color-mix(in srgb,var(--empty-tone,var(--color-blue)) 5%,var(--surface-sunken)); }
[data-color-mode="color"] #screen-tasks .empty{--empty-tone:var(--area-tasks)}
[data-color-mode="color"] #screen-lists .empty{--empty-tone:var(--area-lists)}
[data-color-mode="color"] #screen-finance .empty{--empty-tone:var(--area-finance)}
[data-color-mode="color"] .empty::before { content:""; display:block; width:28px; height:28px; margin:0 auto 9px; border-radius:10px; background:color-mix(in srgb,var(--empty-tone,var(--color-blue)) 18%,var(--surface)); border:1px solid color-mix(in srgb,var(--empty-tone,var(--color-blue)) 38%,var(--line)); box-shadow:inset 0 0 0 7px color-mix(in srgb,var(--empty-tone,var(--color-blue)) 5%,transparent); }

/* ------------------------------------------------ Buttons / micro ------- */
[data-color-mode="color"] .btn-primary { box-shadow:0 5px 16px rgba(0,0,0,.08); }
/* 1.32.0 — EL BOTÓN PRIMARIO SE TIÑE EN LAS CUATRO PANTALLAS, NO EN DOS.
   La idea ya existía y estaba bien: en modo color, la acción principal lleva
   el tono de su pantalla. Sólo que se había aplicado a Pendientes y Listas y
   ahí se quedó — Calendario y Finanzas seguían en tinta. El resultado era que
   el MISMO botón, en la MISMA esquina, cambiaba de naturaleza al navegar:
   periwinkle, ámbar, navy, navy. No es paleta contextual, es un despiste.

   Sigue colgando de [data-color-mode="color"] a propósito: en modo sobrio los
   cuatro --area-* caen a --ink-muted y el botón vuelve a ser tinta, que es
   justo lo que el modo sobrio promete. Un solo mecanismo, dos resultados
   coherentes. */
[data-color-mode="color"] #screen-tasks    .task-add-button     { background:color-mix(in srgb,var(--area-tasks) 92%,#fff 8%);    color:var(--ink-on-color); }
[data-color-mode="color"] #screen-lists    .list-add-button     { background:color-mix(in srgb,var(--area-lists) 92%,#fff 8%);    color:var(--ink-on-color); }
[data-color-mode="color"] #screen-calendar .calendar-add-button { background:color-mix(in srgb,var(--area-calendar) 92%,#fff 8%); color:var(--ink-on-color); }
/* «+ Movimiento» es el único de los cuatro que no tiene clase propia: se le
   entra por su acción, que es igual de estable y no obliga a tocar screens.js. */
[data-color-mode="color"] #screen-finance  [data-act="new-tx-ui"].btn-primary { background:color-mix(in srgb,var(--area-finance) 92%,#fff 8%); color:var(--ink-on-color); }
[data-color-mode="color"] .row, [data-color-mode="color"] .finance-panel, [data-color-mode="color"] .today-mini-card { transition:transform .18s var(--ease),background .18s var(--ease),border-color .18s var(--ease),box-shadow .18s var(--ease); }

/* ------------------------------------------------ Responsive ------------ */
@media (max-width:760px){
  .today-color-strip{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
  .today-mini-card{--card-pad:11px;min-height:60px;padding:9px var(--card-pad)}
  .today-mini-card strong{font-size: 1.1875rem}
  .today-orb{width:34px;height:34px}
  /* 1.15.0 — En pantalla estrecha la fila ocupa casi todo el ancho, así que
     la dosis baja; pero estaba escrita a mano al 7% y contra --surface, y eso
     dejaba a Vivo sin color justo en móvil (que es donde Ricardo lo vio) y con
     la deriva de matiz del navy intacta. Ahora es un token por paleta y se
     mezcla contra el lienzo neutro como todo lo demás. */
  [data-color-mode="color"] #screen-tasks .row[data-ctx]:not([data-ctx="neutro"]),
  [data-color-mode="color"] #screen-lists .row[data-ctx]:not([data-ctx="neutro"]){background:color-mix(in srgb,var(--ctx) var(--mix-row-narrow),var(--tone-canvas))}
}
@media (max-width:430px){
  [data-color-mode="color"] body{background:var(--paper)}
  .today-color-strip{margin-bottom:var(--s4)}
  .today-mini-card{box-shadow:none}
  [data-color-mode="color"] .proposal[data-tone], [data-color-mode="color"] .settings-card{background:color-mix(in srgb,var(--proposal-tone,var(--settings-tone,var(--color-blue))) 7%,var(--surface))}
}

/* 1.7.0 · selector de color reutilizable para cuentas/categorías. */
.color-picker { margin:0; padding:0; border:0; }
.color-picker legend { margin-bottom:8px; color:var(--ink-muted); font-size: 0.8125rem; font-weight:600; }
.color-swatches { display:grid; grid-template-columns:repeat(6,34px); gap: 10px; }
.color-swatch { position:relative; width:34px; height:34px; border-radius:50%; background:var(--swatch); cursor:pointer; box-shadow:0 0 0 1px color-mix(in srgb,var(--swatch) 42%,var(--line)), inset 0 0 0 1px rgba(255,255,255,.13); transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease); }
.color-swatch:hover { transform:translateY(-1px) scale(1.04); box-shadow:0 0 0 2px color-mix(in srgb,var(--swatch) 60%,var(--line)),0 6px 16px color-mix(in srgb,var(--swatch) 18%,transparent); }
.color-swatch input { position:absolute; opacity:0; pointer-events:none; }
/* 2.1.2 — `>`: dentro va ahora el <span class="i"> del ✓ en SVG, que no debe heredar la capa. */
.color-swatch > span { position:absolute; inset:0; display:grid; place-items:center; color:#10263A; font-size: 0.9375rem; font-weight:700; opacity:0; transform:scale(.7); transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease); text-shadow:0 1px rgba(255,255,255,.45); }
.color-swatch input:checked + span { opacity:1; transform:scale(1); }
.color-swatch:has(input:checked) { box-shadow:0 0 0 2px var(--surface),0 0 0 4px var(--swatch),0 8px 18px color-mix(in srgb,var(--swatch) 20%,transparent); }
.color-swatch:has(input:focus-visible) { outline:2px solid var(--focus-ring); outline-offset:4px; }
/* 1.9.3 — Dígitos tabulares donde la fuente los ofrezca: cifras alineadas
   en todo lo que muestra dinero, horas o conteos. Sustituye al monoespaciado. */
.row-amount, .money-cell strong, .finance-tx-amount,
.finance-account-balance, .today-mini-card strong,
.calendar-agenda-time, .calendar-week-head strong, .calendar-week-event-time,
.calendar-week-strip-day strong, .calendar-month-number, .prop-field b {
  font-variant-numeric: tabular-nums;
}

[data-color-mode="color"] .finance-account-row { border-left:3px solid color-mix(in srgb,var(--account-color,var(--ctx-finanzas)) 72%,transparent); }
[data-color-mode="color"] .finance-account-row .finance-account-icon { color:var(--account-color,var(--ctx-finanzas)); background:color-mix(in srgb,var(--account-color,var(--ctx-finanzas)) 15%,var(--surface-sunken)); box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--account-color,var(--ctx-finanzas)) 22%,transparent); }
@media(max-width:430px){.color-swatches{grid-template-columns:repeat(6,30px);gap:8px}.color-swatch{width:30px;height:30px}}

/* ============================================================================
   SAMAPP 1.9.5 · TARJETAS DEL CHAT — PASE PERFECCIONISTA
   ----------------------------------------------------------------------------
   Bloque consolidado al final: gana por cascada sin reescribir el histórico.
   Principios: una sola silueta de tarjeta en el hilo, estados SIEMPRE
   visibles (deshabilitado/guardando/hecho/bloqueado), etiquetas que no
   aplastan valores en móvil, y ritmo de hilo que distingue turnos.
   ========================================================================== */

/* --- Estados de botón que faltaban en toda la app ----------------------- */
.btn:disabled,
.btn[disabled] {
  opacity: .55;
  cursor: default;
  pointer-events: none;
  transform: none !important;
  box-shadow: none !important;
}

/* --- Ritmo del hilo: los turnos respiran, los objetos comparten borde --- */
.thread { gap: var(--s4); }
.msg-group.has-summary { width: min(720px, 94%); }
.msg-group:has(.proposal) { width: min(720px, 94%); max-width: min(720px, 94%); }

/* --- Silueta única: propuesta y briefing son la misma familia ----------- */
.proposal,
.sam-brief {
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-card);
}
.proposal { padding: var(--s5); }
@media (max-width: 430px) {
  .proposal, .sam-brief { border-radius: var(--r-md); }
  .proposal { padding: var(--s4); }
}

/* --- Cabecera de propuesta: el verbo es un chip tonal, el título manda -- */
.prop-head {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
}
.prop-verb {
  display: inline-block;
  padding: 4px 10px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--proposal-tone, var(--ink-muted)) 16%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--proposal-tone, var(--ink-muted)) 24%, transparent);
  color: color-mix(in srgb, var(--proposal-tone, var(--ink-muted)) 72%, var(--ink));
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: .07em;
}
[data-color-mode="sober"] .prop-verb {
  background: var(--surface-sunken);
  border-color: var(--line);
  color: var(--ink-muted);
}
.prop-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink-strong);
  line-height: 1.35;
}

/* --- Campos: rejilla que nunca aplasta el valor ------------------------- */
.prop-fields { margin-top: var(--s3); }
.prop-field {
  display: grid;
  grid-template-columns: minmax(64px, max-content) 1fr;
  gap: 4px 14px;
  align-items: baseline;
  padding: 6px 0;
}
.prop-field + .prop-field { border-top: 1px solid color-mix(in srgb, var(--line) 62%, transparent); }
.prop-field span { min-width: 0; font-size: var(--t-meta); color: var(--ink-muted); }
.prop-field b {
  min-width: 0;
  text-align: right;
  font-size: var(--t-body);
  font-weight: 500;
  overflow-wrap: anywhere;
  word-break: normal;
}
/* 1.22.0 — El importe apenas se distinguía de "Cada mes" o "5 sep". En una
   tarjeta de dinero es EL dato: el que decide si confirmas o no. */
.prop-field b.strong { font-size: 21px; font-weight: 700; letter-spacing: -.01em; }
@media (max-width: 380px) {
  .prop-field { grid-template-columns: 1fr; gap: 1px; padding: 6px 0; }
  .prop-field b { text-align: left; }
}

/* --- Pie: Confirmar carga el tono, Descartar es silencioso -------------- */
.prop-actions { margin-top: var(--s4); gap: var(--s2); }
.prop-actions .btn-secondary { flex: 0 1 auto; }
.prop-actions .btn-primary { flex: 1 1 auto; }

/* --- Guardando: la tarjeta lo dice, no solo el botón -------------------- */
.proposal.is-saving { position: relative; }
.proposal.is-saving .prop-actions .btn { opacity: .55; pointer-events: none; }
.proposal.is-saving .prop-actions .btn-primary::after {
  content: "";
  display: inline-block;
  width: 12px; height: 12px;
  margin-left: 8px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--r-full);
  vertical-align: -2px;
  animation: sam-saving-spin .7s linear infinite;
}
@keyframes sam-saving-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .proposal.is-saving .prop-actions .btn-primary::after { animation: none; opacity: .6; }
}

/* --- Hecho y bloqueado conservan el borde: la tarjeta no "flota" -------- */
.proposal.done {
  border-color: color-mix(in srgb, var(--color-green, var(--ctx-income)) 34%, var(--line));
  box-shadow: var(--shadow-low);
}
.proposal.done .prop-done { color: color-mix(in srgb, var(--color-green, var(--ctx-income)) 70%, var(--ink)); font-weight: 600; }
.proposal.blocked { border-color: color-mix(in srgb, var(--ctx-atencion) 36%, var(--line)); }
.prop-blocked {
  margin-top: var(--s3);
  padding: 10px 12px;
  border-radius: var(--r-sm);
  border-left: 3px solid var(--ctx-atencion);
  background: var(--tint-atencion);
  color: var(--ink);
  font-size: var(--t-meta);
}

/* --- Slot-filling: opciones parejas, la pregunta con voz propia --------- */
.prop-need > p { font-size: var(--t-body); font-weight: 600; color: var(--ink-strong); }
.prop-account-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: var(--s2);
}
.account-choice {
  min-height: 40px;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- Briefings: cifras legibles, títulos que ceden con elipsis ---------- */
.sam-brief-hidden, .sam-brief-more { font-size: var(--t-meta); color: var(--ink-muted); }
.sam-brief-finance-strip span { font-size: 0.75rem; color: var(--ink-muted); }
.sam-brief-money-row > span:first-child,
.sam-brief-payable > span:first-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 2.1.2 — La elipsis va en el <b>/<small> (son bloques): text-overflow no se
   hereda y los títulos se cortaban a media letra, sin puntos suspensivos. */
.sam-brief-money-row > span:first-child > b,
.sam-brief-money-row > span:first-child > small,
.sam-brief-payable > span:first-child > b,
.sam-brief-payable > span:first-child > small { overflow: hidden; text-overflow: ellipsis; }

/* --- Burbujas: el error se distingue de una respuesta normal ------------ */
.msg-retry {
  display: inline-block;
  margin-top: 6px;
  padding: 6px 14px;
  border: 1px solid color-mix(in srgb, var(--ctx-atencion) 40%, var(--line));
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--ctx-atencion) 9%, var(--surface));
  font-weight: 600;
}

/* 1.9.6 — El buscador es un campo de la casa, no el default del navegador.
   type="search" arrastra apariencia de UA (borde doble, botón de borrar
   nativo, ancho mínimo); aquí se neutraliza. 16px evita el auto-zoom de iOS. */
#globalSearchInput {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  min-height: 48px;
  padding: 12px 16px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
}
#globalSearchInput::placeholder { color: var(--ink-faint); }
#globalSearchInput:focus {
  outline: none;
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-ring) 16%, transparent);
}
#globalSearchInput::-webkit-search-cancel-button,
#globalSearchInput::-webkit-search-decoration { -webkit-appearance: none; display: none; }
#globalSearchResults { margin-top: var(--s3); }
#globalSearchResults .sheet-item small { display: block; opacity: .7; }
/* 2.1.2 — Buscar: el estado iba pegado al título, sin hueco («Escuela
   pólizaBandeja», medido: 0 px en las filas de un renglón), «En curso» se
   partía en dos y una palabra larga del título sacaba el texto 217 px de su
   fila y dejaba la hoja con scroll horizontal. Ahora hay un hueco fijo entre
   los dos, el estado va alineado a la derecha y no se parte, y el título
   parte la palabra que no cabe, el mismo criterio que .sheet-title. El título
   se reserva el 55 % de la fila: si el estado no cabe en el resto (el de un
   movimiento, «Gasto · $120,324.60 · 30 de ago»), baja a su propio renglón
   en vez de dejar el título en una columna de seis renglones. El relleno
   vertical sólo se nota en las filas de varios renglones (el texto quedaba a
   2 px del borde); las de un renglón siguen midiendo 44 px. */
#globalSearchResults .sheet-item { flex-wrap: wrap; column-gap: var(--s3); row-gap: 2px; padding-block: var(--s2); }
#globalSearchResults .search-result-title { flex: 1 1 55%; min-width: 0; overflow-wrap: anywhere; }
#globalSearchResults .sheet-item small { flex: 0 1 auto; min-width: 0; margin-left: auto; text-align: right; overflow-wrap: anywhere; }

/* 1.10 — Panel de deudas en la rejilla de Finanzas + subtítulo del sheet. */
@media (min-width: 900px) {
  #screen-finance .finance-debts-card { grid-column: span 12; }
}
@media (min-width: 900px) and (max-width: 1179px) {
  #screen-finance .finance-debts-card { grid-column: span 6; }
}
@media (max-width: 899px) {
  #screen-finance .finance-debts-card { grid-column: 1 / -1; }
}
.finance-sheet-subhead {
  margin: var(--s5) 0 var(--s2);
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink-strong);
}

/* 1.10 — Estado de sincronización y banner de guardado. */
#syncStatus {
  display: none;
  align-items: center;
  padding: 6px 12px;
  border-radius: var(--r-full);
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  border: 1px solid var(--line);
  background: var(--surface-sunken);
  color: var(--ink-muted);
}
#syncStatus[data-state="offline"],
#syncStatus[data-state="pending"] { display: inline-flex; }
#syncStatus[data-state="busy"] { display: inline-flex; color: var(--ink-muted); }
#syncStatus[data-state="error"] {
  display: inline-flex;
  border-color: color-mix(in srgb, var(--ctx-atencion) 42%, var(--line));
  background: var(--tint-atencion);
  color: var(--ctx-atencion);
}
/* 2.1.2 — Sin conexión, sincronizando o con error, la pastilla es rígida
   (nowrap) y en el teléfono no cabía junto al logotipo y los tres botones:
   a 320 px tapaba el logo entero desde «1 cambio sin subir» y, desde 12, sacaba
   el avatar de la pantalla (la página medía 321-342 px); a 360, «Error de
   sincronización» tapaba el logo. En teléfonos de hasta 419 px, mientras la
   pastilla está a la vista, el nombre «Samapp» cede su sitio (el logotipo se
   queda y ya no encoge) y la pastilla parte su texto en dos renglones si hace
   falta. Por debajo de 350 px «Sincronizando…» y «sincronización» no caben
   ni solas junto al logotipo: en esos dos estados cede también el logotipo,
   en vez de partir la palabra. Es pasajero; con todo sincronizado la
   pastilla no se ve y la barra queda igual que siempre. En un renglón la
   pastilla mide lo de siempre (min-height: renglón de 1.5 + 6+6 de relleno
   + 1+1 de borde = 32 px); en dos, 43 px, sin agrandar la barra. */
@media (max-width: 419px) {
  #syncStatus { min-width: 0; min-height: calc(1.5em + 2 * 6px + 2px); flex: 0 1 auto; white-space: normal; text-align: center; line-height: 1.2; overflow-wrap: anywhere; }
  .appbar:has(> #syncStatus:is([data-state="offline"], [data-state="pending"], [data-state="busy"], [data-state="error"])) > .appbar-brand { flex-shrink: 0; }
  .appbar:has(> #syncStatus:is([data-state="offline"], [data-state="pending"], [data-state="busy"], [data-state="error"])) > .appbar-brand > span:not(.sam-mark) { display: none; }
}
@media (max-width: 349px) {
  .appbar:has(> #syncStatus:is([data-state="busy"], [data-state="error"])) > .appbar-brand { display: none; }
}

#saveBanner {
  display: none;
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 90;
  padding: 12px 18px calc(12px + var(--safe-top));
  padding-top: calc(10px + var(--safe-top));
  background: var(--ctx-atencion);
  color: #fff;
  font-size: 0.875rem;
  font-weight: 600;
  text-align: center;
}
#saveBanner.show { display: block; }

/* 1.10.1 — Campana de Avisos. */
.inbox-bell { position: relative; }
.inbox-badge {
  position: absolute;
  top: -3px; right: -3px;
  min-width: 17px; height: 17px;
  padding: 0 4px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--ctx-atencion);
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
  border: 2px solid var(--paper);
}
.sidebar .inbox-bell .inbox-badge { top: 4px; right: auto; left: 26px; }
.inbox-item { display: flex; gap: 10px; align-items: flex-start; text-align: left; }
.inbox-item.is-read { opacity: .58; }
.inbox-icon { flex: 0 0 auto; font-size: 1.0625rem; line-height: 1.35; }
.inbox-copy { min-width: 0; flex: 1; }
.inbox-copy b { display: block; font-weight: 600; }
.inbox-copy small { display: block; color: var(--ink-muted); overflow: hidden; text-overflow: ellipsis; }
.inbox-time { margin-top: 2px; font-size: 0.6875rem; }

/* 1.10.4 — Botón de agendar del calendario + badge de Avisos del sidebar. */
/* El botón vive pegado a las pestañas Día/Semana/Mes, no flotando en medio
   del hueco del space-between (1.10.5). */
.calendar-screen-head { position: relative; }
.calendar-add-button { flex: 0 0 auto; align-self: center; order: 2; margin-left: auto; }
.calendar-screen-head .calendar-view-tabs { order: 3; margin-left: var(--s3); }
@media (max-width: 699px) {
  .calendar-add-button { position: absolute; top: 2px; right: 0; margin-left: 0; }
  /* 2.1.2 — Con la letra del sistema grande, «+ Agendar» tapaba la fecha (y a
     200 % el título). El bloque del título le deja su hueco; en rem, así
     crece con la letra igual que el botón. Al 100 % no cambia nada. */
  .calendar-screen-head > div:first-child { padding-right: 6.4rem; }
  /* 2.1.2 — En móvil las pestañas van a lo ancho de la columna: el margen de
     escritorio las corría 12 px a la derecha y las sacaba de la columna. */
  .calendar-screen-head .calendar-view-tabs { margin-left: 0; }
}
/* [hidden] no gana contra display:grid: sin esto el badge mostraba "0". */
.inbox-badge[hidden] { display: none !important; }
/* La bolita del sidebar quedaba encimada al icono: ahora vive a la derecha
   del renglón, como un contador de lista. */
.sidebar .inbox-bell { position: relative; }
.sidebar .inbox-bell .inbox-badge {
  position: static;
  margin-left: auto;
  border: 0;
}

/* ============================================================================
   SAMAPP 1.11 · CARRUSEL DE CUENTAS
   ----------------------------------------------------------------------------
   Tarjetas deslizables con imán. La física (inercia, rebote, snap) es la
   nativa del navegador — se siente como el sistema porque ES el sistema.
   Las vecinas asoman como naipes; la del frente se lee entera.
   ========================================================================== */
.account-carousel {
  display: flex;
  gap: var(--s3);
  overflow-x: auto;
  overflow-y: visible;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--s4);
  padding: var(--s2) var(--s4) var(--s5);
  margin-inline: calc(var(--s4) * -1);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.account-carousel::-webkit-scrollbar { display: none; }

.account-card {
  scroll-snap-align: center;
  flex: 0 0 auto;
  width: min(76%, 268px);
  min-height: 148px;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: var(--s4);
  border-radius: var(--r-lg);
  border: 1px solid color-mix(in srgb, var(--account-color) 34%, var(--line));
  background:
    linear-gradient(158deg,
      color-mix(in srgb, var(--account-color) 26%, var(--surface)) 0%,
      color-mix(in srgb, var(--account-color) 11%, var(--surface)) 62%,
      var(--surface) 100%);
  box-shadow: var(--shadow-card);
  color: var(--ink);
  text-align: left;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.account-card:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
.account-card:active { transform: scale(.985); }

.account-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s2); }
.account-card-icon {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--account-color) 62%, var(--surface));
  color: var(--ink-on-color);
  font-weight: 700;
  font-size: 0.9375rem;
}
.account-card-more { margin: -4px -4px 0 0; opacity: .75; }
.account-card-body { margin-top: auto; }
.account-card-name { font-size: 1rem; font-weight: 600; color: var(--ink-strong); }
.account-card-type { font-size: var(--t-meta); color: var(--ink-muted); }
/* 1.31.3 — Un saldo de diez cifras ($1,183,567,890.12) pedía 187px en una
   tarjeta de 165 y se salía del borde redondeado a 320px. El tamaño se ata al
   ancho de la ventana: a 390 y más sigue siendo 25px, y sólo encoge en las
   pantallas donde de verdad no cabe. */
.account-card-balance {
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: clamp(19px, 6.4vw, 25px);
  font-weight: 700;
  letter-spacing: -.015em;
  font-variant-numeric: tabular-nums;
  color: var(--ink-strong);
}
.account-card-balance[data-kind="negative"],
.account-card-balance[data-kind="debt"] { color: var(--finance-negative-ink, var(--ctx-atencion)); }

/* La tarjeta de alta cierra el carrusel: siempre hay un siguiente paso. */
.account-card-add {
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  width: min(46%, 150px);
  border-style: dashed;
  border-color: var(--line-strong);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.account-card-add-mark { font-size: 22px; line-height: 1; }
.account-card-add:hover { border-color: var(--ctx-finanzas); color: var(--ink); }

/* Naipes: las tarjetas fuera del centro se hunden un poco. Donde el navegador
   soporta timeline de scroll, la transición es continua con el dedo; donde no,
   simplemente se quedan planas — nada se rompe. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .account-card {
      animation: account-card-peek linear both;
      animation-timeline: view(inline);
      animation-range: entry 8% exit 92%;
    }
    @keyframes account-card-peek {
      0%   { transform: scale(.9) translateY(8px); opacity: .55; }
      42%, 58% { transform: scale(1) translateY(0); opacity: 1; }
      100% { transform: scale(.9) translateY(8px); opacity: .55; }
    }
  }
}

/* En escritorio no hay gesto táctil: las cuentas caben en rejilla y el
   carrusel deja de tener sentido. */
@media (min-width: 900px) {
  .account-carousel {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
    overflow: visible;
    margin-inline: 0;
    padding: var(--s2) 0 0;
  }
  .account-card { width: auto; min-height: 138px; }
  .account-card-add { width: auto; }
  @supports (animation-timeline: view()) { .account-card { animation: none; } }
}

/* 1.11 — Fichas de ruta y categoría en el historial de movimientos. */
.finance-tx-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.tx-route { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.tx-route-arrow { color: var(--ink-faint); font-size: 0.6875rem; }
.tx-chip {
  display: inline-block;
  max-width: 12ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 1px 8px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--account-color) 18%, var(--surface-sunken));
  border: 1px solid color-mix(in srgb, var(--account-color) 30%, transparent);
  color: var(--ink);
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.5;
}
[data-color-mode="sober"] .tx-chip {
  background: var(--surface-sunken);
  border-color: var(--line);
  color: var(--ink-muted);
}
.tx-meta-date { color: var(--ink-muted); font-size: var(--t-meta); white-space: nowrap; }
@media (max-width: 380px) { .tx-chip { max-width: 9ch; } }

/* ============================================================================
   SAMAPP 1.11 · CAPTURA DE MOVIMIENTOS — hoja única con teclado propio
   ========================================================================== */

/* Cuerpo scrolleable, teclado anclado abajo (mismo patrón que el alta de
   pendientes, activado por :has sobre la clase del formulario). */
.sheet:has(.tx-capture) { overflow: hidden; display: flex; flex-direction: column; }
.sheet:has(.tx-capture) .sheet-body { min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.sheet:has(.tx-capture) .sheet-foot { flex: 0 0 auto; }
@media (max-width: 899px) { .sheet:has(.tx-capture) { max-height: 92dvh; } }

/* --- Pestañas de tipo ---------------------------------------------------- */
.tx-type-tabs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  margin: 0 0 var(--s2);
  padding: 4px;
  border: 0;
  border-radius: var(--r-full);
  background: var(--surface-sunken);
}
.tx-type-tabs label { min-width: 0; cursor: pointer; }
.tx-type-tabs input { position: absolute; inline-size: 1px; block-size: 1px; opacity: 0; pointer-events: none; }
.tx-type-tabs span {
  display: flex; align-items: center; justify-content: center;
  min-height: 38px;
  border-radius: var(--r-full);
  color: var(--ink-muted);
  font-size: 0.875rem; font-weight: 600;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.tx-type-tabs input:checked + span { background: var(--surface); color: var(--ink-strong); box-shadow: var(--shadow-low); }
.tx-type-tabs input:focus-visible + span { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* --- Importe: la pantalla de la calculadora ------------------------------ */
.tx-amount { text-align: center; padding: var(--s3) 0 var(--s2); }
.tx-amount-row { display: flex; align-items: baseline; justify-content: center; gap: 4px; }
.tx-amount-currency { font-size: 22px; font-weight: 600; color: var(--ink-muted); }
.tx-amount-value {
  font-size: clamp(38px, 12vw, 52px);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1;
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.tx-amount-expr { min-height: 18px; color: var(--ink-faint); font-size: var(--t-meta); font-variant-numeric: tabular-nums; }

/* Color del importe según el tipo elegido: verde entra, coral sale. */
.tx-capture:has(input[name="txType"][value="income"]:checked) .tx-amount-value { color: var(--color-green, var(--ctx-income)); }
.tx-capture:has(input[name="txType"][value="expense"]:checked) .tx-amount-value { color: var(--color-coral, var(--ctx-expense)); }

/* --- Fichas de cuenta y categoría ---------------------------------------- */
.tx-picker { display: flex; flex-direction: column; gap: var(--s2); }
.tx-picker-label { font-size: 0.8125rem; font-weight: 600; color: var(--ink-muted); }
.tx-chip-grid { display: flex; flex-wrap: wrap; gap: var(--s2); }
.tx-chip-option { min-width: 0; cursor: pointer; }
.tx-chip-option input { position: absolute; inline-size: 1px; block-size: 1px; opacity: 0; pointer-events: none; }
.tx-chip-option span {
  display: inline-flex; align-items: center;
  min-height: 40px;
  padding: 0 14px;
  border-radius: var(--r-full);
  border: 1px solid color-mix(in srgb, var(--chip-color) 30%, var(--line));
  background: color-mix(in srgb, var(--chip-color) 8%, var(--surface));
  color: var(--ink);
  font-size: 0.875rem; font-weight: 500;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.tx-chip-option input:checked + span {
  background: color-mix(in srgb, var(--chip-color) 78%, var(--surface));
  border-color: transparent;
  color: var(--ink-on-color);
  font-weight: 600;
}
.tx-chip-option input:focus-visible + span { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.tx-chip-option span:active { transform: scale(.97); }
[data-color-mode="sober"] .tx-chip-option span { background: var(--surface); border-color: var(--line-strong); }
[data-color-mode="sober"] .tx-chip-option input:checked + span { background: var(--ink-strong); color: var(--paper); }

/* --- Campos que dependen del tipo: los conmuta el CSS, sin recargar ------ */
.tx-only-transfer, .tx-only-expense, .tx-only-income { display: none; }
.tx-capture:has(input[name="txType"][value="transfer"]:checked) .tx-only-transfer { display: flex; }
.tx-capture:has(input[name="txType"][value="expense"]:checked) .tx-only-expense { display: flex; }
.tx-capture:has(input[name="txType"][value="income"]:checked) .tx-only-income { display: flex; }
.tx-capture:has(input[name="txType"][value="transfer"]:checked) .tx-not-transfer { display: none; }

.tx-date .manual-task-date-options { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tx-custom-date { display: none !important; margin-top: var(--s3); }
.tx-capture:has(input[name="datePreset"][value="custom"]:checked) .tx-custom-date { display: flex !important; }

/* --- Teclado ------------------------------------------------------------- */
.tx-foot { display: flex; flex-direction: column; gap: var(--s3); width: 100%; }
.tx-keypad {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
}
.tx-key {
  min-height: 50px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--ink-strong);
  font: inherit;
  font-size: 20px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background var(--dur) var(--ease), transform .06s var(--ease);
}
.tx-key:hover { background: var(--surface-hover); }
.tx-key:active { transform: scale(.95); background: var(--surface-hover); }
.tx-key.is-op { color: var(--ctx-finanzas); font-size: 22px; }
.tx-key.is-del { color: var(--ink-muted); }
.tx-key:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.tx-save { min-height: 50px; font-size: 1rem; font-weight: 600; }

@media (max-width: 380px) { .tx-key { min-height: 44px; font-size: 1.125rem; } }
@media (min-width: 900px) {
  .tx-keypad { max-width: 340px; margin-inline: auto; }
  .tx-key { min-height: 44px; }
}

/* ============================================================================
   SAMAPP 1.12 · ICONOS DE FINANZAS
   ----------------------------------------------------------------------------
   La clase .i no tenía regla global de tamaño/trazo (solo dentro del composer
   y los briefings de Sam): aquí se declara para todos los sitios donde ahora
   aparece un icono de categoría o cuenta.
   ========================================================================== */
.i { display: inline-flex; align-items: center; justify-content: center; }
.i svg { width: 1em; height: 1em; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

/* 2.1.2 — SÍMBOLOS DE TEXTO → SVG. Los ✓ › ‹ → ↗ ↔ ↻ que hacían de icono
   dependían de la fuente; ahora son iconos de esta misma familia. El icono
   mide 1em, pero la tinta de un «›» o un «✓» no ocupa lo mismo que la de su
   sustituto: cada forma lleva su caja para que el alto visual quede como el
   del glifo (medido en Chrome, canvas.measureText contra getBoundingClientRect).
   `middle` pone el centro del icono donde los glifos tenían el suyo (a media
   altura de la x), así que no cambia la alineación con el texto. */
.i.i-glifo { vertical-align: middle; }
.i.i-glifo svg { width: var(--glifo-caja, 1em); height: var(--glifo-caja, 1em); }
/* Márgenes negativos = el aire lateral que el trazo deja dentro de su caja
   24×24: el contenedor conserva el ancho que tenía con el glifo y el texto
   de al lado no se recorta más. El chevrón es chico (.72em) y a esa escala el
   trazo de 1.9 bajaba de 1 px: va con trazo fijo en px de pantalla. La
   palomita reemplaza un ✓ en negrita: trazo 2.4, como .tutorial-habit-check. */
.i.i-glifo-chevron { --glifo-caja: .72em; margin-left: -.26em; margin-right: -.2em; }
.i.i-glifo-chevron svg { stroke-width: 1.35; }
.i.i-glifo-chevron svg * { vector-effect: non-scaling-stroke; }
.i.i-glifo-check { --glifo-caja: 1.2em; margin-left: -.19em; margin-right: -.165em; }
.i.i-glifo-check svg { stroke-width: 2.4; }
.i.i-glifo-diagonal { --glifo-caja: 1.15em; margin-left: -.22em; margin-right: -.22em; }
/* «Ver todos ↗»: el espacio lo daba `.home-habits-all span` (4 px) + el
   espacio lateral del glifo; el trazo SVG ya trae su aire, basta 1 px. */
.home-habits-all > .i.i-glifo-diagonal { margin-left: 1px; }
.i.i-glifo-refrescar { --glifo-caja: .9em; }
.finance-tx-amount > .i.i-glifo { margin-right: .25em; }

/* --- Selector de icono ---------------------------------------------------- */
.icon-picker { margin: 0; padding: 0; border: 0; }
.icon-picker legend { margin-bottom: 8px; color: var(--ink-muted); font-size: 0.8125rem; font-weight: 600; }
.icon-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
  gap: 6px;
  max-height: 188px;
  overflow-y: auto;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  overscroll-behavior: contain;
}
.icon-choice { position: relative; cursor: pointer; }
.icon-choice input { position: absolute; inline-size: 1px; block-size: 1px; opacity: 0; pointer-events: none; }
.icon-choice span {
  display: grid; place-items: center;
  aspect-ratio: 1;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: var(--surface);
  color: var(--ink-muted);
  font-size: 21px;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.icon-choice:hover span { background: var(--surface-hover); color: var(--ink); }
.icon-choice input:checked + span {
  background: color-mix(in srgb, var(--ctx-finanzas) 20%, var(--surface));
  border-color: var(--ctx-finanzas);
  color: var(--ink-strong);
}
.icon-choice input:focus-visible + span { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* --- Iconos dentro de las fichas de captura ------------------------------ */
.tx-chip-option span { gap: 6px; }
.tx-chip-option span .i { font-size: 1rem; opacity: .9; }

/* --- Icono en tarjeta de cuenta y en filas -------------------------------- */
.account-card-icon .i { font-size: 1.1875rem; }
.finance-account-icon .i { font-size: 1.0625rem; }
.finance-tx-icon.is-category {
  color: var(--account-color, var(--ctx-finanzas));
  background: color-mix(in srgb, var(--account-color, var(--ctx-finanzas)) 15%, var(--surface-sunken));
}
.finance-tx-icon.is-category .i { font-size: 1rem; }
[data-color-mode="sober"] .finance-tx-icon.is-category { color: var(--ink-muted); background: var(--surface-sunken); }

/* --- Lista de categorías en Configuración -------------------------------- */
.settings-cat-row {
  display: flex; align-items: center; gap: var(--s3);
  min-height: 48px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--line);
}
.settings-cat-row:last-of-type { border-bottom: 0; }
.settings-cat-icon {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  flex: 0 0 auto;
  border-radius: var(--r-full);
  font-size: 1.0625rem;
  color: var(--account-color, var(--ctx-finanzas));
  background: color-mix(in srgb, var(--account-color, var(--ctx-finanzas)) 15%, var(--surface-sunken));
}
[data-color-mode="sober"] .settings-cat-icon { color: var(--ink-muted); background: var(--surface-sunken); }
.settings-cat-body { flex: 1; min-width: 0; }
.settings-cat-body b { display: block; font-size: var(--t-body); font-weight: 500; }
/* 2.1.2 — C2: con letra al 200 % los nombres de categoría se metían debajo
   del «•••» (21 de 59 a 320). Parten dentro de su columna. */
.settings-cat-body b { overflow-wrap: anywhere; }
.settings-cat-body small { color: var(--ink-muted); font-size: var(--t-meta); }

/* 1.13 — Selector de moneda en Configuración. */
/* 2.1.1 — Sin `min-width: 0` un flex item no encoge por debajo de su
   contenido, y con la letra al 200% el desplegable de Moneda sacaba la fila
   entera del ancho de la pantalla (medido: right=388 en un viewport de 320). */
.settings-currency { min-width: 0; max-width: 100%; }
.settings-currency select {
  /* 2.1.1 — 40px medidos en Moneda (199x40) e Idioma (210x40). El token existe
     desde siempre; aquí no se estaba aplicando. Y `max-width` para que con la
     letra grande el desplegable no se salga de la fila. */
  min-height: var(--tap);
  max-width: 100%;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
  cursor: pointer;
}

/* ============================================================================
   SAMAPP 1.13 · CAPTURA COMPACTA — que todo quepa sin deslizar
   ----------------------------------------------------------------------------
   Medición previa: el cuerpo pedía 782px con solo 396px disponibles (con las
   15 categorías sembradas, la rejilla de fichas sola ocupaba 307px). Tres
   cambios: fichas en una fila con desplazamiento lateral (altura fija pase lo
   que pase), fecha compacta, y teclado con Guardar como columna.
   ========================================================================== */

/* --- Fichas: una sola fila, se deslizan de lado ------------------------- */
.tx-chip-grid {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--s2);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  padding-bottom: 2px;
  scroll-padding-inline: 4px;
  -webkit-overflow-scrolling: touch;
}
.tx-chip-grid::-webkit-scrollbar { display: none; }
/* Sin esto las fichas se comprimen unas sobre otras dentro del flex nowrap. */
.tx-chip-option { flex: 0 0 auto; }
.tx-chip-option span { white-space: nowrap; }
.tx-picker { gap: 6px; }

/* Ficha para crear una categoría al vuelo. */
.tx-chip-new {
  flex: 0 0 auto;
  min-height: 40px;
  padding: 0 14px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}
.tx-chip-new:hover { border-color: var(--ctx-finanzas); color: var(--ink); }

/* --- Importe más contenido ---------------------------------------------- */
.tx-amount { padding: 6px 0 2px; }
.tx-amount-value { font-size: clamp(34px, 10vw, 44px); }
.tx-amount-expr { min-height: 0; }
.tx-amount-expr:empty { display: none; }

/* --- Fecha en una línea -------------------------------------------------- */
.tx-date-row { display: flex; flex-direction: column; gap: var(--s2); }
.tx-date-options { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.tx-date-options span { min-height: 38px; }
input.tx-custom-date {
  display: none !important;
  min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--ink);
  font: inherit;
}
.tx-capture:has(input[name="datePreset"][value="custom"]:checked) input.tx-custom-date { display: block !important; }

/* --- Teclado: Guardar es una columna, no una fila extra ------------------ */
.tx-keypad {
  /* Las filas se declaran: sin esto `grid-row: 1 / -1` en Guardar no tiene
     contra qué abarcar y el teclado se desordena. */
  grid-template-columns: repeat(4, minmax(0, 1fr)) 82px;
  grid-template-rows: repeat(4, minmax(var(--tap), auto));
  gap: 6px;
}
.tx-key { min-height: var(--tap); font-size: 1.1875rem; }
.tx-save {
  grid-column: 5;
  grid-row: 1 / -1;
  min-height: 0;
  height: 100%;
  font-size: 0.9375rem;
  font-weight: 600;
  padding: 0 8px;
}
.tx-foot { gap: 0; }

@media (max-width: 360px) {
  .tx-keypad { grid-template-columns: repeat(4, minmax(0, 1fr)) 70px; }
  .tx-key { min-height: 40px; font-size: 1.0625rem; }
}
@media (min-width: 900px) {
  .tx-keypad { max-width: 420px; margin-inline: auto; }
}

/* 1.13.1 — Solo las más usadas a la vista; el resto se despliega con "+N".
   Idea de Ricardo: la hoja no debe llenarse de fichas. */
.tx-more-toggle { position: absolute; inline-size: 1px; block-size: 1px; opacity: 0; pointer-events: none; }
.tx-chip-more {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 40px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: 0.8125rem; font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.tx-chip-more:hover { border-color: var(--ctx-finanzas); color: var(--ink); }
.tx-chip-more::after { content: " ⌄"; }
.tx-chip-rest {
  display: none;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s2);
  max-height: 132px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.tx-picker:has(.tx-more-toggle:checked) .tx-chip-rest { display: flex; }
.tx-picker:has(.tx-more-toggle:checked) .tx-chip-more::after { content: " ⌃"; }
.tx-picker:has(.tx-more-toggle:checked) .tx-chip-more {
  background: var(--surface);
  border-color: var(--ctx-finanzas);
  color: var(--ink);
}


/* ============================================================================
   SAMAPP 1.16 · RED DE CONTACTOS
   ----------------------------------------------------------------------------
   Todo lo de aqui se apoya SOLO en tokens (--surface, --ink, --line,
   --ctx-atencion, --r-*), que existen en los cuatro cruces de tema x estilo.
   No hay ningun hex suelto: si manana cambia una paleta, esto la sigue.

   Contraste: los contadores van con --ink-on-color sobre --ctx-atencion y no
   con blanco. Blanco sobre el rojo de la paleta da 3.2:1 en claro y 2.6:1 en
   oscuro — suficiente para una insignia decorativa, insuficiente para un
   numero que hay que LEER. --ink-on-color lo sube por encima de 4.5:1 en los
   dos temas.
   ========================================================================== */

/* ------------------------------------------------------------- Contadores */
.people-badge {
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--ctx-atencion);
  color: var(--ink-on-color);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
  flex: 0 0 auto;
}
/* [hidden] no gana contra display:inline-grid — mismo tropiezo que el badge
   de la campana en 1.10.4. */
.people-badge[hidden] { display: none !important; }

.sidebar .nav-people { position: relative; }
.sidebar .nav-people-badge { margin-left: auto; }

/* --------------------------------------------- Acceso desde Pendientes */
.task-people-button { flex: 0 0 auto; gap: 6px; margin-left: auto; }
.task-people-button svg {
  width: 16px; height: 16px;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.task-people-button .people-badge { margin-left: 1px; }

/* En movil el encabezado ya lleva titulo + "Pendiente". El de Personas se
   queda en icono: el aria-label sigue diciendo que es, y el contador sigue
   viendose, que es lo que tiene que llamar. */
@media (max-width: 699px) {
  .task-people-label { display: none; }
  .task-people-button {
    /* 2.1.1 — era 42x44: el botón de Personas se queda en icono en móvil. */
    width: var(--tap); min-width: var(--tap);
    padding-inline: 0;
    justify-content: center;
    position: relative;
  }
  .task-people-button .people-badge {
    position: absolute;
    top: -5px; right: -5px;
    margin-left: 0;
    border: 2px solid var(--paper);
  }
}

.setting-action-people > span {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

/* ------------------------------------------------------ Pantalla Personas */
.people-net-note {
  margin: 0 0 var(--s3);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--ink);
  font-size: 0.8125rem;
  line-height: 1.5;
}
/* El fallo de red se marca en el filete, no en el texto: el rojo de la paleta
   no llega a 4.5:1 como tinta. */
.people-net-note[data-tone="error"] { border-left-color: var(--ctx-atencion); }

.people-block { margin-bottom: var(--s5); }
.people-block:last-child { margin-bottom: 0; }

.people-block-title {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: 0 0 var(--s2);
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--ink-strong);
}
.people-count {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  display: inline-grid;
  place-items: center;
  border-radius: var(--r-full);
  background: var(--ctx-atencion);
  color: var(--ink-on-color);
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
}
.people-block-hint {
  margin: -2px 0 var(--s2);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--ink-muted);
}

/* El bloque de solicitudes es el unico que se tine. Es lo unico que pide una
   respuesta: si todo llevara color, el color dejaria de decir nada. */
.people-block-alert {
  padding: var(--s3);
  border: 1px solid color-mix(in srgb, var(--ctx-atencion) 34%, var(--line));
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--ctx-atencion) 7%, var(--surface));
}

/* La fila envuelve SIEMPRE, no sólo en móvil. Con tres botones al lado, el
   nombre se comía a sí mismo en puntos suspensivos incluso en escritorio
   ("Pl…", "bet…"): la base de 180px le garantiza sitio y manda los botones a
   su propio renglón antes de dejar el nombre ilegible. */
.people-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s2) var(--s3);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--line);
}
.people-row:last-child { border-bottom: 0; }
.people-row-copy { flex: 1 1 150px; min-width: 0; }
.people-row-copy b {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink-strong);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.people-row-copy small {
  display: block;
  font-size: 0.8125rem;
  color: var(--ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.people-row-actions {
  display: flex;
  flex: 0 1 auto;
  flex-wrap: wrap;
  justify-content: flex-end;
  margin-left: auto;
  gap: var(--s2);
}

.people-local-add {
  width: 100%;
  min-height: 44px;
  margin-top: var(--s3);
  padding: 0 var(--s3);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: 0.8125rem;
  cursor: pointer;
}
.people-local-add:hover { background: var(--surface-hover); color: var(--ink); }
.people-local-add:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* En movil los botones bajan a su propio renglon a lo ancho: dos botones
   apretados contra el borde derecho no se aciertan con el pulgar. */
@media (max-width: 520px) {
  .people-row {
    flex-wrap: wrap;
    padding: var(--s3) 0;
  }
  .people-row-actions {
    width: 100%;
    justify-content: stretch;
  }
  .people-row-actions .btn {
    flex: 1 1 0;
    min-height: 44px;
  }
}

/* --------------------------------------------------- Agregar por correo */
.people-add {
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
}
.people-add-form {
  display: flex;
  gap: var(--s2);
  align-items: stretch;
}
.people-add-form input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 46px;
  padding: 0 var(--s3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.9375rem;
}
.people-add-form input::placeholder { color: var(--ink-faint); }
.people-add-form input:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
}
.people-add-form .btn { flex: 0 0 auto; min-height: 46px; }
.people-add-hint {
  display: block;
  margin-top: var(--s2);
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--ink-muted);
}

/* El resultado NUNCA va en rojo puro sobre la superficie: el --ctx-atencion
   de la paleta da 3.2:1 como texto y no llega a AA. El color va al borde,
   que no hay que leer; el texto se queda en --ink. */
.people-result {
  margin: var(--s3) 0 0;
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--r-xs);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.8125rem;
  line-height: 1.5;
}
.people-result[data-tone="error"] { border-left-color: var(--ctx-atencion); }
.people-result[data-tone="info"]  { border-left-color: var(--ctx-calendario); }
.people-result strong { font-weight: 700; word-break: break-all; }

@media (max-width: 420px) {
  .people-add-form { flex-wrap: wrap; }
  .people-add-form input,
  .people-add-form .btn { width: 100%; flex: 1 1 100%; }
}

/* ---------------------------------------- Elegir con quien compartir */
.people-options-flat {
  max-height: none;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
  margin-bottom: var(--s4);
}
.people-options-flat .people-option:last-child { border-bottom: 0; }

/* -------------------------------------------- Solicitud dentro de Avisos */
.inbox-item-contact { align-items: flex-start; cursor: default; }
.inbox-item-contact .person-avatar { margin-top: 1px; }
.inbox-actions {
  display: flex;
  gap: var(--s2);
  margin-top: var(--s2);
}
.inbox-actions .btn { flex: 1 1 0; min-height: 40px; }

/* Vacios: `.empty-mini` se usaba desde 1.6 sin una sola regla propia, asi que
   caia como parrafo suelto. Ahora es una superficie tranquila que se
   distingue del contenido real sin gritar. */
.empty-mini {
  padding: var(--s3);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.5;
}
.empty-mini.danger-text { color: var(--ink); border-left: 3px solid var(--ctx-atencion); }

/* ==========================================================================
   1.16 · DIRECCION DE LA ASIGNACION EN LA FILA
   --------------------------------------------------------------------------
   El avatar dice QUIEN es la otra persona; esta marca dice EN QUE SENTIDO.
   La flecha a la izquierda es trabajo que entro (te lo pasaron), la flecha a
   la derecha es trabajo que salio (lo estas esperando). Las dos son
   caracteres basicos, no emoji: se dibujan con la fuente del sistema en
   cualquier telefono.

   La frase completa vive en el aria-label que compone ui.js ("Asignada a ti
   por Jenny" / "Asignada a Jenny"), asi que esto es refuerzo visual y no la
   unica via a la informacion.
   ======================================================================== */
.row.assign-in .row-assignee,
.row.assign-out .row-assignee { position: relative; overflow: visible; }

.row.assign-in .row-assignee::after,
.row.assign-out .row-assignee::after {
  position: absolute;
  right: -4px;
  bottom: -4px;
  width: 15px;
  height: 15px;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}
.row.assign-in  .row-assignee::after { content: "\2190"; }
.row.assign-out .row-assignee::after { content: "\2192"; }

/* Lo que te pasaron a ti pesa mas que lo que estas esperando: el aro solido
   es para lo primero, el punteado para lo segundo. */
.row.assign-in .row-assignee .row-person-avatar {
  border-color: color-mix(in srgb, var(--ink) 38%, var(--line));
}
.row.assign-out .row-assignee .row-person-avatar {
  border-style: dashed;
}

/* ============================================================================
   1.16.2 · AVISOS — iconos dibujados y renglones cuadrados
   ----------------------------------------------------------------------------
   Ricardo: "los emojis se ven feos y todo mal cuadrado de espacios".
   Los emojis ya salieron de app.js; aquí se ajusta la caja que los sostenía.
   `.inbox-icon` estaba pensada para un glifo de texto (font-size + line-height)
   y con un SVG eso no alinea nada: hay que darle medida propia y centrarlo.
   ========================================================================== */
.inbox-icon {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font-size: 0;                 /* mata el hueco de línea del glifo anterior */
}
.inbox-icon .i { font-size: 1rem; }
/* Cada tipo de aviso con su color, pero sólo en el icono: el texto se queda
   en tinta normal, que es la regla de color que sigue toda la app. */
[data-color-mode="color"] .inbox-item[data-kind="assigned"] .inbox-icon { color: var(--ctx-calendario); }
[data-color-mode="color"] .inbox-item[data-kind="contact"] .inbox-icon { color: var(--ctx-personal); }
[data-color-mode="color"] .inbox-item[data-kind="due"] .inbox-icon { color: var(--ctx-atencion); }

/* Renglones parejos: el alto lo manda el icono, no el texto, y los tres
   bloques de texto llevan la misma separación entre sí. */
.inbox-item { gap: var(--s3); align-items: flex-start; padding: 10px 12px; min-height: 0; }
.inbox-copy { display: flex; flex-direction: column; gap: 2px; }
.inbox-copy b { line-height: 1.35; }
/* 2.1.2 — Avisos: el título es texto del usuario. Una palabra larga sin
   espacios salía 199 px de su fila a 360 y dejaba la hoja con scroll
   horizontal. Se parte sólo la palabra que no cabe, como .sheet-title. */
.inbox-copy b { overflow-wrap: anywhere; }
.inbox-copy small { line-height: 1.35; }
.inbox-time { margin-top: 4px; font-size: 0.75rem; color: var(--ink-faint); }

/* ============================================================================
   1.16.4 · PUERTA DE ACCESO — legibilidad de los dos textos que importan
   ----------------------------------------------------------------------------
   Medido por la inspección, en tema claro (que es el de por defecto):
     .auth-error  2.00 (pastel) / 2.78 (sobrio)   ← el canal de los fallos
     .auth-note   2.42 / 2.50                      ← el único texto de confianza
   El mínimo es 4.5. O sea que el sitio donde la app avisa de un problema era
   lo menos legible de la pantalla, y la nota de privacidad —que además va a
   crecer cuando se añada el aviso de la IA— tampoco se leía.

   El rojo de la paleta no alcanza como TINTA sobre papel claro por más que se
   ajuste: se le da un fondo tenue y una tinta oscura del mismo matiz, que es
   como se resuelve una alerta legible sin gritar.
   ========================================================================== */
.auth-error:not(:empty) {
  color: var(--color-red-ink, #B1422F);
  background: color-mix(in srgb, var(--ctx-atencion) 12%, var(--tone-canvas, #fff));
  border-radius: var(--r-sm);
  padding: 8px 10px;
  font-size: 0.8125rem;
  line-height: 1.35;
  text-align: left;
}
.auth-note { color: var(--ink-muted); }

/* ========================================================================
   1.18.2 · SAMAPP PREMIUM / GOOGLE PLAY BILLING
   ====================================================================== */
/* 1.21.7 — Seis tokens que no existían en ninguna parte. Cada `var()` caía
   a su fallback, o a NADA cuando el fallback tampoco existía: aquí
   `--color-verde` y `--color-mint` no están definidos, así que la tarjeta de
   suscripción ACTIVA se quedaba sin tono mientras la inactiva sí lucía el
   suyo — la jerarquía justo al revés. Se usan los tokens reales del sistema,
   que además se adaptan solos al tema oscuro. */
/* 1.21.7 — Un verde que se LEA, que es distinto de un verde de acento. Los
   tonos verdes del sistema (--ctx-income, --color-green) están calculados para
   fondo oscuro: sobre la superficie blanca del tema claro dan 1.44:1, casi
   invisibles. El fallback fijo que había antes daba 3.80:1, tampoco suficiente.
   Con un valor por tema: 5.29:1 en claro y 6.14:1 en oscuro, ambos por encima
   del mínimo de 4.5:1 para texto. */
:root { --ok-ink: #1F7A55; }
[data-theme=dark] { --ok-ink: #78B58D; }
.billing-settings-card{--settings-tone:var(--color-indigo);}
.billing-card-active{--settings-tone:var(--ctx-income);}
.billing-status-pill{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:auto;max-width:none;min-height:28px;padding: 6px 10px;border:1px solid color-mix(in srgb,var(--settings-tone,var(--line-strong)) 24%,var(--line));border-radius:999px;background:color-mix(in srgb,var(--settings-tone,var(--surface-sunken)) 8%,var(--surface));color:var(--ink);font-size: 0.6875rem;font-weight:700;line-height:1.2;text-align:center;white-space:nowrap;}
.billing-sheet{display:grid;gap:18px;}
.billing-hero{display:flex;align-items:flex-start;gap:14px;padding:18px;border:1px solid var(--line);border-radius:16px;background:var(--surface-sunken);}
.billing-mark{width:42px;height:42px;flex:0 0 42px;color:var(--ink-strong);}
.billing-eyebrow{display:block;margin-bottom: 6px;color:var(--ink-muted);font-size: 0.625rem;font-weight:700;letter-spacing:.12em;}
.billing-hero h3{margin:0;color:var(--ink-strong);font-size: 1.1875rem;line-height:1.2;}
.billing-hero p{margin: 6px 0 0;color:var(--ink-muted);font-size: 0.8125rem;line-height:1.5;}
.billing-trial{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:14px 16px;border:1px solid color-mix(in srgb,var(--color-indigo,var(--line-strong)) 26%,var(--line));border-radius:14px;background:color-mix(in srgb,var(--color-indigo,var(--surface-sunken)) 8%,var(--surface));}
.billing-trial strong{font-size: 1rem;color:var(--ink-strong);}
.billing-trial span{font-size: 0.75rem;color:var(--ink-muted);text-align:right;}
.billing-benefits{display:grid;gap: 10px;margin:0;padding:0;list-style:none;}
.billing-benefits li{position:relative;padding-left:24px;color:var(--ink);font-size: 0.8125rem;line-height:1.5;}
.billing-benefits li::before{content:'✓';position:absolute;left:0;top:0;color:var(--ok-ink);font-weight:700;}
.billing-actions{display:grid;gap: 10px;}
.billing-error{padding: 10px 12px;border:1px solid color-mix(in srgb,var(--ctx-atencion) 35%,var(--line));border-radius:12px;background:color-mix(in srgb,var(--ctx-atencion) 8%,var(--surface));color:var(--ink);font-size: 0.75rem;line-height:1.5;}
.billing-notice{padding: 10px 12px;border:1px solid color-mix(in srgb,var(--ctx-income) 32%,var(--line));border-radius:12px;background:color-mix(in srgb,var(--ctx-income) 8%,var(--surface));color:var(--ink);font-size: 0.75rem;line-height:1.5;}
.billing-expiry{margin:0;padding:0 2px;color:var(--ink-muted);font-size: 0.75rem;}
.billing-fineprint{margin:0;color:var(--ink-faint,var(--ink-muted));font-size: 0.6875rem;line-height:1.5;}
.billing-gate{position:fixed;inset:0;z-index:100000;display:grid;place-items:center;padding:max(20px,var(--safe-top)) 20px max(20px,var(--safe-bottom));background:color-mix(in srgb,var(--surface) 94%,transparent);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);}
.billing-gate-card{width:min(420px,100%);padding:28px 22px;border:1px solid var(--line);border-radius:24px;background:var(--surface);box-shadow:0 24px 80px rgba(0,0,0,.18);text-align:center;}
.billing-gate-mark{display:block;width:54px;height:54px;margin:0 auto 14px;color:var(--ink-strong);}
.billing-gate-card h2{margin:4px 0 8px;color:var(--ink-strong);font-size:25px;}
.billing-gate-card>p{margin:0 auto 20px;max-width:330px;color:var(--ink-muted);font-size: 0.8125rem;line-height:1.5;}
.billing-gate-card>.btn{margin-top: 10px;}
.billing-gate-links{display:flex;flex-wrap:wrap;justify-content:center;gap:10px 16px;margin-top:18px;font-size: 0.6875rem;}
.billing-gate-links a,.billing-gate-links button{border:0;background:none;color:var(--ink-muted);font:inherit;text-decoration:underline;cursor:pointer;padding:0;}
html.billing-locked body{overflow:hidden;}
/* 1.22.5 — El modo solo lectura no tapa nada: informa. La app se ve y se
   navega entera; lo que se cierra es crear y editar. La barra se apoya sobre
   la barra de pestañas en móvil para no comerse contenido. */
.billing-gate-bar{position:fixed;inset:auto 0 0 0;z-index:60;display:block;padding:0;background:transparent;place-items:stretch;}
/* 2.1.2 — La barra es una franja fija a todo lo ancho que heredaba de
   .billing-gate el desenfoque (blur 14px) y recibía los toques. En escritorio
   y en la tableta horizontal la tarjeta mide 780 px y la franja, la ventana
   entera: «Configuración», al pie de la barra lateral, quedaba borroso y el
   toque lo recibía #billingGate (medido a 1024, 1280 y 1440). La franja ya
   no desenfoca ni atrapa toques; la tarjeta de la barra sigue igual y es la
   única que los recibe. La puerta que bloquea (.billing-gate sin -bar)
   conserva su desenfoque. */
.billing-gate-bar{backdrop-filter:none;-webkit-backdrop-filter:none;pointer-events:none;}
.billing-gate-bar .billing-bar-inner{pointer-events:auto;}
.billing-bar-inner{display:flex;align-items:center;gap:var(--s3);margin:0 auto;max-width:780px;
  padding:10px var(--s4);border:1px solid var(--line-strong);border-radius:var(--r-lg);
  background:var(--surface);box-shadow:0 8px 28px rgba(0,0,0,.16);}
.billing-bar-text{flex:1;min-width:0;font-size: 0.8125rem;color:var(--ink);}
.billing-bar-text strong{color:var(--ctx-atencion);}
/* 1.22.6 — El pie del chat cuando Sam está fuera de alcance: se dice antes de
   escribir, no después de pulsar enviar. */
.sam-locked{display:flex;align-items:center;gap:var(--s3);padding:12px var(--s4);
  border:1px solid var(--line-strong);border-radius:var(--r-lg);background:var(--surface);}
.sam-locked-text{flex:1;min-width:0;margin:0;display:flex;flex-direction:column;gap:2px;font-size: 0.8125rem;}
.sam-locked-text span{color:var(--ink-muted);font-size: 0.8125rem;}
/* 2.1.2 — La barra «Solo lectura» tapaba las cinco pestañas en tableta: el
   corte estaba en 700 px y la barra inferior sigue ahí hasta 900. Tampoco
   contaba el área segura ni el disco de Sam, que sobresale. Y como es fija,
   se comía el final de cada pantalla y el aviso quedaba debajo de ella. Su
   alto real lo publica ui.js en --billing-bar-h (cambia con el idioma, el
   ancho y el tamaño de letra); contenido y aviso le dejan ese sitio. */
html.billing-readonly .billing-gate-bar{margin-bottom:calc(var(--tabbar-h,64px) + var(--safe-bottom) + var(--s6));}
@media (min-width:900px){ html.billing-readonly .billing-gate-bar{margin-bottom:var(--s4);} }
html.billing-readonly .content{padding-bottom:calc(var(--tabbar-h) + var(--safe-bottom) + var(--s6) + var(--billing-bar-h,0px) + var(--s4));}
html.billing-readonly .toast{bottom:calc(var(--tabbar-h) + var(--safe-bottom) + var(--s6) + var(--billing-bar-h,0px) + var(--s2));}
@media (min-width:900px){
  html.billing-readonly .content{padding-bottom:calc(var(--s4) + var(--billing-bar-h,0px) + var(--s6));}
  html.billing-readonly .toast{bottom:calc(var(--s4) + var(--billing-bar-h,0px) + var(--s2));}
}
/* En Sam la pantalla mide lo que mide la ventana: con la barra encima, el
   saludo vacío (427 px medidos) no cabía y empujaba el pie del chat debajo de
   la barra. En solo lectura el saludo puede encogerse y se desplaza dentro;
   `safe` evita que el centrado lo saque por arriba, donde no se alcanza. */
html.billing-readonly .sam-hello{min-height:0;overflow-y:auto;justify-content:safe center;}
/* 2.1.2 — Con una hoja o un diálogo abiertos, el aviso quedaba DETRÁS del
   velo (60 contra 70): pulsabas «Guardar» con un dato mal y parecía que no
   pasaba nada. ui.js marca con .toast-arriba el aviso que nace con una capa
   abierta; sube arriba y por encima del velo, sin tapar el pie de la hoja.
   Va con #toast para ganarle a las reglas de solo lectura de arriba.
   Orden de capas: toast 60 < hojas 70 < toast con capa 80 < acceso 90. */
#toast.toast-arriba{z-index:80;top:calc(var(--safe-top) + var(--s3));bottom:auto;}
/* Lo que no se puede usar, se ve que no se puede usar. */
html.billing-readonly [data-act=new-task-ui],
html.billing-readonly [data-act=tx-capture-open]{opacity:.55;}
@media(max-width:699px){
  .billing-status-pill{max-width:none;padding: 6px 10px;font-size: 0.6875rem;}
  .billing-trial{align-items:flex-start;flex-direction:column;gap:4px;}
  .billing-trial span{text-align:left;}
}

/* 1.19.0 · i18n: el texto puede crecer sin romper geometría ni centrado. */
.settings-language select{min-width:150px;max-width:220px;text-align-last:center;}
.settings-language-row .settings-choice-copy{min-width:0;}
html[data-locale="en-US"] .nav-link span,html[data-locale="en-US"] .tab span{letter-spacing:-.01em;}
html[data-locale="en-US"] .settings-choice-row{align-items:center;}
html[data-locale="en-US"] .btn,html[data-locale="en-US"] .chip,html[data-locale="en-US"] .billing-status-pill{overflow-wrap:normal;}
@media(max-width:699px){.settings-language{width:min(100%,210px)}.settings-language select{width:100%;min-width:0}.settings-language-row{gap:12px}.settings-language-row .settings-choice-copy{flex:1 1 auto}}
/* 2.1.2 — C1: entre 900 y 1179 px la columna de Configuración mide 326-450 px
   y las elecciones no caben en una fila: Moneda se leía «Peso me…» (101 px
   para un texto de 138) e Idioma salía 8 px de su tarjeta, que recorta. Se
   apilan como en móvil (la misma disposición que por debajo de 700 px). */
@media (min-width: 900px) and (max-width: 1179px) {
  .settings-choice-row { align-items: flex-start; flex-direction: column; gap: var(--s2); }
  .settings-language { width: min(100%, 210px); }
  .settings-language select { width: 100%; min-width: 0; }
}
/* 2.1.2 — C1 (inglés): cuando las elecciones van apiladas, la regla de 1.19
   `html[data-locale="en-US"] .settings-choice-row{align-items:center}` (misma
   que en la fila, donde sobra) las centraba: «Appearance», «Style», Moneda e
   Idioma quedaban a media tarjeta en inglés y a la izquierda en español. */
@media (max-width: 699px), (min-width: 900px) and (max-width: 1179px) {
  html[data-locale="en-US"] .settings-choice-row { align-items: flex-start; }
}

/* ============================================================================
   1.25 · WIDGET DE ANDROID — hoja de vinculación (código de 6 dígitos)
   ========================================================================= */
.widget-pair-code {
  margin: var(--s3) 0 var(--s2);
  text-align: center;
  font-size: 40px;
  font-weight: 600;
  letter-spacing: .16em;
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
}
.widget-pair-hint { margin: 0 0 var(--s3); font-size: 0.8125rem; color: var(--ink-muted); text-align: center; }
.widget-pair-steps { margin: var(--s3) 0; padding-left: 20px; font-size: 0.875rem; line-height: 1.5; color: var(--ink); }
.widget-pair-steps li + li { margin-top: 6px; }

/* ============================================================================
   1.26 — Pestañas de Inicio (móvil) · 1.31 — ahora "Mi día · Hábitos"
   ----------------------------------------------------------------------------
   En 1.26 la pareja era "Mi día · Finanzas"; en 1.31 se intercambian: Finanzas
   recupera su sitio en la barra inferior y Hábitos pasa a ser la pestaña de
   Inicio. La tira no cambia —dos mitades, filete inferior sutil y subrayado
   corto en la activa—, sólo su segundo destino.
   En escritorio manda el sidebar y las pestañas no se pintan.
   ========================================================================== */
.home-tabs {
  position: relative;   /* 1.32.1 — ancla del marcador; antes colgaba de .home-tab */
  display: grid; grid-template-columns: 1fr 1fr;
  margin: -2px 0 var(--s4);
  border-bottom: 1px solid var(--line);
}
.home-tab {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 44px; padding: 8px 4px 10px;
  border: 0; background: transparent; color: var(--ink-muted);
  font: inherit; font-size: 0.9375rem; font-weight: 500; letter-spacing: .005em;
  cursor: pointer;
}
.home-tab svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.home-tab[aria-selected="true"] { color: var(--ink-strong); font-weight: 600; }

/* ----------------------------------------------------------------------------
   1.32.1 · EL MARCADOR VIAJA CON EL DEDO
   El subrayado era un ::after de la pestaña activa: sabía estar en un sitio o
   en el otro, y nada más. Al deslizar entre Mi día y Hábitos hace falta que
   esté EN MEDIO mientras el dedo está en medio — es la única señal honesta de
   "esto va a cambiar, y si sueltas ahora no". Ahora es un elemento cuyo
   desplazamiento sale de --home-pos (0 = Mi día, 1 = Hábitos): el gesto la
   mueve en fracciones y al soltar la deja en un entero.
   La transición se apaga durante el arrastre (.is-dragging) porque ahí el
   movimiento ya lo da el dedo; interpolar encima produce el retardo elástico
   que hace que un gesto se sienta "pegajoso". */
/* El elemento ocupa UNA COLUMNA entera (50% de dos columnas iguales), así que
   translateX(100%) lo mueve exactamente una pestaña: sin números mágicos y sin
   depender del ancho de la pantalla. La barra visible de 56px va centrada
   dentro con flex, que es donde vive el aspecto. */
.home-tabs-ink {
  position: absolute; bottom: -1px; left: 0;
  width: 50%; height: 2.5px;
  display: flex; justify-content: center;
  transform: translateX(calc(var(--home-pos, 0) * 100%));
  transition: transform .26s var(--ease);
  pointer-events: none;
}
.home-tabs-ink::before {
  content: ""; width: 56px; height: 100%;
  border-radius: var(--r-full); background: var(--ink-strong);
}
.home-tabs.is-dragging .home-tabs-ink { transition: none; }

/* ----------------------------------------------------------------------------
   1.32.1 · EL CONTENIDO SIGUE AL DEDO, PERO AMORTIGUADO
   Por qué no 1:1: sólo hay una pantalla montada a la vez (`.screen` es
   display:none), así que arrastrar el ancho completo dejaría a la vista una
   franja vacía — se vería el truco. El contenido se mueve una fracción del
   recorrido y con tope; quien lleva la cuenta exacta de "cuánto llevas" es el
   marcador de la pestaña, que sí va 1:1 con el progreso. El resultado es que
   el gesto se siente enganchado sin enseñar el vacío.

   --home-dx lo escribe el gesto en píxeles ya amortiguados y con tope. */
.home-pane { will-change: auto; }
.home-pane.is-dragging {
  transform: translate3d(var(--home-dx, 0px), 0, 0);
  transition: none;
  will-change: transform;
}
.home-pane.is-settling {
  transform: translate3d(0, 0, 0);
  transition: transform .24s var(--ease);
}

/* La entrada de la pantalla que llega NO vive aquí: la escribe el gesto en
   estilo en línea (ui.js), porque arranca desde donde el dedo dejó el
   contenido y ese punto sólo se conoce al soltar. Una animación con @keyframes
   siempre empezaría desde el mismo sitio, y entonces el cambio se lee como un
   corte seguido de una animación en vez de como la continuación del gesto. */

/* Quien pide menos movimiento no recibe ninguno: el cambio de pestaña sigue
   funcionando igual, sólo que sin viaje. */
@media (prefers-reduced-motion: reduce) {
  .home-tabs-ink { transition: none; }
  .home-pane.is-dragging,
  .home-pane.is-settling { transform: none; transition: none; }
}
.home-tab:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; border-radius: var(--r-sm); }
@media (min-width: 900px) { .home-tabs { display: none; } }

/* ============================================================================
   1.26 — TARJETAS DE SAM
   ----------------------------------------------------------------------------
   Qué cambia y por qué:

   1. ICONO EN EL ENCABEZADO. Nueve clases de propuesta se distinguían sólo
      por una etiqueta en versalitas de 11px. Con dos tarjetas seguidas había
      que LEER para saber si era un gasto o un pendiente; el icono se
      reconoce antes que la palabra y no depende de distinguir colores.

   2. LA CIFRA SUBE. El importe vivía en la misma fila diminuta que
      "Categoría: Alimentos", y es justo el dato que decide si confirmas.
      Ahora va arriba a la derecha, en la tipografía de cifras.

   3. EL RECIBO LLEVA A ALGÚN SITIO. Tras confirmar, "Ver" abre la pantalla
      donde quedó guardado. Antes había que ir a buscarlo a mano.

   Todo sale de tokens.css: ni un color propio, y --proposal-tone ya existía.
   ========================================================================== */
.proposal[data-tone="habit"] { --proposal-tone: var(--ctx-finanzas); }

.prop-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s3);
}
.prop-icon {
  display: grid; place-items: center;
  width: 34px; height: 34px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--proposal-tone, var(--ink-faint)) 16%, var(--surface));
  color: color-mix(in srgb, var(--proposal-tone, var(--ink-muted)) 55%, var(--ink-strong));
}
.prop-icon .i { font-size: 1.125rem; }
/* align-items: la etiqueta es una píldora; sin esto se estira a todo el ancho
   de la columna y deja de leerse como etiqueta. */
.prop-head-copy { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
.prop-head-copy .prop-verb { max-width: 100%; }
.prop-headline {
  display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  flex: 0 0 auto; text-align: right;
}
.prop-headline small { font-size: 0.6875rem; letter-spacing: .03em; text-transform: uppercase; color: var(--ink-muted); }
.prop-headline b {
  font-family: var(--font-num);
  font-size: 1.1875rem; font-weight: 600; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--ink-strong); letter-spacing: -.01em;
}
.prop-title { overflow-wrap: anywhere; }

/* El recibo: lo guardado a la izquierda, a dónde ir a la derecha. */
.prop-done { justify-content: space-between; width: 100%; }
.prop-done-text { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.prop-done-go {
  display: inline-flex; align-items: center; gap: 2px;
  min-height: 32px; padding: 0 4px 0 10px;
  border: 0; background: transparent;
  color: var(--ctx-finanzas); font: inherit; font-size: 0.8125rem; font-weight: 600;
  cursor: pointer; flex: 0 0 auto;
}
.prop-done-go .i { font-size: 1rem; }
.prop-done-go:hover { text-decoration: underline; }
.prop-done-go:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: var(--r-sm); }

/* 2.1.2 — Tinta legible en el recibo y en Reintentar. El tono puro sobre su
   tinte daba 1.35:1 en «Ver», 2.5 en «Pendiente creado» y 2.0 en
   «Reintentar» (tema claro). Mezclado con la tinta fuerte, como .prop-icon,
   conserva el matiz y pasa de 5:1 en los cuatro temas. */
.proposal.done .prop-done, .prop-done-go { color: color-mix(in srgb, var(--ctx-finanzas) 40%, var(--ink-strong)); }
.msg-retry { color: color-mix(in srgb, var(--ctx-atencion) 40%, var(--ink-strong)); }
/* 2.1.2 — Objetivos táctiles de Sam a 44 px (--tap, 1.21.9). La palomita de
   hábitos crece sólo en área de toque (::after), no a la vista: el ::before
   sigue dibujando el círculo y la fila no cambia de ancho. También en tableta
   táctil horizontal (más de 899 px pero con el dedo). */
@media (max-width: 899px), (pointer: coarse) {
  .msg-action, .msg-retry, .prop-done-go { min-height: var(--tap); }
  .sam-hab-check::after { content: ""; position: absolute; inset: -5px; }
}

@media (max-width: 359px) {
  .prop-head { gap: var(--s2); }
  .prop-icon { width: 30px; height: 30px; }
  .prop-icon .i { font-size: 1rem; }
  .prop-headline b { font-size: 1.0625rem; }
}

/* ============================================================================
   2.1.2 — DETALLES DE LAS TARJETAS DE SAM (catálogo f7)
   ========================================================================== */
/* Encabezado con importe. Con la cifra en una tercera columna el verbo
   («REGISTRAR TRANSFERENCIA») se partía en dos renglones y el título se
   estrechaba a tres palabras por renglón. Texto y cifra van juntos en
   .prop-head-main, que sólo se parte cuando no caben a su ancho natural: el
   importe baja a su propio renglón (a la derecha, como un total) y verbo y
   título conservan el ancho. Si caben, se ve como antes. El icono vive en su
   propia columna: nunca se queda solo en un renglón. */
.prop-head { grid-template-columns: auto minmax(0, 1fr); }
.prop-head-main {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: var(--s3); row-gap: var(--s2);
  min-width: 0;
}
.prop-head-main .prop-head-copy { flex: 1 1 auto; }
.prop-head-main .prop-headline { margin-left: auto; }

/* Descartada: queda como recibo compacto y apagado — sin campos, sin
   botones, sin el tinte del tono. */
.proposal.discarded,
[data-color-mode="color"] .proposal.discarded[data-tone] {
  background: var(--surface-sunken);
  border-color: var(--line);
  box-shadow: none;
}
.proposal.discarded .prop-done { color: var(--ink-muted); font-weight: 600; }

/* Agenda: «11:30 a.m.» no cabe en 44 px (la hora lleva espacio duro) y el
   punto de la línea de tiempo tapaba su final. La columna mide lo que la
   hora más larga en 12 horas, en rem para seguir a la escala de letra. */
.sam-brief-event { grid-template-columns: 3.75rem 16px minmax(0, 1fr) 14px; }
.sam-brief-time { white-space: nowrap; }
@media (max-width: 520px) {
  .sam-brief-event { grid-template-columns: 3.75rem 14px minmax(0, 1fr) 12px; }
}

/* Todas las tarjetas de resumen a lo ancho del hilo, como ya hacía la de
   hábitos: con poco contenido (Pendientes de hoy vacío, Agenda de mañana)
   se ajustaban a su texto y medían 234–310 px contra 329 de las demás. */
.msg-group > .sam-brief { align-self: stretch; }

/* «No hay nada urgente ahora mismo» era el tono puro sobre su propio tinte
   (menta sobre menta). Misma mezcla que el recibo de «hecho». */
.sam-brief-calm { color: color-mix(in srgb, var(--ctx-finanzas) 40%, var(--ink-strong)); }

/* El aviso antes de borrar arma su texto con un campo por renglón («\n»),
   pero el diálogo lo juntaba todo en uno. */
.confirm-text { white-space: pre-line; }

/* ----------------------------------------------------------------------------
   Resumen de hábitos en el chat (show_summary type="habits")
   ----------------------------------------------------------------------------
   Reutiliza los tokens de habitos.css: mismo verde, mismos estados de punto.
   La palomita SÍ escribe (sam-summary-toggle-habit) y vuelve a capturar el
   resumen: una tarjeta que muestra un estado que ya no es cierto es peor que
   una tarjeta sin botón.
   -------------------------------------------------------------------------- */
.sam-hab-top { display: flex; align-items: center; gap: var(--s4); margin-top: var(--s3); }
.sam-hab-top-copy { min-width: 0; }
.sam-hab-top-copy > strong { display: block; font-size: 1rem; font-weight: 600; color: var(--ink-strong); }
.sam-hab-top-copy .sam-brief-pills { margin-top: 6px; }
.sam-hab-ring { position: relative; width: 62px; height: 62px; flex: 0 0 auto; }
.sam-hab-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.sam-hab-track { fill: none; stroke: var(--surface-sunken); stroke-width: 7; }
.sam-hab-fill {
  fill: none; stroke: var(--habit-green, var(--ctx-finanzas)); stroke-width: 7; stroke-linecap: round;
}
.sam-hab-ring b {
  position: absolute; inset: 0;
  /* flex en línea de base, no grid: con grid el "/4" caía a una segunda fila. */
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-num); font-size: 1.0625rem; font-weight: 600; color: var(--ink-strong);
  white-space: nowrap;
}
.sam-hab-ring b span { font-size: 0.75rem; font-weight: 500; color: var(--ink-muted); }

.sam-hab-list { margin-top: var(--s3); border-top: 1px solid var(--line); }
.sam-hab-row {
  display: grid; grid-template-columns: 30px minmax(0, 1fr) auto 34px;
  align-items: center; gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
.sam-hab-row:last-child { border-bottom: 0; }
.sam-hab-row.is-off { opacity: .62; }
.sam-hab-icon {
  display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-full);
  background: var(--surface-sunken); color: var(--ink);
}
.sam-hab-icon .i { font-size: 1rem; }
/* Variante grande: ocupa el hueco del anillo cuando hoy no toca ninguno. */
.sam-hab-icon-lg { width: 62px; height: 62px; background: color-mix(in srgb, var(--ctx-finanzas) 12%, var(--surface)); color: var(--ctx-finanzas); }
.sam-hab-icon-lg .i { font-size: 26px; }
.sam-hab-copy { min-width: 0; }
.sam-hab-copy b { display: block; font-size: 0.875rem; font-weight: 600; color: var(--ink-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sam-hab-copy small { display: block; font-size: 0.75rem; color: var(--ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sam-hab-dots { display: flex; gap: 4px; flex: 0 0 auto; }
.sam-hab-dots i { display: block; width: 7px; height: 7px; border-radius: var(--r-full); background: var(--ink-faint); }
.sam-hab-dots i[data-state="done"] { background: var(--habit-green, var(--ctx-finanzas)); }
.sam-hab-dots i[data-state="off"] { background: var(--habit-green-soft, var(--surface-sunken)); }
.sam-hab-dots i[data-state="missed"] { background: var(--ink-faint); }
.sam-hab-dots i[data-state="today"] { background: transparent; box-shadow: inset 0 0 0 1.5px var(--ink-faint); }
.sam-hab-dots i[data-state="future"] { background: var(--surface-sunken); }

.sam-hab-check {
  position: relative;
  width: 34px; height: 34px; margin-right: -2px;
  padding: 0; border: 0; background: transparent; cursor: pointer;
  display: grid; place-items: center; border-radius: var(--r-full);
}
.sam-hab-check::before {
  content: ""; position: absolute; width: 26px; height: 26px;
  border-radius: var(--r-full);
  border: 1.5px solid var(--ink-faint); background: var(--surface);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.sam-hab-check svg { position: relative; width: 14px; height: 14px; fill: none; stroke: transparent; stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.sam-hab-check[aria-pressed="true"]::before { background: var(--habit-green, var(--ctx-finanzas)); border-color: var(--habit-green, var(--ctx-finanzas)); }
.sam-hab-check[aria-pressed="true"] svg { stroke: var(--habit-on-green, #fff); }
.sam-hab-check:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.sam-hab-check.is-empty { cursor: default; }

.sam-hab-week {
  display: flex; justify-content: space-between;
  margin-top: var(--s3); padding-top: var(--s3);
  border-top: 1px solid var(--line);
}
.sam-hab-week span { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.sam-hab-week i { display: block; width: 9px; height: 9px; border-radius: var(--r-full); background: var(--ink-faint); }
.sam-hab-week i[data-state="full"] { background: var(--habit-green, var(--ctx-finanzas)); }
.sam-hab-week i[data-state="partial"] { background: var(--habit-green-soft, var(--surface-sunken)); }
.sam-hab-week i[data-state="none"] { background: var(--ink-faint); }
.sam-hab-week i[data-state="today"] { background: transparent; box-shadow: inset 0 0 0 1.5px var(--ink-faint); }
.sam-hab-week i[data-state="future"] { background: var(--surface-sunken); }
.sam-hab-week small { font-size: 0.625rem; color: var(--ink-muted); }
.sam-hab-week .is-today small { color: var(--ink-strong); font-weight: 600; }

@media (max-width: 359px) {
  .sam-hab-dots { display: none; }
  .sam-hab-row { grid-template-columns: 30px minmax(0, 1fr) 34px; }
}
/* 2.1.2 — «¿Cómo van mis hábitos?» en el teléfono: la tira de 7 puntos (73
   px) y la palomita se comían la fila y el nombre se quedaba en 98 px a 360
   y 126 a 390 («Tomar vitami…», «Sacar a pase…»). Cuando la lista mide menos
   de 22rem, los puntos bajan a un segundo renglón bajo el nombre en vez de
   ocultarse (también por debajo de 360, donde antes se quitaban): el nombre
   gana la fila y la tira se sigue viendo. Icono y palomita quedan centrados
   en la fila. `rem` sigue a la escala de letra. En tableta y escritorio los
   puntos siguen en la fila.
   La tarjeta de hábitos era la única de las cinco respuestas rápidas que no
   iba a lo ancho del hilo (423 px entre cuatro de 720 a 1280; se ajustaba a
   su contenido). Ahora va a lo ancho como las demás, igual que 2.1.2 ya hizo
   con gasto e ingreso; además la lista necesita un ancho que no dependa de
   ella misma para poder medirse. */
.sam-brief-habits { align-self: stretch; }
.sam-hab-list { container-type: inline-size; }
@container (max-width: 22rem) {
  .sam-hab-row { grid-template-columns: 30px minmax(0, 1fr) 34px; row-gap: 6px; }
  .sam-hab-row > .sam-hab-icon { grid-column: 1; grid-row: 1 / span 2; }
  .sam-hab-row > .sam-hab-copy { grid-column: 2; grid-row: 1; }
  .sam-hab-row > .sam-hab-dots { display: flex; grid-column: 2; grid-row: 2; justify-self: start; }
  .sam-hab-row > .sam-hab-check { grid-column: 3; grid-row: 1 / span 2; }
}

/* 1.26.1 — Tarjeta de hábitos de un día anterior: sin botones, con su fecha.
   Un resumen guardado en el chat es una foto, no un mando a distancia. */
.sam-hab-stale {
  display: flex; align-items: center; gap: 6px;
  margin-top: var(--s3); padding-top: var(--s3);
  border-top: 1px solid var(--line);
  font-size: 0.8125rem; color: var(--ink-muted);
}
.sam-hab-stale .i { font-size: 0.9375rem; flex: 0 0 auto; }
/* En una tarjeta vieja el cumplido se ve, pero no se toca. */
.sam-hab-check.is-marked::before { background: var(--habit-green, var(--ctx-finanzas)); border-color: var(--habit-green, var(--ctx-finanzas)); }
.sam-hab-check.is-marked svg { position: relative; width: 14px; height: 14px; fill: none; stroke: var(--habit-on-green, #fff); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }

/* ==========================================================================
   1.28 · PERSONAS — menos saturación, misma estética
   --------------------------------------------------------------------------
   No hay identidad nueva: se reutilizan el papel crema, los pasteles, la
   tipografía redondeada, los avatares con su tono y los mismos botones. Lo que
   cambia es la densidad de decisiones: UNA acción principal, dos pestañas, una
   fila por persona y las acciones dentro de su ficha.
   ========================================================================== */

/* La acción principal manda: ocupa la fila entera y es el único botón
   primario de la vista. */
.people-top { margin: 0 0 var(--s3); }
.people-cta { width: 100%; }

/* Pestañas discretas, no un segundo menú. El subrayado es el estado; el color
   no va solo, porque distinguir "activo" sólo por color deja fuera a quien no
   percibe bien los matices. */
.people-tabs {
  display: flex;
  gap: var(--s1);
  margin: 0 0 var(--s4);
  border-bottom: 1px solid var(--line);
}
.people-tab {
  flex: 1 1 auto;
  min-height: 44px;
  padding: var(--s2) var(--s3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
}
.people-tab.is-on {
  color: var(--ink-strong);
  border-bottom-color: var(--ctx-personas, var(--color-blue));
}
.people-tab:focus-visible { outline: 2px solid var(--focus, var(--ink)); outline-offset: -2px; border-radius: var(--r-sm); }

/* Una fila por persona: avatar, nombre, UNA línea secundaria y un chevrón.
   Toda la fila es el objetivo táctil, de 56 px, y no lleva botones dentro. */
.people-list { margin: 0 0 var(--s4); }
.person-line {
  width: 100%;
  min-height: 56px;
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s1);
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.person-line:last-child { border-bottom: 0; }
.person-line:hover { background: var(--surface-hover); }
.person-line:focus-visible { outline: 2px solid var(--focus, var(--ink)); outline-offset: -2px; border-radius: var(--r-sm); }
.person-line-copy { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.person-line-copy b {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--ink-strong);
  /* Un nombre largo se corta con puntos, no rompe la fila. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.person-line-copy small {
  font-size: 0.8125rem;
  color: var(--ink-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.person-line-tag {
  flex: 0 0 auto;
  padding: 2px 8px;
  border-radius: var(--r-full);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: 0.6875rem;
  font-weight: 600;
}
.person-line-go { flex: 0 0 auto; color: var(--ink-faint); font-size: 1.125rem; line-height: 1; }

.people-search-wrap { margin: 0 0 var(--s3); }
.people-search {
  width: 100%;
  min-height: 44px;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--ink);
  font: inherit;
  font-size: 0.875rem;
}

/* Fichas privadas: al final, plegadas y con una sola línea de explicación.
   Son el camino secundario, no el principal. */
.people-private { margin: var(--s4) 0 0; border-top: 1px solid var(--line); padding-top: var(--s3); }
.people-private-head {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
}
.people-private-head .people-count { background: var(--surface-sunken); color: var(--ink-muted); border: 1px solid var(--line); }
.people-private-chevron { margin-left: auto; transition: transform .15s ease; }
.people-private-head.is-open .people-private-chevron { transform: rotate(90deg); }
.people-private-body { padding-top: var(--s2); }

/* Una solicitud es una tarjeta: hay que leerla antes de decidir. */
.request-card {
  display: flex;
  gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--line);
}
.request-card:last-child { border-bottom: 0; }
.request-copy { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.request-copy b { font-size: 0.9375rem; font-weight: 600; color: var(--ink-strong); }
.request-copy small { font-size: 0.8125rem; color: var(--ink-muted); line-height: 1.35; }
/* 2.1.2 — P1: los correos y asuntos largos se parten. Uno realista de 42
   caracteres salía 40 px de la solicitud a 320 y la hoja se desplazaba en
   horizontal. La lista con separadores se queda como está (es de diseño). */
.request-copy small { overflow-wrap: anywhere; }
.request-subject { font-weight: 600; color: var(--ink) !important; }
.request-note { color: var(--ink-faint) !important; }
.request-actions { display: flex; gap: var(--s2); margin-top: var(--s2); flex-wrap: wrap; }
.request-actions .btn { min-height: 44px; }
/* 2.1.2 — A4: la lista «Actividad del área» no tenía ni una regla: el avatar
   quedaba solo en su renglón y la hora iba pegada al texto. Avatar a la
   izquierda, texto al lado y detalle y hora cada uno en su renglón. */
.activity-item { display: flex; gap: 10px; align-items: flex-start; padding: 10px 0; border-bottom: 1px solid var(--line); }
.activity-item:last-child { border-bottom: 0; }
.activity-item > div { min-width: 0; flex: 1; overflow-wrap: anywhere; }
.activity-item small, .activity-item time { display: block; color: var(--ink-muted); font-size: 0.75rem; }
/* 2.1.2 — C6: en Mis dispositivos, a 320 «Cerrar sesión» dejaba al texto
   ~80 px y «Notificaciones» se metía debajo del botón. En teléfonos angostos
   el botón baja a su propio renglón, a la derecha. Sólo esta hoja
   (`.devices-list`, collab.js). */
@media (max-width: 380px) {
  .devices-list > .row { flex-wrap: wrap; }
  .devices-list > .row > .row-body { flex-basis: calc(100% - 48px); }
  .devices-list > .row > .btn { margin-left: auto; }
}

/* Ficha de una persona */
.person-head { display: flex; gap: var(--s3); align-items: center; margin: 0 0 var(--s4); }
.person-head > div { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.person-head strong { font-size: 1.0625rem; color: var(--ink-strong); overflow-wrap: anywhere; }
.person-head small { font-size: 0.8125rem; color: var(--ink-muted); overflow-wrap: anywhere; }
.person-relation { color: var(--ink-faint) !important; }
.people-danger { margin-top: var(--s5); padding-top: var(--s3); border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: var(--s2); }
.people-merge { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3); background: var(--surface-sunken); }

/* Estado de la respuesta a una asignación: el color va con la palabra, nunca
   solo (H13). */
.assignment-state {
  margin: 0 0 var(--s3);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  background: var(--surface-sunken);
  border-left: 3px solid var(--line-strong);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ink);
}
.assignment-state[data-state="accepted"] { border-left-color: var(--ctx-finanzas, var(--color-teal)); }
.assignment-state[data-state="rejected"] { border-left-color: var(--ctx-atencion, var(--color-coral)); }
.assignment-state[data-state="completed"] { border-left-color: var(--ctx-finanzas, var(--color-green)); }

/* Destinatario de una invitación, dentro de la hoja del enlace. */
.invite-target { margin: var(--s2) 0 0; font-size: 0.8125rem; color: var(--ink-muted); overflow-wrap: anywhere; }
.invite-target strong { color: var(--ink); }

.people-search-empty { margin-top: var(--s3); }
.linklike {
  border: 0; background: none; padding: 0;
  color: var(--ink); font: inherit; font-weight: 600;
  text-decoration: underline; cursor: pointer;
}

/* Con el teclado abierto y en pantallas cortas, las tarjetas de solicitud
   apilan sus botones para que sigan alcanzables sin desplazamiento lateral. */
@media (max-width: 360px) {
  .request-actions .btn { flex: 1 1 100%; }
  .people-tab { padding: var(--s2) var(--s1); font-size: 0.875rem; }
}

/* Ajustes tras ver las capturas en Chrome:
   · el bloque que sigue al botón principal necesitaba aire;
   · un contador de tareas no es una alerta. El coral se reserva para lo que
     pide respuesta —las solicitudes recibidas—, y dentro de un bloque el
     número va en tono neutro. */
.person-head + .btn-block { margin-bottom: var(--s5); }
.people-block .people-count {
  background: var(--surface-sunken);
  color: var(--ink-muted);
  border: 1px solid var(--line);
}
.people-tabs .people-count { background: var(--ctx-atencion); color: var(--ink-on-color); border: 0; }

/* ============================================================================
   1.30 — Espaciado: lo que la auditoría midió
   ----------------------------------------------------------------------------
   Sin tocar colores, tipografía ni navegación: sólo separar, alinear y dejar
   respirar. Cada regla dice qué medida arregla.
   ========================================================================== */

/* El pie de una hoja tocaba el último elemento del cuerpo (0 px medidos en
   Personas, entre "Crear una ficha privada" y Cerrar/Actualizar). 16 px, y
   sólo cuando el pie tiene algo: las hojas sin pie no cambian. */
.sheet-foot:not(:empty) { margin-top: var(--s4); }
/* Y si el cuerpo ya termina en un bloque con su propio margen, no se suman:
   el pie manda una sola vez. */
.sheet-body > :last-child { margin-bottom: 0; }

/* Botón con icono + texto: el conjunto va centrado y con 8 px entre los dos.
   `.btn` ya trae `padding: 0 var(--s5)` (20 px) y `justify-content:center`, que
   es justo lo que pide el documento de espaciado, así que aquí NO se toca el
   relleno: hacerlo aplastaba los botones redondos de sólo icono, que fijan su
   ancho a 42 px a 360 px de pantalla. */
.btn { gap: var(--s2); }

/* Barra inferior con el texto del sistema aumentado: "Pendientes" y
   "Calendario" se acercaban hasta pisarse. La etiqueta se recorta con puntos
   suspensivos dentro de SU columna en vez de invadir la de al lado, y cada
   pestaña conserva su área táctil. El nombre completo sigue anunciado para el
   lector de pantalla, que lee el botón entero. */
.tab { padding-inline: 2px; }
.tab > span:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
}

/* Personas: el bloque de fichas privadas ya no queda pegado al pie, y las
   filas comparten la columna izquierda con el resto de la hoja. */
.people-private { margin-bottom: var(--s3); }
.people-block:last-child { margin-bottom: var(--s2); }

/* ============================================================================
   1.31.3 · LISTAS LARGAS: QUE EL NAVEGADOR NO PINTE LO QUE NO SE VE
   ----------------------------------------------------------------------------
   Medido con 1.000 pendientes en una CPU 6 veces más lenta (un teléfono de
   gama media): abrir la pestaña Pendientes costaba 2.653 ms y 21 de 40 frames
   del desplazamiento pasaban de 50 ms. La causa es la masa: 821 filas, 14.605
   nodos, 52.996 px de alto — 63 pantallas.

   `content-visibility: auto` le dice al navegador que se salte el diseño y el
   pintado de lo que está fuera de la ventana. No cambia el marcado, ni el
   comportamiento, ni el orden del DOM: Ctrl+F, el lector de pantalla y el
   desplazamiento siguen funcionando igual.

   `contain-intrinsic-size` es obligatorio acompañarlo: es la altura que el
   navegador supone mientras no ha pintado la fila. Sin ella, la barra de
   desplazamiento daría saltos. 62px es el alto real medido de una fila con
   una línea de meta (--tap 44 + 2×12 de relleno + borde).

   Se aplica SÓLO donde hay listas que pueden crecer sin límite. Las listas
   cortas no ganan nada y sí podrían notar el salto. */
#screen-tasks .row,
/* 2.1.2 — También el envoltorio deslizable (.swipe, hijo directo de la
   pantalla, con la capa «Eliminar»): era lo que seguía costando layout con
   1,640 filas. Medido a 390: el layout de cada repintado baja de 69–99 ms
   a 10–17 ms; las filas en vista se pintan igual (0 saltadas). */
#screen-tasks > .swipe,
#screen-habits .habits-list > *,
.finance-tx-list > .finance-tx-row {
  content-visibility: auto;
  contain-intrinsic-size: auto 62px;
}
/* 2.1.2 — El envoltorio .swipe mide 84 px (fila + meta), no 62: con 1,640
   pendientes el alto supuesto se quedaba 24 % corto y «Fin» no llegaba al
   último. Con su alto real el documento vuelve a medir lo mismo que en 2.1.1. */
#screen-tasks > .swipe { contain-intrinsic-size: auto 84px; }

/* 1.32.10 — Espacio legible en recuperación de permisos. */
.notification-guidance p+p{margin-top:10px}
.notification-sheet details p{line-height:1.5;overflow-wrap:anywhere}

/* 2.1.0 — Observabilidad de recordatorios: prueba con el teléfono bloqueado,
   guía de batería (Android) y recordatorios no mostrados. Misma anatomía que
   el resto de la hoja: bordes --line, radio --r-md, texto secundario
   --ink-muted y el punto de estado de .notification-status. Los desplegables
   de marca reutilizan .devices-platform (tutorial.css). */
.notif-bloqueo-intro{margin:0;color:var(--ink-muted);font-size:var(--t-meta);line-height:1.4}
.notif-estado{position:relative;margin:0;padding:10px 12px 10px 30px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-sunken);color:var(--ink);font-size:var(--t-meta);line-height:1.4}
.notif-estado::before{content:"";position:absolute;left:12px;top:15px;width:9px;height:9px;border-radius:50%;background:var(--ink-faint)}
.notif-estado[data-tono="ok"]::before{background:var(--ctx-finanzas)}
.notif-estado[data-tono="alerta"]::before{background:var(--ctx-atencion)}
.notif-estado small{display:block;margin-top:4px;color:var(--ink-muted)}
.notif-estado+.notif-estado{margin-top:8px}
.notif-acciones{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.notif-bateria{border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface)}
.notif-bateria>summary{position:relative;display:flex;flex-direction:column;justify-content:center;gap:2px;min-height:var(--tap);padding:12px 40px 12px 16px;cursor:pointer;list-style:none}
.notif-bateria>summary::-webkit-details-marker{display:none}
.notif-bateria>summary::after{content:"";position:absolute;right:18px;top:50%;width:7px;height:7px;margin-top:-6px;border-right:2px solid var(--ink-muted);border-bottom:2px solid var(--ink-muted);transform:rotate(45deg);transition:transform .18s ease}
.notif-bateria[open]>summary::after{margin-top:-2px;transform:rotate(-135deg)}
.notif-bateria-titulo{font-weight:600;color:var(--ink)}
.notif-bateria>summary small{color:var(--ink-muted);font-size:var(--t-micro);line-height:1.35}
.notif-bateria-cuerpo{display:grid;gap:8px;padding:0 12px 12px}
.notif-bateria-lead{margin:0;color:var(--ink-muted);font-size:var(--t-meta);line-height:1.45}
.notif-perdidos-lead{margin:0;color:var(--ink-muted);line-height:1.45}
.notif-perdidos-lista{list-style:none;margin:0;padding:0;display:grid;gap:6px}
.notif-perdido{display:flex;align-items:center;gap:12px;width:100%;min-height:var(--tap);padding:10px 14px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface);color:var(--ink);font:inherit;text-align:left;cursor:pointer}
.notif-perdido::after{content:"";flex:none;margin-left:auto;width:7px;height:7px;border-right:2px solid var(--ink-faint);border-top:2px solid var(--ink-faint);transform:rotate(45deg)}
.notif-perdido-copy{min-width:0;display:grid;gap:2px}
.notif-perdido-copy b{font-weight:600;overflow-wrap:anywhere}
.notif-perdido-copy small{color:var(--ink-muted);font-size:var(--t-micro);line-height:1.35}
.notif-perdidos-ayuda{display:flex;flex-wrap:wrap;gap:8px}
.notif-perdidos-ayuda .btn{flex:1 1 160px}
@media (prefers-reduced-motion:reduce){.notif-bateria>summary::after{transition:none}}

/* ============================================================================
   2.1.2 — REDISEÑO DE LAS TARJETAS DE SAM (propuesta visual aprobada)
   ----------------------------------------------------------------------------
   Sólo tokens existentes; los tintes nuevos se mezclan contra --tone-canvas
   o reutilizan los --tint-* de tokens.css, y el texto sobre tinte va mezclado
   con --ink-strong (como el recibo de «hecho»), medido en los cuatro temas.
   Tamaños mínimos intactos: letra de siempre y objetivos de 44 px.
   ========================================================================== */

/* A · Icono pequeño junto a cada valor: etiqueta a la izquierda, icono y
   valor a la derecha. El icono va en línea con el texto (no en flex): con un
   valor de dos renglones se queda pegado al primero en vez de irse al borde. */
.proposal .prop-field .prop-field-icon {
  display: inline-flex; width: 1em; height: 1em; margin-right: 6px; vertical-align: -0.14em;
  font-size: inherit; color: var(--ink-muted);
}
.proposal .prop-field-value { font-style: normal; }

/* C · Confirmada: todo el tono pasa al verde de «hecho» (franja, icono,
   rótulo); el icono es una palomita grande en su círculo. */
.proposal.done[data-tone] { --proposal-tone: var(--ctx-finanzas); }
.proposal.done { background: var(--tint-finanzas); }
.proposal .prop-icon-state { width: 40px; height: 40px; }
.proposal .prop-icon-state .i { font-size: 1.375rem; }
.proposal.done .prop-icon-state {
  background: color-mix(in srgb, var(--ctx-finanzas) 22%, var(--tone-canvas));
  color: color-mix(in srgb, var(--ctx-finanzas) 40%, var(--ink-strong));
}
.proposal.done .prop-icon-state .i svg { stroke-width: 2.6; }
.proposal.done .prop-verb,
[data-color-mode="color"] .proposal.done[data-tone] .prop-verb,
[data-color-mode="sober"] .proposal.done .prop-verb {
  background: color-mix(in srgb, var(--ctx-finanzas) 16%, var(--tone-canvas));
  border-color: color-mix(in srgb, var(--ctx-finanzas) 26%, transparent);
  color: color-mix(in srgb, var(--ctx-finanzas) 40%, var(--ink-strong));
}
.proposal.done .prop-done-text b { font-weight: 700; }
.proposal.done .prop-done { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid color-mix(in srgb, var(--ctx-finanzas) 22%, var(--line)); }

/* C · Descartada: el mismo esquema, discreto y en gris. */
.proposal.discarded[data-tone] { --proposal-tone: var(--ink-faint); }
.proposal.discarded .prop-icon-state { width: 34px; height: 34px; background: var(--surface); color: var(--ink-muted); }
.proposal.discarded .prop-icon-state .i { font-size: 1.125rem; }
.proposal.discarded .prop-verb,
[data-color-mode="color"] .proposal.discarded[data-tone] .prop-verb,
[data-color-mode="sober"] .proposal.discarded .prop-verb {
  background: var(--surface); border-color: var(--line); color: var(--ink-muted);
}
.proposal.discarded .prop-title { color: var(--ink-muted); font-weight: 500; }
.proposal .prop-was { display: block; font-size: var(--t-meta); color: var(--ink-muted); }

/* D · Varias en un mensaje: un contenedor con su recuento y las propuestas
   dentro, más compactas pero cada una con sus botones. */
.prop-group {
  display: flex; flex-direction: column; gap: var(--s2);
  width: 100%; margin-top: var(--s3); padding: var(--s3);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface-sunken);
}
.prop-group-head { display: flex; align-items: center; gap: var(--s2); margin: 0 0 2px; padding: 0 2px; color: var(--ink-strong); font-size: var(--t-body); font-weight: 600; }
.prop-group-head .i { width: 1.1em; height: 1.1em; color: var(--ink-muted); }
.prop-group .proposal { margin-top: 0; padding: var(--s4); border-radius: var(--r-md); box-shadow: none; }
.prop-group .prop-fields { margin-top: var(--s2); }
.prop-group .prop-field { padding: 4px 0; }
.prop-group .prop-actions { margin-top: var(--s3); }

/* E · Resúmenes. Filas con su ficha de fecha a la derecha (mismas piezas que
   las preguntas rápidas) y la ficha de atraso en el tono de atención. */
.sam-brief-qchip.is-late { color: color-mix(in srgb, var(--ctx-atencion) 40%, var(--ink-strong)); background: var(--tint-atencion); }
[data-color-mode="color"] .sam-brief-tasks .sam-brief-qchip:not(.is-late) { background: var(--surface); }
.sam-brief-tasks .sam-brief-rows { container-type: inline-size; }

/* Pendientes de hoy: barra del reparto por estado y sus fichas. */
.sam-brief-stack { display: flex; gap: 2px; height: 6px; margin: -4px 0 var(--s3); border-radius: var(--r-full); overflow: hidden; background: color-mix(in srgb, var(--ink) 9%, transparent); }
.sam-brief-stack i { display: block; min-width: 4px; height: 100%; }
.sam-brief-stack i[data-st="doing"] { background: var(--brief-accent); }
.sam-brief-stack i[data-st="waiting"] { background: var(--ctx-calendario); }
.sam-brief-stack i[data-st="blocked"] { background: var(--ink-faint); }
.sam-brief-stack i[data-st="todo"] { background: color-mix(in srgb, var(--ink) 9%, transparent); }
.sam-brief-pill.is-st-doing { background: var(--brief-tint); color: color-mix(in srgb, var(--brief-accent) 40%, var(--ink-strong)); }
.sam-brief-pill.is-st-waiting { background: var(--tint-calendario); color: color-mix(in srgb, var(--ctx-calendario) 40%, var(--ink-strong)); }
.sam-brief-pill.is-late { color: color-mix(in srgb, var(--ctx-atencion) 40%, var(--ink-strong)); }

/* Agenda: la hora es ahora la columna que se lee primero. El tono puro sobre
   el tinte de la fila se perdía en el tema claro (rosa sobre rosa); mezclado
   con la tinta fuerte conserva el matiz y se lee. */
.sam-brief-time { color: color-mix(in srgb, var(--brief-accent) 40%, var(--ink-strong)); }
.sam-brief-time.is-due { color: color-mix(in srgb, var(--ctx-atencion) 40%, var(--ink-strong)); }
/* Agenda de la semana: el día sobre la hora, en la misma columna. */
.sam-brief-timeline.is-week .sam-brief-time { display: flex; flex-direction: column; gap: 1px; line-height: 1.25; }
.sam-brief-timeline.is-week .sam-brief-time b { color: var(--ink-strong); font-weight: 700; }

/* Tu día: tres contadores al pie. */
.sam-brief-counters { display: grid; grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr)); gap: var(--s2); margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--line); }
.sam-brief-counters > div { min-width: 0; }
.sam-brief-counters strong { display: block; color: var(--ink-strong); font-size: 1.375rem; font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; }
.sam-brief-counters span { display: block; margin-top: 2px; color: var(--ink-muted); font-size: 0.75rem; line-height: 1.3; overflow-wrap: anywhere; }

/* Ingresos sin datos: estado vacío con su botón. */
.sam-brief-empty-cta { flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-bottom: var(--s3); padding: var(--s3); border-radius: var(--r-md); background: var(--brief-tint); }
.sam-brief-empty-cta > span:not(.sam-brief-empty-icon) { flex: 1 1 10rem; min-width: 0; }
.sam-brief-empty-icon { display: grid; place-items: center; width: 34px; height: 34px; border-radius: var(--r-full); background: var(--surface); color: color-mix(in srgb, var(--brief-accent) 40%, var(--ink-strong)); }
.sam-brief-empty-icon .i { width: 17px; height: 17px; }
.sam-brief-empty-cta .btn { min-height: var(--tap); }

/* 2.1.2 — Sin glifos de texto como icono (el dueño: "usa SVGs siempre").
   Los últimos que vivían en el CSS (+ − ⌄ ⌃ ← → ✓) pasan a SVG de la misma
   familia que icons.js, dibujados con máscara sobre currentColor: heredan el
   color del texto en los cuatro temas y miden lo mismo que el glifo. */
:root {
  --glifo-chev-down: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.4%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m6%209%206%206%206-6%27%2F%3E%3C%2Fsvg%3E");
  --glifo-chev-up: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.4%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m18%2015-6-6-6%206%27%2F%3E%3C%2Fsvg%3E");
  --glifo-izq: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.4%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M19%2012H5%27%2F%3E%3Cpath%20d%3D%27m12%2019-7-7%207-7%27%2F%3E%3C%2Fsvg%3E");
  --glifo-der: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.4%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M5%2012h14%27%2F%3E%3Cpath%20d%3D%27m12%205%207%207-7%207%27%2F%3E%3C%2Fsvg%3E");
  --glifo-check: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.4%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M20%206%209%2017l-5-5%27%2F%3E%3C%2Fsvg%3E");
  --glifo-mas: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.4%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M12%205v14%27%2F%3E%3Cpath%20d%3D%27M5%2012h14%27%2F%3E%3C%2Fsvg%3E");
  --glifo-menos: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27black%27%20stroke-width%3D%272.4%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M5%2012h14%27%2F%3E%3C%2Fsvg%3E");
  --glifo-tri-arriba: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27black%27%3E%3Cpath%20d%3D%27M12%205%2020%2018H4z%27%2F%3E%3C%2Fsvg%3E");
  --glifo-tri-abajo: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27black%27%3E%3Cpath%20d%3D%27M12%2019%204%206h16z%27%2F%3E%3C%2Fsvg%3E");
}
.manual-task-more summary::after,
.tx-chip-more::after {
  content: ""; display: inline-block; width: 0.95em; height: 0.95em;
  margin-left: 0.3em; vertical-align: -0.14em; background-color: currentColor; -webkit-mask: var(--glifo) center / contain no-repeat; mask: var(--glifo) center / contain no-repeat;
}
.manual-task-more summary::after { --glifo: var(--glifo-mas); }
.manual-task-more[open] summary::after { --glifo: var(--glifo-menos); content: ""; }
.tx-chip-more::after { --glifo: var(--glifo-chev-down); }
.tx-picker:has(.tx-more-toggle:checked) .tx-chip-more::after { --glifo: var(--glifo-chev-up); content: ""; }
/* Asignación: el aro sigue en ::after; la flecha va encima en ::before. */
.row.assign-in  .row-assignee::after,
.row.assign-out .row-assignee::after { content: ""; }
.row.assign-in  .row-assignee::before,
.row.assign-out .row-assignee::before {
  content: ""; position: absolute; right: -4px; bottom: -4px; width: 15px; height: 15px;
  z-index: 1; pointer-events: none; color: var(--ink);
  background-color: currentColor; -webkit-mask: var(--glifo) center / contain no-repeat; mask: var(--glifo) center / contain no-repeat;
  -webkit-mask-size: 9px 9px; mask-size: 9px 9px;
}
.row.assign-in  .row-assignee::before { --glifo: var(--glifo-izq); }
.row.assign-out .row-assignee::before { --glifo: var(--glifo-der); }
.billing-benefits li::before {
  content: ""; width: 14px; height: 14px; top: 0.2em; background-color: currentColor; -webkit-mask: var(--glifo) center / contain no-repeat; mask: var(--glifo) center / contain no-repeat; --glifo: var(--glifo-check);
}

/* ============================================================================
   2.1.3 — SAM · PANEL DE OPCIONES Y AUTOCOMPLETAR (sam.js)
   Va DENTRO de la barra del cuadro y en el flujo: el hilo se encoge, así el
   panel nunca tapa el cuadro de texto ni la barra inferior, y si no cabe
   desliza dentro de sí mismo. Hereda el ancho de .composer-bar, así que sus
   bordes caen exactamente sobre los del cuadro (y sobre la columna del chat
   en escritorio). Una sola medida de separación entre opciones, en
   horizontal y en vertical (--s2); los grupos se separan con --s3.
   ========================================================================== */
/* El botón de opciones va pegado al borde izquierdo del cuadro (su círculo
   de 44 px cabe dentro de la curva de la píldora) y sin hueco hasta el
   texto: el icono ya trae su aire. Así el cuadro pierde lo mínimo. */
.composer.has-opts { padding-left: var(--s1); }
.composer.has-opts .composer-opts { margin-right: calc(var(--s1) * -1); }
/* Abierto se nota: el botón se queda hundido mientras el panel está a la vista. */
.composer-opts[aria-expanded="true"] { background: var(--surface-sunken); color: var(--ink); }
.sam-locked.has-opts { padding-left: var(--s2); }
/* Sin Premium, en el teléfono el aviso ocupa su renglón entero y debajo van
   el botón de opciones (a la izquierda) y «Ver Premium» (a la derecha): con
   los tres en fila el texto quedaba en una columna angosta de 5–8 renglones. */
@media (max-width: 459px) {
  .sam-locked.has-opts { flex-wrap: wrap; row-gap: var(--s1); padding-left: var(--s4); }
  .sam-locked.has-opts .sam-locked-text { flex-basis: 100%; order: -1; }
  .sam-locked.has-opts .composer-opts { margin-left: calc(var(--s2h) * -1); }
  .sam-locked.has-opts [data-act="billing-open"] { margin-left: auto; }
}
/* Con el botón, en 320–360 px la frase de ayuda ya no cabe: en un renglón
   y desvanecida al final, en vez de partirse y hacer crecer el cuadro. */
.composer.has-opts textarea::placeholder { white-space: nowrap; }
.composer.has-opts textarea:placeholder-shown {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--s5)), transparent);
          mask-image: linear-gradient(to right, #000 calc(100% - var(--s5)), transparent);
}
/* Mientras el panel está abierto: la fila de chips se oculta (son las mismas
   preguntas) y también los arranques del saludo, que quedarían cortados. */
.sam-rapidas[hidden] { display: none; }
#screen-sam.sam-opts-open .sam-hello .sam-starters { display: none; }
/* Sin sitio para el saludo completo (sam.js, ajustarSaludo): se oculta su
   contenido pero no su hueco, así el panel sigue pegado al cuadro. */
#screen-sam.sam-opts-open .sam-hello.is-squeezed > * { visibility: hidden; }
/* 2.1.3 — …y el panel toma el sitio que el saludo deja: sam.js marca
   #screen-sam con sam-hello-squeezed y el panel pierde su tope. Antes se
   quedaba en 52vh bajo un hueco en blanco (76 px a 320×568, 177 px con letra
   al 200 %) cortando fichas a la mitad. La barra ya es columna flexible y el
   panel cede (flex 0 1 auto; min-height 0): crece hasta lo disponible y, si
   aun así no cabe, desliza. Sobre el panel queda sólo el aire de la barra. */
#screen-sam.sam-opts-open.sam-hello-squeezed .sam-opts { max-height: none; }
#screen-sam.sam-opts-open .sam-hello.is-squeezed:not(.is-compact) { padding-top: 0; padding-bottom: 0; }
/* Si con el panel entero todavía sobra alto (sin Premium a 390), en vez del
   hueco se ve el husky solo, centrado: saludo compacto (is-compact). */
#screen-sam.sam-opts-open .sam-hello.is-squeezed.is-compact > :not(.sam-mark-lg) { display: none; }
#screen-sam.sam-opts-open .sam-hello.is-squeezed.is-compact > .sam-mark-lg { visibility: visible; }
/* 2.1.3 — El panel es la tarjeta (borde, fondo, sombra y tope de alto) y
   no desliza: dentro van el cuerpo con las opciones, que es lo único que
   desliza, y el pie con «Ver más» / «Ver menos», que nunca desliza ni se
   desvanece. */
.sam-opts {
  display: flex;
  flex-direction: column;
  margin: 0 0 var(--s2);
  max-height: min(28rem, 52vh);
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-low);
}
.sam-opts-body {
  --sam-opts-pie: var(--s3);
  flex: 0 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s3) var(--s3) var(--sam-opts-pie);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
/* Con pie debajo, el cuerpo cierra con --s2 y el pie con otros --s2: de la
   última ficha al botón y del botón al borde, la misma separación. */
.sam-opts-body:not(:last-child) { --sam-opts-pie: var(--s2); }
/* Abierto del todo («Ver más»), el panel puede tomar más alto; si el saludo
   ya no cabe, sam.js lo aprieta y el panel ocupa su sitio. Sin transición:
   con movimiento reducido la regla global deja .01ms en «all» y el tope
   nuevo llegaba un cuadro tarde, cuando sam.js ya había medido el saludo. */
.sam-opts.is-expanded { max-height: min(44rem, 78vh); }
.sam-opts { transition-property: none; }
.sam-opts[hidden], .sam-opts-group[hidden], .sam-opt[hidden] { display: none; }
/* 2.1.3 — Si quedan opciones abajo, fuera de la vista (sam.js marca el
   cuerpo con has-more-below), el pie del cuerpo se desvanece hacia su
   fondo: una ficha cortada por el borde se lee como «hay más». Es una capa
   pegada al pie de lo visible que no ocupa sitio (el margen negativo
   compensa su alto y el hueco) ni recibe toques. Sólo sobre el cuerpo:
   «Ver más» está fuera, en el pie del panel. Chrome pega lo sticky al borde
   del relleno: el relleno de abajo en negativo lo baja hasta el borde. */
.sam-opts-body.has-more-below::after {
  content: '';
  flex: none;
  position: sticky;
  bottom: calc(var(--sam-opts-pie) * -1);
  height: var(--s6);
  margin-top: calc((var(--s6) + var(--s3)) * -1);
  background: linear-gradient(to bottom, transparent, var(--surface));
  pointer-events: none;
}
/* Con el teclado abierto la pantalla mide lo visible (--sam-visual-height):
   el panel se queda con menos de la mitad y el hilo conserva su sitio. */
body.mobile-keyboard-open .sam-opts { max-height: calc(var(--sam-visual-height, 100dvh) * .45); }
/* Si aun así no cabe (320 px con el aviso de Premium, letra grande), el
   panel es lo que cede: la barra se vuelve columna flexible, el cuadro (o el
   aviso) conserva su alto y el panel se encoge y desliza. Sólo con el panel
   abierto; cerrado, la barra es la de siempre. */
#screen-sam.sam-opts-open .composer-bar { display: flex; flex-direction: column; min-height: 0; }
#screen-sam.sam-opts-open .composer-bar > * { flex: none; }
#screen-sam.sam-opts-open .composer-bar > .sam-opts { flex: 0 1 auto; min-height: 0; }
.sam-opts-group { display: flex; flex-direction: column; gap: var(--s2); flex: none; }
/* Títulos en versalitas, como las secciones de las tarjetas de Sam. */
.sam-opts-title {
  margin: 0;
  padding: 0 var(--s1);
  color: var(--ink-muted);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .07em;
  line-height: 1.3;
  text-transform: uppercase;
}
/* Cada renglón se llena: una opción sola en su renglón toma el ancho entero,
   así no quedan huecos a un lado. Consultar y Pedirle a Sam: una columna en
   el teléfono y dos en escritorio. Crear: dos en el teléfono, cuatro en
   escritorio. Las bases van en rem: con letra grande bajan a una columna. */
.sam-opts-list { display: flex; flex-wrap: wrap; gap: var(--s2); }
.sam-opt {
  flex: 1 1 17rem;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--s2h);
  min-height: var(--tap);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-sunken);
  color: var(--ink);
  font-size: 0.875rem;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
/* Crear: fichas con el icono encima y el texto centrado (acciones, no
   preguntas). Dos por renglón en el teléfono, todas en uno en escritorio. */
.sam-opts-list-crear .sam-opt {
  flex-basis: 7rem;
  flex-direction: column;
  justify-content: center;
  gap: var(--s1h);
  padding: var(--s2h) var(--s2);
  font-size: 0.8125rem;
  text-align: center;
}
.sam-opts-list-crear .sam-opt-text { flex: none; max-width: 100%; }
/* 2.1.3 — Todas las fichas de un renglón miden lo mismo (el renglón las
   estira) y cada una centra su icono y su texto en vertical: la de una
   línea junto a «Agregar a Súper del mes» ya no queda con el contenido
   arriba y un hueco abajo. El texto toma el ancho de la ficha (centrado
   por text-align), el icono se centra solo, y la etiqueta cabe en dos
   renglones como mucho, con elipsis (el texto completo va en aria-label y
   title, sam.js). */
.sam-opts-list-crear .sam-opt { justify-content: center; align-items: stretch; }
.sam-opts-list-crear .sam-opt-icon { align-self: center; }
.sam-opts-list-crear .sam-opt-text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
/* 2.1.3 — Con «Ver más» abierto hay 5–6 fichas: en un solo renglón (la
   computadora) se angostaban de 180 a 119 px y el texto se partía. Con
   cinco o más a la vista van de tres en tres como máximo; en el teléfono
   siguen de dos en dos (manda la base de 7rem). */
.sam-opts-list-crear:has(> .sam-opt:nth-child(5):not([hidden])) > .sam-opt {
  flex-basis: max(7rem, calc((100% - 2 * var(--s2)) / 3 - 1px));
}
.sam-opt:hover:not(:disabled) { background: var(--surface-hover); border-color: var(--line-strong); }
.sam-opt:disabled { opacity: .55; cursor: default; }
.sam-opt-icon { flex: none; display: inline-grid; place-items: center; width: 1.125rem; height: 1.125rem; color: var(--ink-muted); }
.sam-opt-icon .i, .sam-opt-icon svg { width: 100%; height: 100%; }
.sam-opt-icon svg { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.sam-opt-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* El título de una lista es del usuario: dos renglones como mucho. */
.sam-opt-text[data-user-content] { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
/* 2.1.3 — Pie del panel, fuera del cuerpo que desliza: no se encoge, no se
   desvanece y siempre se ve entero. */
.sam-opts-more {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s1h);
  min-height: var(--tap);
  margin: 0 var(--s3) var(--s2);
  padding: 0 var(--s3);
  border: 0;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink);
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
}
.sam-opts-more:hover { background: var(--surface-hover); }
.sam-opts-more .i { width: 1rem; height: 1rem; color: var(--ink-muted); transition: transform var(--dur) var(--ease); }
.sam-opts-more .i svg { width: 100%; height: 100%; }
.sam-opts-more[aria-expanded="true"] .i { transform: rotate(180deg); }
/* En Color, el mismo tono que los chips de preguntas rápidas (.sam-rapida). */
[data-color-mode="color"] .sam-opt { border-color: color-mix(in srgb, var(--color-blue) 22%, var(--line)); background: color-mix(in srgb, var(--color-blue) 6%, var(--surface)); }
[data-color-mode="color"] .sam-opt:hover:not(:disabled) { border-color: color-mix(in srgb, var(--color-blue) 40%, var(--line)); background: color-mix(in srgb, var(--color-blue) 12%, var(--surface)); }

/* 2.1.3 — Enviar en tema oscuro. En oscuro --action es claro y el cian
   de Color también: el avión quedaba cian sobre casi blanco (1.29:1). Va
   con --action-ink, como en Sobrio. Deshabilitado (cuadro vacío) quedaba en
   2.01:1: sin la opacidad llega a ≥3:1 y sigue viéndose apagado (sin
   fondo). El claro no cambia. Se repite para quien sigue al sistema. */
[data-theme="dark"][data-color-mode="color"] .composer-send:not(:disabled) { color: var(--action-ink); }
[data-theme="dark"] .composer-send:disabled,
[data-theme="dark"] .composer-send:disabled:hover { color: var(--ink-faint); opacity: 1; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"])[data-color-mode="color"] .composer-send:not(:disabled) { color: var(--action-ink); }
  :root:not([data-theme="light"]) .composer-send:disabled,
  :root:not([data-theme="light"]) .composer-send:disabled:hover { color: var(--ink-faint); opacity: 1; }
}

/* ===========================================================================
   2.1.4 · DICTADO A PANTALLA COMPLETA (app.js · Dictado)
   ---------------------------------------------------------------------------
   Sam en grande al centro, el estado arriba y lo que vas diciendo abajo.
   Sólo se animan transform y opacity (60 fps en gama media):
     · abrir: un disco del color del papel crece desde el micrófono hasta
       cubrir la pantalla (340 ms, salida suave) y el contenido sube con un
       fundido; Sam entra con un rebote pequeño;
     · escuchando: el halo respira y dos ondas salen despacio (2.8 s);
     · procesando: las ondas se apagan y un arco gira alrededor de Sam;
     · cada palabra nueva entra con un fundido y 0.35em hacia arriba;
     · cerrar: lo mismo al revés, de vuelta al micrófono.
   Con prefers-reduced-motion: sin disco que crece, sin ondas, sin arco ni
   rebote; sólo fundidos y cambios de color. El tono es el de Sam en la barra
   (azul en Pastel, tinta en Sobrio); verde en «Listo», atención en un error.
   Los rellenos teñidos se mezclan contra --tone-canvas, no contra el papel.
   =========================================================================== */
.dictado {
  --dictado-sam: var(--ink-muted);
  --dictado-tono: var(--dictado-sam);
  --dictado-disco: min(148px, 20vh);
  /* Un lienzo apenas teñido del tono de Sam: sobre el papel de la app el disco
     que crece desde el micrófono se ve, y el modo dictado se distingue. */
  --dictado-lienzo: color-mix(in srgb, var(--dictado-sam) 7%, var(--tone-canvas));
  position: fixed;
  inset: 0;
  z-index: 76;
  display: flex;
  justify-content: center;
  overflow: hidden;
  color: var(--ink);
  font-family: var(--font-text);
  -webkit-tap-highlight-color: transparent;
}
.dictado[hidden] { display: none; }
[data-color-mode="color"] .dictado { --dictado-sam: var(--color-blue); }
.dictado[data-state="listo"] { --dictado-tono: var(--ctx-finanzas); }
.dictado[data-state="error"] { --dictado-tono: var(--ctx-atencion); }
/* Ya abierta, el papel lo pone la capa: si la pantalla gira, el disco
   calculado para la orientación anterior podría no cubrirla. */
.dictado.is-settled { background: var(--dictado-lienzo); }

.dictado-bloom {
  position: absolute;
  border-radius: 50%;
  background: var(--dictado-lienzo);
  transform: scale(var(--dictado-s0, .02));
  transition: transform 340ms cubic-bezier(.3, .55, .3, 1);
  will-change: transform;
  pointer-events: none;
}
.dictado.is-open .dictado-bloom { transform: scale(1); }
.dictado.is-closing .dictado-bloom {
  transform: scale(var(--dictado-s0, .02));
  transition: transform 320ms cubic-bezier(.35, 0, .25, 1) 40ms;
}

.dictado-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 40rem;
  overflow-x: hidden;
  overflow-y: auto;   /* último recurso con letra enorme en pantallas bajas */
  height: 100%;
  padding: calc(var(--safe-top) + var(--s8)) calc(env(safe-area-inset-right, 0px) + var(--s5)) calc(var(--safe-bottom) + var(--s5)) calc(env(safe-area-inset-left, 0px) + var(--s5));
  opacity: 0;
  transform: translateY(16px) scale(.985);
  transform-origin: 50% 100%;
  transition: opacity 240ms var(--ease), transform 340ms cubic-bezier(.22, .8, .26, 1);
}
.dictado.is-open .dictado-panel { opacity: 1; transform: none; transition-delay: 90ms, 90ms; }
.dictado.is-closing .dictado-panel {
  opacity: 0;
  transform: translateY(12px) scale(.985);
  transition-duration: 170ms, 220ms;
  transition-delay: 0s, 0s;
}

.dictado-cabeza { flex: none; width: 100%; max-width: 30rem; min-height: 3.25rem; text-align: center; }
.dictado-estado {
  margin: 0;
  font-size: var(--t-title);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
  overflow-wrap: anywhere;
}
.dictado-ayuda { margin: var(--s2) 0 0; font-size: var(--t-body); line-height: 1.45; color: var(--ink-muted); }

/* ---- Sam y sus anillos -------------------------------------------------- */
.dictado-escena {
  position: relative;
  flex: 1 1 auto;
  width: 100%;
  min-height: calc(var(--dictado-disco) + 44px);
  display: grid;
  place-items: center;
}
.dictado-escena > * { grid-area: 1 / 1; }
.dictado-halo, .dictado-anillo { width: var(--dictado-disco); height: var(--dictado-disco); border-radius: 50%; }
.dictado-halo {
  background: color-mix(in srgb, var(--dictado-tono) 15%, var(--tone-canvas));
  transform: scale(1.14);
  transition: background-color .45s var(--ease), transform .45s var(--ease);
}
.dictado-anillo {
  border: 2px solid color-mix(in srgb, var(--dictado-tono) 42%, transparent);
  opacity: 0;
  transition: opacity .35s var(--ease);
}
.dictado[data-state="escuchando"] .dictado-halo,
.dictado[data-state="oyendo"] .dictado-halo { animation: dictado-respira 2.8s ease-in-out infinite; }
.dictado[data-state="escuchando"] .dictado-anillo,
.dictado[data-state="oyendo"] .dictado-anillo { animation: dictado-onda 2.8s cubic-bezier(.33, .55, .45, 1) infinite; }
.dictado[data-state] .dictado-anillo-2 { animation-delay: 1.4s; }
.dictado[data-state="oyendo"] .dictado-anillo { border-color: color-mix(in srgb, var(--dictado-tono) 58%, transparent); }

.dictado-orbita {
  width: calc(var(--dictado-disco) + 24px);
  height: calc(var(--dictado-disco) + 24px);
  fill: none;
  stroke: var(--dictado-tono);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-dasharray: 20 80;
  opacity: 0;
  transition: opacity .3s var(--ease);
}
.dictado[data-state="procesando"] .dictado-orbita { opacity: 1; animation: dictado-gira 1.2s linear infinite; }

.dictado-disco {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--dictado-disco);
  height: var(--dictado-disco);
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--dictado-tono) 38%, var(--line));
  box-shadow: var(--shadow-lift);
  transition: border-color .45s var(--ease), background-color .45s var(--ease);
}
[data-color-mode="color"] .dictado-disco { background: color-mix(in srgb, var(--dictado-tono) 30%, var(--tone-canvas)); }
.dictado-disco .sam-mark { width: 60%; height: 60%; background: var(--ink); }
.dictado.is-open .dictado-disco { animation: dictado-rebote 560ms cubic-bezier(.34, 1.5, .64, 1) 110ms both; }

.dictado-mic {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--action);
  color: var(--action-ink);
  box-shadow: var(--shadow-lift);
  transform: translate(calc(var(--dictado-disco) * .36), calc(var(--dictado-disco) * .36));
}
.dictado-mic svg { width: 20px; height: 20px; }
.dictado.is-open .dictado-mic { animation: dictado-mic-entra 420ms cubic-bezier(.34, 1.5, .64, 1) 260ms both; }

/* ---- Lo que vas diciendo ------------------------------------------------ */
/* Alto FIJO y el texto pegado abajo, como subtítulos que crecen hacia
   arriba: así Sam no se mueve de sitio cada vez que la frase gana un renglón
   (animar ese salto sería animar layout). `margin-top: auto` y no
   `justify-content: flex-end`, que dejaría sin desplazamiento lo que se sale
   por arriba. */
.dictado-texto {
  flex: 0 1 auto;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 34vh;
  height: 34dvh;
  min-height: 0;
  margin-bottom: var(--s5);
  padding: var(--s3) var(--s2) var(--s1);
  overflow-y: auto;
  overscroll-behavior: contain;
  text-align: center;
  scrollbar-width: thin;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--s3));
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--s3));
}
.dictado-pista {
  max-width: 26rem;
  margin: auto auto 0;
  font-size: var(--t-body);
  line-height: 1.5;
  color: var(--ink-muted);
}
.dictado.has-texto .dictado-pista { display: none; }
/* En un error sin nada dicho, la pista sobra: el espacio es para el mensaje. */
.dictado[data-state="error"]:not(.has-texto) .dictado-texto { display: none; }
.dictado.has-texto .dictado-frase { margin-top: auto; }
.dictado-frase {
  flex: none;
  margin: 0;
  font-size: 1.625rem;
  font-weight: 600;
  line-height: 1.38;
  overflow-wrap: anywhere;
}
.dictado-palabra { display: inline-block; max-width: 100%; color: var(--ink-muted); font-weight: 500; transition: color .3s var(--ease); }
.dictado-palabra.is-fuerte { color: var(--ink-strong); font-weight: 600; }
.dictado-palabra.is-nueva { animation: dictado-palabra 300ms cubic-bezier(.22, .8, .26, 1) both; }

/* ---- Botones ------------------------------------------------------------ */
.dictado-acciones {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  width: 100%;
  max-width: 26rem;
}
.dictado-btn {
  flex: 1 1 9rem;
  min-height: 52px;
  padding: var(--s2) var(--s5);
  font-weight: 600;
  white-space: normal;
  line-height: 1.25;
}
.dictado-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
.dictado-btn:disabled { opacity: .6; cursor: default; }

@keyframes dictado-respira { 0%, 100% { transform: scale(1.1); } 50% { transform: scale(1.24); } }
@keyframes dictado-onda { 0% { transform: scale(1); opacity: 0; } 18% { opacity: .75; } 100% { transform: scale(1.8); opacity: 0; } }
@keyframes dictado-gira { to { transform: rotate(360deg); } }
@keyframes dictado-rebote { 0% { transform: scale(.6); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
@keyframes dictado-mic-entra { 0% { opacity: 0; transform: translate(calc(var(--dictado-disco) * .36), calc(var(--dictado-disco) * .36)) scale(.4); } 100% { opacity: 1; transform: translate(calc(var(--dictado-disco) * .36), calc(var(--dictado-disco) * .36)) scale(1); } }
@keyframes dictado-palabra { from { opacity: 0; transform: translateY(.35em); } to { opacity: 1; transform: none; } }
@keyframes dictado-fundido { from { opacity: 0; } to { opacity: 1; } }

/* Escritorio y tabletas: más aire y letra más grande. */
@media (min-width: 700px) and (min-height: 620px) {
  .dictado { --dictado-disco: 168px; }
  .dictado-panel { padding-top: calc(var(--safe-top) + var(--s9) + var(--s4)); }
  .dictado-frase { font-size: 2rem; }
}
/* Poca altura (teléfono acostado, letra grande): Sam se achica antes que el texto. */
@media (max-height: 700px) {
  .dictado { --dictado-disco: min(120px, 17vh); }
  .dictado-panel { padding-top: calc(var(--safe-top) + var(--s6)); }
  .dictado-escena { min-height: calc(var(--dictado-disco) + 32px); }
  .dictado-texto { margin-bottom: var(--s3); }
}
@media (max-height: 620px) {
  .dictado { --dictado-disco: 96px; }
  .dictado-panel { padding-top: calc(var(--safe-top) + var(--s5)); }
  .dictado-texto { height: 30vh; height: 30dvh; margin-bottom: var(--s3); }
  .dictado-mic { width: 32px; height: 32px; }
  .dictado-mic svg { width: 16px; height: 16px; }
}

/* Sin movimiento: sólo fundidos y color. Los selectores llevan la capa para
   ganarle al `*` global de reduced-motion, que deja todo en .01 ms. */
@media (prefers-reduced-motion: reduce) {
  .dictado { opacity: 0; transition: opacity 220ms ease !important; }
  .dictado.is-open { opacity: 1; }
  .dictado.is-closing { opacity: 0; transition-duration: 200ms !important; }
  .dictado .dictado-bloom { transform: none !important; transition: none !important; }
  .dictado .dictado-panel { transform: none !important; transition: opacity 220ms ease !important; }
  .dictado .dictado-halo,
  .dictado .dictado-anillo,
  .dictado .dictado-orbita,
  .dictado .dictado-disco,
  .dictado .dictado-mic { animation: none !important; }
  .dictado .dictado-halo { transform: scale(1.14) !important; transition: background-color .45s ease !important; }
  .dictado .dictado-disco { transition: background-color .45s ease, border-color .45s ease !important; }
  .dictado .dictado-anillo { display: none; }
  .dictado[data-state="procesando"] .dictado-orbita { opacity: 1; stroke-dasharray: none; stroke-opacity: .5; }
  .dictado .dictado-palabra { transition: color .3s ease !important; }
  .dictado .dictado-palabra.is-nueva { animation: dictado-fundido 200ms ease both !important; }
}

/* ===========================================================================
   2.1.4 · TARJETAS DENTRO DEL DICTADO (app.js · Dictado, «TARJETAS»)
   ---------------------------------------------------------------------------
   «Listo» envía y la capa sigue abierta:
     · pensando: lo que dijiste arriba, en su burbuja; Sam al centro con la
       órbita girando despacio y el halo latiendo; «Sam lo está acomodando…»;
     · tarjetas: Sam se encoge y sube a su sitio junto a su respuesta (FLIP en
       JS) y las tarjetas del chat entran desde abajo, una tras otra, con un
       fundido y un resorte leve (retraso por tarjeta en línea, desde JS);
     · hecha / descartada: la tarjeta repintada late una vez y su icono de
       estado entra con un rebote; la lista se reacomoda con FLIP;
     · resuelto: «Listo» con una barra que se vacía antes del cierre solo.
   Sólo transform y opacity. Las tarjetas son las del hilo: aquí sólo se
   ajusta su sitio en la capa, nunca su aspecto.
   Las hojas (Premium, «Agregar método») y el «¿Eliminar?» se abren ENCIMA de
   la capa: la capa baja a 69, justo debajo de los velos (70).
   =========================================================================== */
.dictado { z-index: 69; }

/* Sam es una pieza que se muda: del centro de la escena al hueco del avatar. */
.dictado-sam {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--dictado-disco);
  height: var(--dictado-disco);
  /* Pista del tamaño del disco: la órbita y el halo, más grandes, se
     centran desbordando por igual y no empujan a Sam a un lado. */
  grid-template: 100% / 100%;
}
.dictado-sam > * { grid-area: 1 / 1; }

/* ---- «Editar antes de enviar» ------------------------------------------ */
.dictado-btn[hidden], .dictado-editar[hidden] { display: none; }
.dictado-listo { position: relative; overflow: hidden; }
.dictado-editar {
  flex: 1 1 100%;
  min-height: 44px;
  margin-top: calc(var(--s2) * -1);
  padding: var(--s2) var(--s3);
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--ink-muted);
  font: inherit;
  font-size: var(--t-body);
  font-weight: 500;
  line-height: 1.3;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
  text-underline-offset: 3px;
  cursor: pointer;
  transition: opacity .25s var(--ease);
}
.dictado-editar:hover { color: var(--ink); }
.dictado-editar:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
/* Sin nada dicho no hay qué editar; el hueco se guarda para que Sam no
   brinque cuando llega la primera palabra. */
.dictado:not(.has-texto) .dictado-editar { opacity: 0; pointer-events: none; }

.dictado-anuncio {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---- La conversación ---------------------------------------------------- */
.dictado-conversa {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  width: 100%;
  max-width: 34rem;
}
.dictado-conversa[hidden] { display: none; }
.dictado.is-conversa .dictado-texto { display: none; }
.dictado.is-conversa .dictado-conversa { order: 0; }
.dictado.is-conversa .dictado-escena { order: 1; }
.dictado.is-conversa .dictado-cabeza { order: 2; min-height: 0; margin-bottom: var(--s5); }
.dictado.is-conversa .dictado-acciones { order: 3; }
.dictado.is-conversa .dictado-panel { padding-top: calc(var(--safe-top) + var(--s6)); }

.dictado-yo { align-self: flex-end; max-width: 84%; }
.dictado-yo .msg-text { overflow-wrap: anywhere; }

/* Pensando: la burbuja arriba (sin crecer más de cuatro renglones), Sam al
   centro y el estado debajo de él. */
.dictado[data-state="pensando"] .dictado-conversa { flex: none; }
.dictado[data-state="pensando"] .dictado-yo .msg-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}
.dictado[data-state="pensando"] .dictado-estado { color: var(--ink-muted); font-weight: 500; }
/* Sam y «Sam lo está acomodando…» juntos, al centro del hueco que queda. */
.dictado[data-state="pensando"] .dictado-escena { flex: 0 0 auto; margin-top: auto; }
.dictado[data-state="pensando"] .dictado-cabeza { margin: var(--s5) 0 auto; padding-bottom: var(--s5); }
.dictado[data-state="pensando"] .dictado-anillo { opacity: 0; animation: none; }
.dictado[data-state="pensando"] .dictado-halo { animation: dictado-late 1.9s ease-in-out infinite; }
.dictado[data-state="pensando"] .dictado-orbita {
  opacity: 1;
  stroke-dasharray: 16 34;
  stroke-width: 2;
  animation: dictado-gira 2.6s linear infinite;
}
.dictado.is-conversa .dictado-mic { animation: dictado-mic-sale 200ms var(--ease) both; }

/* Con respuesta: Sam vive en su avatar, la conversación desliza y el pie
   (siempre con una forma de cerrar) queda fijo abajo. */
.dictado.is-conversa:not([data-state="pensando"]) .dictado-panel { overflow: hidden; }
.dictado.is-conversa:not([data-state="pensando"]) .dictado-escena,
.dictado.is-conversa:not([data-state="pensando"]) .dictado-cabeza { display: none; }
.dictado.is-conversa:not([data-state="pensando"]) .dictado-conversa {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  margin: 0 calc(var(--s2) * -1);
  padding: var(--s2) var(--s2) var(--s6);
  width: calc(100% + var(--s2) * 2);
  max-width: calc(34rem + var(--s2) * 2);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--s3));
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--s3));
}

.dictado-fila-sam { display: flex; align-items: flex-start; gap: var(--s3); }
.dictado-fila-sam[hidden] { display: none; }
.dictado-avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
}
.dictado-avatar .dictado-sam { --dictado-disco: 44px; }
.dictado-avatar .dictado-halo,
.dictado-avatar .dictado-anillo,
.dictado-avatar .dictado-orbita { opacity: 0; animation: none; transition: opacity .25s var(--ease); }
.dictado-avatar .dictado-mic { display: none; }
.dictado-avatar .dictado-disco { box-shadow: var(--shadow-card); }
.dictado.is-open .dictado-avatar .dictado-disco { animation: none; }
.dictado-dice { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.dictado-dice > .msg { max-width: 100%; align-self: flex-start; }
.dictado-dice > .msg-group { max-width: 100%; width: 100%; }
/* La respuesta que acompaña a tarjetas es corta: el texto largo sigue
   entero en el chat. */
.dictado[data-state="tarjetas"] .dictado-dice .msg-text,
.dictado[data-state="resuelto"] .dictado-dice .msg-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  overflow: hidden;
}
.dictado.es-error { --dictado-tono: var(--ctx-atencion); }

.dictado-grupo { margin: var(--s2) 0 0; }
.dictado-grupo[hidden] { display: none; }
.dictado-tarjetas { display: flex; flex-direction: column; gap: var(--s3); }
.dictado-tarjeta { border-radius: var(--r-lg); }
.dictado-tarjeta:focus { outline: none; }
.dictado-tarjeta:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
.dictado-tarjeta > .proposal { margin-top: 0; }
.dictado-rechazos:empty { display: none; }
.dictado-rechazos .msg-rejected:first-child { margin-top: 0; }

/* ---- Pie ---------------------------------------------------------------- */
.dictado.is-conversa .dictado-acciones { position: relative; max-width: 34rem; }
.dictado.is-conversa:not([data-state="pensando"]) .dictado-acciones { padding-top: var(--s3); }
.dictado.is-conversa .dictado-acciones::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: calc(var(--s5) * -1);
  height: var(--s5);
  background: linear-gradient(to top, var(--dictado-lienzo), transparent);
  pointer-events: none;
}
.dictado[data-state="pensando"] .dictado-acciones::before { display: none; }
.dictado-cuenta {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: currentColor;
  opacity: 0;
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}
.dictado.con-cuenta .dictado-cuenta { opacity: .45; animation: dictado-cuenta 2600ms linear both; }

/* ---- Entradas y estados ------------------------------------------------- */
.dictado-entra { animation: dictado-entra 560ms cubic-bezier(.3, 1.3, .5, 1) both; }
.dictado-tarjeta.is-hecha > .proposal { animation: dictado-hecha 520ms cubic-bezier(.3, 1.35, .5, 1) both; }
.dictado-tarjeta.is-hecha .prop-icon-state { position: relative; animation: dictado-estado-icono 620ms cubic-bezier(.34, 1.7, .5, 1) 80ms both; }
.dictado-tarjeta.is-hecha .prop-icon-state::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--ctx-finanzas) 55%, transparent);
  opacity: 0;
  animation: dictado-onda-hecha 760ms cubic-bezier(.2, .7, .3, 1) 160ms both;
  pointer-events: none;
}
.dictado-tarjeta.is-hecha .prop-done { animation: dictado-sube 360ms var(--ease) 200ms both; }
.dictado-tarjeta.is-descartada > .proposal { animation: dictado-descartada 420ms cubic-bezier(.22, .8, .26, 1) both; }
.dictado-tarjeta.is-descartada .prop-icon-state { animation: dictado-estado-icono 460ms cubic-bezier(.34, 1.5, .5, 1) 60ms both; }

@keyframes dictado-late { 0%, 100% { transform: scale(1.1); opacity: .85; } 50% { transform: scale(1.2); opacity: 1; } }
@keyframes dictado-mic-sale { from { opacity: 1; transform: translate(calc(var(--dictado-disco) * .36), calc(var(--dictado-disco) * .36)) scale(1); } to { opacity: 0; transform: translate(calc(var(--dictado-disco) * .36), calc(var(--dictado-disco) * .36)) scale(.4); } }
@keyframes dictado-entra { 0% { opacity: 0; transform: translateY(26px) scale(.965); } 55% { opacity: 1; } 100% { opacity: 1; transform: none; } }
@keyframes dictado-hecha { 0% { opacity: .6; transform: scale(.975); } 100% { opacity: 1; transform: none; } }
@keyframes dictado-descartada { 0% { opacity: .3; transform: scale(.985); } 100% { opacity: 1; transform: none; } }
@keyframes dictado-estado-icono { 0% { opacity: 0; transform: scale(.25) rotate(-25deg); } 100% { opacity: 1; transform: none; } }
@keyframes dictado-onda-hecha { 0% { opacity: .9; transform: scale(.85); } 100% { opacity: 0; transform: scale(2); } }
@keyframes dictado-sube { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes dictado-cuenta { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* Escritorio: un poco más de aire arriba. */
@media (min-width: 700px) and (min-height: 620px) {
  .dictado.is-conversa .dictado-panel { padding-top: calc(var(--safe-top) + var(--s8)); }
}
/* Pantallas bajas: el enlace de editar pega más a los botones. */
@media (max-height: 700px) {
  .dictado-editar { min-height: 40px; padding-top: var(--s1); padding-bottom: var(--s1); }
}

/* Sin movimiento: sólo fundidos (también escalonados). */
@media (prefers-reduced-motion: reduce) {
  /* Por partes y no con el atajo `animation`: el atajo borraría el retraso
     de cada tarjeta, y los fundidos también van escalonados. */
  .dictado .dictado-entra,
  .dictado .dictado-tarjeta.is-hecha > .proposal,
  .dictado .dictado-tarjeta.is-descartada > .proposal {
    animation-name: dictado-fundido !important;
    animation-duration: 240ms !important;
    animation-timing-function: ease !important;
  }
  .dictado .dictado-tarjeta .prop-icon-state,
  .dictado .dictado-tarjeta .prop-done { animation: none !important; }
  .dictado .dictado-tarjeta .prop-icon-state::after { display: none; }
  .dictado[data-state="pensando"] .dictado-orbita { animation: none !important; stroke-dasharray: none; stroke-opacity: .5; }
  .dictado.is-conversa .dictado-mic { opacity: 0; }
  .dictado .dictado-cuenta { display: none; }
  .dictado .dictado-editar { transition: none !important; }
}

/* ===========================================================================
   2.1.5 · EL TÍTULO DE LA LISTA SE TOCA PARA CAMBIARLE EL NOMBRE
   ========================================================================= */
/* En línea, como texto: el nombre se parte en renglones igual que antes y el
   lápiz (y el punto de color de la sección) quedan tras la última palabra.
   El relleno vertical de un elemento en línea no mueve nada, sólo agranda la
   zona de toque a 44 px. */
.list-title-btn {
  display: inline; margin: 0; padding: 9px 0;
  border: 0; background: none; color: inherit; font: inherit;
  letter-spacing: inherit; line-height: inherit; text-align: left; cursor: pointer;
  overflow-wrap: anywhere;
  -webkit-tap-highlight-color: transparent;
}
.list-title-btn .i { display: inline; width: auto; height: auto; }
.list-title-btn svg {
  display: inline-block; width: 17px; height: 17px; margin-left: 8px; vertical-align: -1px;
  fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  opacity: .55;
}
.list-title-btn:active { opacity: .6; }
.list-title-btn:focus-visible { outline: 2px solid var(--focus, currentColor); outline-offset: 4px; border-radius: 8px; }
/* El lápiz ocupa el lugar del punto de color de la sección: con los dos, el
   punto se caía solo a un renglón propio. */
[data-color-mode="color"] #screen-lists .screen-title.list-title::after { content: none; }
[data-color-mode="color"] #screen-lists .list-title-btn svg { color: var(--area-lists-ink); opacity: .9; }

/* ==========================================================================
   2.2.0 · Probar sin cuenta y «Continuar con Google» en la puerta de acceso.
   [hidden] no gana contra el display de .btn: sin esta regla los botones que
   esperan la respuesta del servidor se verían desde el primer momento.
   ========================================================================== */
.auth-gate [hidden] { display: none !important; }
.auth-google { display: flex; justify-content: center; min-height: 44px; margin-bottom: var(--s3); color-scheme: light; }
.auth-or { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); font-size: 0.8125rem; color: var(--ink-muted); }
.auth-or::before, .auth-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.auth-guest { margin-top: var(--s3); }
.auth-upgrade { margin-bottom: var(--s5); }
.auth-upgrade strong { display: block; font-family: var(--font-display); font-size: 20px; font-weight: 500; color: var(--ink-strong); }
.auth-upgrade p { margin: var(--s2) 0 0; font-size: 0.875rem; line-height: 1.45; color: var(--ink-muted); }
