/* ======================================================
   COMPONENTES REUTILIZABLES (Paso 18 · refinamiento)
   ======================================================
   Pocos y con propósito. La jerarquía la hace la tipografía,
   el espacio y el color funcional, no una card por elemento. */

/* --- Título de pantalla --- */
.screen-title {
    font-size: var(--text-xl);
    font-weight: 750;
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-tight);
}
.screen-sub { color: var(--color-muted); font-size: var(--text-sm); margin-top: var(--space-1); }

.head-bar {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3); margin-bottom: var(--space-5);
}
.section { margin-top: var(--space-6); }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-3); }
.section-head h2 { font-size: var(--text-lg); font-weight: 700; letter-spacing: var(--tracking-tight); }

.group-label {
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--color-muted);
    margin: var(--space-5) 0 var(--space-2);
}
.group-label:first-child { margin-top: 0; }

/* --- Botones --- */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
    min-height: 44px; padding: 0 var(--space-4);
    border-radius: var(--radius-sm);
    font-weight: 650; font-size: var(--text-sm);
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}
.btn svg { width: 18px; height: 18px; }
.btn-primary { background: var(--color-primary); color: #fff; }
.btn-primary:hover:not([disabled]) { background: var(--color-primary-strong); }
.btn-ghost { border: 1px solid var(--color-border-strong); color: var(--color-text); background: var(--color-surface); }
.btn-ghost:hover:not([disabled]) { border-color: var(--color-primary-light); color: var(--color-primary-strong); }
.btn-danger { border: 1px solid var(--color-danger); color: var(--color-danger); background: var(--color-surface); }
.btn-danger:hover:not([disabled]) { background: var(--color-danger-soft); }
.btn-block { display: flex; width: 100%; }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }

.link-btn { color: var(--color-primary); font-weight: 650; font-size: var(--text-sm); padding: var(--space-1) 0; }
.link-btn:hover { color: var(--color-primary-strong); }

/* --- Segmented (filtros de estado / tipo) ---
   Píldora compacta cuando las opciones caben. Si NO caben (p. ej. las 4 de
   Metas "Todas/Activas/Pausadas/Cumplidas" a 320 px), la píldora se limita
   al ancho disponible (`max-width: 100%`) y el scroll es SÓLO interno: la
   página nunca se desplaza horizontalmente. Barra de scroll oculta; los
   botones conservan su tamaño táctil (`flex: 0 0 auto`, no se aplastan). */
.segmented {
    display: inline-flex;
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;      /* Firefox */
    -ms-overflow-style: none;   /* Edge/IE antiguos */
    background: var(--color-surface-2);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 4px;
    gap: 2px;
}
.segmented::-webkit-scrollbar { display: none; }   /* WebKit / Chrome */
.segmented button {
    flex: 0 0 auto;
    white-space: nowrap;
    min-height: 36px; padding: 0 var(--space-4);
    border-radius: 999px;
    font-size: var(--text-sm); font-weight: 650;
    color: var(--color-muted);
    transition: color var(--transition), background var(--transition);
}
.segmented button[aria-pressed="true"] { background: var(--color-primary); color: #fff; }

/* --- Select refinado --- */
.select-native {
    -webkit-appearance: none; appearance: none;
    width: 100%; min-height: 46px;
    padding: 0 2.25rem 0 var(--space-3);
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm); font-weight: 600;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
}
.select-native:focus { border-color: var(--color-primary); }

/* --- Bloque de contenido (con moderación) --- */
.panel {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-4);
}

/* --- Lista de filas --- */
.rows { border-top: 1px solid var(--color-border); }
.row {
    display: flex; align-items: center; gap: var(--space-3);
    width: 100%; min-height: 56px;
    padding: var(--space-3) 0;
    text-align: left;
    border-bottom: 1px solid var(--color-border);
}
button.row { transition: background var(--transition); }
button.row:hover { background: var(--color-primary-tint); }
.row-main { flex: 1; min-width: 0; }
.row-title { font-weight: 600; overflow-wrap: anywhere; }
.row-sub { font-size: var(--text-xs); color: var(--color-muted); margin-top: 1px; }
.row-amount { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Paso 23.1: una fila anulada se atenúa; el monto tachado deja claro que no cuenta. */
.row.is-voided { opacity: 0.72; }
.row.is-voided .row-amount { text-decoration: line-through; color: var(--color-muted); }
/* Fase 6: fila de movimiento de extracto con checkbox de inclusión (nuevo/
   ambiguo) -- reutiliza `.row.is-voided` (arriba) para "probable_duplicate"
   bloqueado, sin checkbox. */
.row input[type="checkbox"] { width: 20px; height: 20px; min-height: 0; flex: 0 0 auto; }
.row-value { font-size: var(--text-sm); color: var(--color-muted); text-align: right; max-width: 55%; }
.amount-in { color: var(--color-income); }
.amount-out { color: var(--color-text); }
.row-chevron { color: var(--color-faint); flex-shrink: 0; display: flex; }

/* --- Marcador de icono (categoría, meta) --- */
.imark {
    width: 34px; height: 34px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    border-radius: 10px;
    background: var(--color-surface-2);
    color: var(--color-muted);
}
.imark svg { width: 18px; height: 18px; }
.imark.accent { background: var(--color-primary-soft); color: var(--color-primary-strong); }
.imark.income { background: var(--color-income-soft); color: var(--color-income); }
.imark.expense { background: var(--color-expense-soft); color: var(--color-expense); }

/* --- Barras horizontales de categoría --- */
.bars { display: flex; flex-direction: column; gap: var(--space-4); }
.bar-item { display: grid; grid-template-columns: auto 1fr auto; gap: var(--space-1) var(--space-3); align-items: center; }
.bar-name { font-weight: 600; }
.bar-value { font-variant-numeric: tabular-nums; font-weight: 650; }
.bar-track { grid-column: 2 / -1; height: 7px; border-radius: 999px; background: var(--color-surface-2); overflow: hidden; }
/* Ancho data-driven vía custom property (etapa 2: antes `style="width:X%"`
   inline). Sin `--bar-w` -> 0% (mismo resultado que el antiguo width:0%). */
.bar-fill { display: block; height: 100%; width: var(--bar-w, 0%); background: var(--color-primary); border-radius: 999px; transition: width var(--transition); }
.bar-pct { grid-column: 2 / -1; font-size: var(--text-xs); color: var(--color-faint); }

/* --- Meta / Tarjeta --- */
/* `.goal`/`.card-row` son siempre un <button> que actúa como fila (F6: antes
   llevaba `style="display:block;width:100%;text-align:left"` inline).
   `.card-row` (Fase 4, pantalla Tarjetas) comparte EXACTAMENTE el mismo
   patrón fila-botón + barra de progreso que `.goal` -- misma idea visual
   (una barra que resume "cuánto" de un total), sólo que la barra representa
   cupo USADO en vez de ahorro logrado (ver `.card-row--over` más abajo para
   el único color que sí difiere). */
.goal, .card-row { padding: var(--space-4) 0; border-bottom: 1px solid var(--color-border); display: block; width: 100%; text-align: left; }
.goal:last-child, .card-row:last-child { border-bottom: none; }
.goal-head, .card-head { display: flex; align-items: center; gap: var(--space-3); }
.goal-head .goal-name, .card-head .card-name { flex: 1; font-weight: 700; font-size: var(--text-base); letter-spacing: var(--tracking-tight); overflow-wrap: anywhere; }
.goal-amounts, .card-amounts { font-variant-numeric: tabular-nums; margin: var(--space-2) 0 var(--space-2); font-weight: 600; }
.goal-amounts .of, .card-amounts .of { color: var(--color-muted); font-weight: 500; }
.goal-amounts-lg { font-size: var(--text-lg); }           /* F10: detalle de meta */
.goal-meta, .card-meta { font-size: var(--text-xs); color: var(--color-muted); margin-top: var(--space-2); }
.goal .bar-track, .card-row .bar-track { height: 8px; }
.bar-track.bar-track-goal { height: 8px; }                 /* F11: bar-track del detalle (fuera de `.goal`) */
/* Tarjeta con deuda por encima del cupo configurado: la barra se llena al
   100% (clamp, ver `filaTarjeta` en tarjetas.js) y cambia a rojo -- mismo
   token `--color-danger` que `.btn-danger`. */
.card-row--over .bar-fill { background: var(--color-danger); }

/* Bloque de cifras clave del detalle de una tarjeta (deuda / cupo / corte):
   mismo `.panel` que el resto de bloques de contenido, en una grilla de 3
   columnas que se apila a un ancho de teléfono estrecho. */
.card-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); margin: var(--space-4) 0; }
.card-stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.card-stat-label { font-size: var(--text-xs); color: var(--color-muted); }
.card-stat-value { font-weight: 700; overflow-wrap: anywhere; }
@media (max-width: 380px) {
    .card-stats { grid-template-columns: 1fr; }
}

/* Fila de dos botones dentro de una sub-vista de sheet (F14/F15/F16/F21:
   antes `style="display:flex;gap:.5rem"` inline). */
.sheet-btn-row { display: flex; gap: .5rem; }

/* Etiqueta de estado discreta (no botón, no sólo color) --- */
.state-tag {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: var(--text-xs); font-weight: 650;
    color: var(--color-muted);
    white-space: nowrap;
}
.state-tag::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.state-tag.activa   { color: var(--color-primary); }
.state-tag.cumplida { color: var(--color-income); }
.state-tag.pausada  { color: var(--color-faint); }
.state-tag.anulado  { color: var(--color-faint); }   /* Paso 23.1: indicador sobrio */
.state-tag.revision { color: var(--color-faint); }   /* Fase 6: "requiere revisión / no se importará" */

/* --- Formularios --- */
.field { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.field label, .field-heading { font-weight: 650; font-size: var(--text-sm); }
.field input, .field select, .field textarea {
    min-height: 46px; padding: var(--space-2) var(--space-3);
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
}
.field textarea { min-height: 110px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--color-primary); }
.field .hint { font-size: var(--text-xs); color: var(--color-muted); }
.field-error { color: var(--color-danger); font-size: var(--text-sm); }

/* Checkbox de aceptación (consentimiento legal, Paso 25.2) */
.field-check {
    display: flex; align-items: flex-start; gap: var(--space-2);
    margin-bottom: var(--space-4); font-size: var(--text-sm); font-weight: 400;
    line-height: 1.4;
}
.field-check input[type="checkbox"] {
    width: 20px; height: 20px; min-height: 0; margin-top: 2px; flex: 0 0 auto;
}
.field-check a { color: var(--color-primary); text-decoration: underline; }

/* Páginas de documentos legales (#/terminos, #/privacidad) */
.legal-card .legal-h { font-size: var(--text-sm); font-weight: 650; margin: var(--space-4) 0 var(--space-1); }
.legal-card p { font-size: var(--text-sm); color: var(--color-muted); margin: 0 0 var(--space-2); }
.legal-card .legal-doc-titulo { font-weight: 650; color: var(--color-text); }
.legal-card .legal-list { margin: 0 0 var(--space-2); padding-left: var(--space-4); }
.legal-card .legal-list li { font-size: var(--text-sm); color: var(--color-muted); margin-bottom: var(--space-1); }
.input-prefix { display: flex; align-items: stretch; border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); overflow: hidden; background: var(--color-surface); }
.input-prefix span { display: flex; align-items: center; padding: 0 var(--space-3); background: var(--color-surface-2); color: var(--color-muted); font-weight: 650; border-right: 1px solid var(--color-border); }
.input-prefix input { border: none; flex: 1; }

/* --- Toggle --- */
.toggle-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); min-height: 52px; padding: var(--space-2) 0; border-bottom: 1px solid var(--color-border); }
.toggle-row:last-child { border-bottom: none; }
.switch { position: relative; width: 46px; height: 28px; flex-shrink: 0; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch .track { position: absolute; inset: 0; background: var(--color-border-strong); border-radius: 999px; transition: background var(--transition); }
.switch .track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; background: #fff; border-radius: 50%; transition: transform var(--transition); box-shadow: 0 1px 2px rgba(0,0,0,.2); }
.switch input:checked + .track { background: var(--color-primary); }
.switch input:checked + .track::after { transform: translateX(18px); }
.switch input:focus-visible + .track { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* --- Estados --- */
/* Columna centrada: cualquier combinación de mascota + título + texto + acción
   se apila con un ritmo consistente. Antes la acción (p. ej. "Nueva meta" en
   Metas vacías) quedaba pegada al texto porque era un inline-flex sin salto. */
.state {
    padding: var(--space-8) var(--space-4);
    text-align: center;
    color: var(--color-muted);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
}
.state strong { color: var(--color-text); font-size: var(--text-lg); font-weight: 700; }
.state .btn { margin-top: var(--space-3); }

.skeleton { background: var(--color-surface-2); border-radius: var(--radius-sm); animation: sk 1.2s ease-in-out infinite alternate; }
@keyframes sk { from { opacity: 0.6; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }
.sk-line { height: 14px; margin: var(--space-2) 0; }
.sk-line.w-40 { width: 40%; }
.sk-block { height: 60px; margin: var(--space-3) 0; }

/* --- Acordeón FAQ --- */
.faq details { border-bottom: 1px solid var(--color-border); }
.faq summary { padding: var(--space-4) 0; font-weight: 600; cursor: pointer; list-style: none; display: flex; justify-content: space-between; gap: var(--space-3); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--color-muted); font-weight: 700; }
.faq details[open] summary::after { content: "\2212"; }
.faq p { padding-bottom: var(--space-4); color: var(--color-muted); }

/* --- Aviso contextual (discreto) --- */
/* Rebranding GenkIA: acento fucsia a la izquierda (identidad/énfasis); sigue
   siendo un aviso discreto, sin fondo ni caja. */
.note {
    font-size: var(--text-xs); color: var(--color-muted);
    border-left: 2px solid var(--color-primary);
    padding-left: var(--space-3);
    line-height: 1.45;
}
.note-danger { border-color: var(--color-danger); color: var(--color-text); }

/* --- Sheet / modal (componente global reutilizable) ---
   Se comporta como MODAL CENTRADO (vertical y horizontalmente) en todos los
   tamaños. Márgenes de seguridad alrededor; si el contenido es más alto que
   el viewista, el panel se acota y el SCROLL es interno (la cabecera con la
   "×" queda fija). */
.sheet-backdrop {
    position: fixed; inset: 0;
    /* Alto real (auditoría "Grupos -- sheets móviles/layout", puntos 1-5):
       `100vh` primero (compatibilidad universal), `100dvh` después --
       iOS Safari ya reduce `dvh` cuando aparece el teclado, y en Android
       el meta `interactive-widget=resizes-content` (index.html) hace que
       el propio layout viewport (y por lo tanto `dvh`) se encoja igual,
       sin necesidad de JS. La declaración VÁLIDA que quede última gana. */
    height: 100vh;
    height: 100dvh;
    background: rgba(15, 23, 42, .42);
    display: flex;
    align-items: center;          /* centrado vertical */
    justify-content: center;      /* centrado horizontal */
    z-index: 50;
    /* Márgenes de seguridad: verticales fijos, laterales por breakpoint.  */
    padding: 16px 12px;
    /* Respeta notch / barra de gestos si el contenedor no lo hace ya.     */
    padding-block: max(16px, env(safe-area-inset-top, 0px)) max(16px, env(safe-area-inset-bottom, 0px));
}
@media (max-width: 340px) { .sheet-backdrop { padding-inline: 6px; } }   /* 320: prioriza ancho */
@media (min-width: 400px) { .sheet-backdrop { padding-inline: 20px; } }  /* móvil mediano/grande */
@media (min-width: 480px) { .sheet-backdrop { padding-inline: 24px; } }  /* desktop: más aire lateral */

.sheet {
    background: var(--color-surface);
    width: 100%;
    max-width: 440px;
    border-radius: var(--radius-lg);
    padding: var(--space-4) var(--space-4) var(--space-6);
    /* No supera la altura disponible: internamente hace scroll. Misma
       cadena de fallback que `.sheet-backdrop` (arriba). */
    max-height: calc(100vh - 32px);
    max-height: calc(100dvh - 32px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    box-shadow: 0 12px 40px rgba(15, 23, 42, .18);
    animation: modal-in var(--transition);
}
@media (min-width: 480px) { .sheet { max-width: 400px; } }
@keyframes modal-in { from { transform: scale(.985); opacity: .6; } to { transform: none; opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .sheet { animation: none; } }

/* Cabecera: título a la izquierda, "×" reservando su espacio a la derecha.
   Queda FIJA arriba mientras el contenido del modal hace scroll. */
.sheet-header {
    position: sticky;
    top: calc(-1 * var(--space-4));
    z-index: 1;
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--space-3);
    margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-4)) var(--space-4);
    padding: var(--space-4) var(--space-4) var(--space-3);
    background: var(--color-surface);
}
.sheet-title {
    font-size: var(--text-lg); font-weight: 750;
    letter-spacing: var(--tracking-tight);
    line-height: var(--lh-tight);
    margin: 0; flex: 1; min-width: 0;
    overflow-wrap: anywhere;
    padding-top: 6px;
}
.sheet-x {
    flex-shrink: 0;
    width: 44px; height: 44px;
    margin: -8px -10px 0 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.6rem; line-height: 1;
    color: var(--color-muted);
    border-radius: 999px;
    transition: background var(--transition), color var(--transition);
}
.sheet-x:hover { background: var(--color-surface-2); color: var(--color-text); }
/* Un .sheet-title dentro del contenido (sub-vistas) no lleva el padding de cabecera. */
.sheet > *:not(.sheet-header) .sheet-title { padding-top: 0; margin-bottom: var(--space-3); }

/* Fila de acciones dentro de un sheet: envuelve ordenado; "Eliminar" (danger)
   siempre en su propia línea, claramente separada. */
.sheet-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); }
.sheet-actions .btn { flex: 1 1 auto; min-width: 118px; }
.sheet-actions .btn-danger { flex-basis: 100%; margin-top: var(--space-2); }

/* Cuerpo del popup de novedades de beta (public/js/anuncios.js). Clase
   ESPECÍFICA de este popup -- no toca `.sheet-actions` ni ningún otro modal
   que reutilice `.sheet`. El "gap" separa los párrafos entre sí; el botón,
   además del gap, recibe un margen superior propio para que quede
   claramente despegado del último párrafo (ajuste UX post-producción,
   reportado en móvil). */
.anuncio-cuerpo { display: flex; flex-direction: column; gap: var(--space-3); }
.anuncio-cuerpo .btn { margin-top: var(--space-3); }

.detail { margin: var(--space-4) 0 0; }
.detail-row { display: flex; justify-content: space-between; gap: var(--space-4); padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border); }
.detail-row:last-child { border-bottom: none; }
.detail-row dt { color: var(--color-muted); font-size: var(--text-sm); }
.detail-row dd { margin: 0; font-weight: 600; text-align: right; overflow-wrap: anywhere; }

.toast {
    position: fixed; left: 50%; bottom: calc(var(--bottom-nav-height) + var(--space-4));
    transform: translateX(-50%);
    max-width: calc(var(--app-max-width) - var(--space-6));
    background: var(--color-text); color: #fff;
    font-size: var(--text-sm); padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm); z-index: 60;
    box-shadow: var(--shadow-soft);
}
.toast.error { background: var(--color-danger); }
