// Rumbo — Ahorros · metas con historial de aportes
// Cada meta: { id, name, iconName, target, targetDate?, createdAt, contributions: [{id, date, amount, note?}] }
// El total ahorrado SIEMPRE se deriva de contributions (goalSaved en Data.jsx) — sin contadores sueltos.

const { useState: useStSav, useEffect: useEffSav } = React;

/* ── Helpers de monto es-AR (mismo patrón que AddFixedDrawer) ── */
const parseAmountSav = (s) => {
  let v = String(s || '').replace(/[^\d,]/g, '');
  const parts = v.split(',');
  if (parts.length > 2) v = parts[0] + ',' + parts.slice(1).join('');
  const [i, d] = v.split(',');
  return d !== undefined ? `${i},${d.slice(0, 2)}` : (i || '');
};
const fmtAmountSav = (raw) => {
  if (raw === '' || raw == null) return '';
  const [i, d] = String(raw).split(',');
  const intNum = Number(i || '0');
  const intFmt = isNaN(intNum) ? '0' : intNum.toLocaleString('es-AR');
  return d !== undefined ? `${intFmt},${d}` : intFmt;
};
const toNumberSav = (raw) => {
  if (raw === '' || raw == null) return 0;
  return parseFloat(String(raw).replace(/\./g, '').replace(',', '.')) || 0;
};
const numToRaw = (n) => (n == null || n === 0) ? '' : String(n).replace('.', ',');

/* Meses entre hoy y una fecha (mínimo 1, para sugerir aporte mensual) */
const monthsUntilSav = (dateStr) => {
  if (!dateStr) return null;
  const now = new Date();
  const t = new Date(dateStr + 'T00:00:00');
  const m = (t.getFullYear() - now.getFullYear()) * 12 + (t.getMonth() - now.getMonth());
  return Math.max(1, m);
};

/* Fecha ISO a N meses de hoy (para los planes sugeridos) */
const dateInMonthsSav = (n) => {
  const d = new Date();
  d.setMonth(d.getMonth() + n);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
};

/* Formato para cuotas del plan: número completo (es "cuánto pongo"), compacto solo en millones */
const fmtPlanSav = (v, currency) => (Math.abs(v) >= 1000000 ? fmt(v, currency) : fmtFull(v, currency));

/* Divide lo que falta en cuotas por mes / semana / día (redondeadas — acá los centavos molestan) */
const planSplitSav = (remaining, months) => {
  const days = Math.max(1, Math.round(months * 30.44));
  return {
    perMonth: Math.round(remaining / months),
    perWeek:  Math.round((remaining / days) * 7),
    perDay:   Math.round(remaining / days),
  };
};

/* Margen mensual real: promedio de (ingresos − gastos) de los últimos 3 meses completos.
   Es la base de los consejos de Rumbo — null si no hay historia. */
const monthlyCapacitySav = (transactions) => {
  const nets = [1, 2, 3].map(i => {
    const d = new Date(); d.setDate(1); d.setMonth(d.getMonth() - i);
    const k = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
    const tx = (transactions || []).filter(t => (t.date || '').startsWith(k));
    if (!tx.length) return null;
    return tx.reduce((s, t) => s + (t.type === 'income' ? t.amount : -t.amount), 0);
  }).filter(v => v !== null);
  if (!nets.length) return null;
  return nets.reduce((s, v) => s + v, 0) / nets.length;
};

/* ── Drawer: nueva meta / editar meta ──
   `initial` con id → edición; sin id → plantilla (crea una meta nueva prellenada).
   `capacity`: margen mensual real del usuario — alimenta los consejos de Rumbo. */
const GoalDrawer = ({ onClose, onSave, initial, capacity = null, currency = 'ARS' }) => {
  const isEdit = !!(initial && initial.id);
  const [form, setF] = useStSav({
    name: initial?.name || '',
    iconName: initial?.iconName || 'piggyBank',
    target: numToRaw(initial?.target),
    targetDate: initial?.targetDate || '',
    planCadence: initial?.planCadence || null,
  });
  const [showIconPicker, setShowIconPicker] = useStSav(false);
  // Plazo elegido para el plan cuando todavía no hay fecha objetivo (en meses)
  const [horizon, setHorizon] = useStSav(initial?.planMonths || 1);

  // Elegir cadencia del plan (día/semana/mes) — pide permiso de notificación en ese momento
  const pickCadence = (c) => {
    setF(f => ({ ...f, planCadence: f.planCadence === c ? null : c }));
    if ('Notification' in window && Notification.permission === 'default') {
      Notification.requestPermission();
    }
  };

  const secCard  = { background: 'var(--surface-2)', border: '1.5px solid var(--border)', borderRadius: 'var(--r-lg)', padding: '14px 18px', marginBottom: 14 };
  const lbl      = { fontSize: 11, fontWeight: 700, color: 'var(--t5)', textTransform: 'uppercase', letterSpacing: '0.4px', marginBottom: 8 };
  const rawInput = { width: '100%', background: 'none', border: 'none', outline: 'none', color: 'var(--t1)', fontSize: 15, fontFamily: 'var(--font)', fontWeight: 500 };

  const submit = () => {
    const target = toNumberSav(form.target);
    if (!form.name.trim() || target <= 0) { window.showToast('Completá nombre y monto objetivo', 'warning'); return; }
    onSave({
      id: initial?.id || 'g' + Date.now(),
      name: form.name.trim(),
      iconName: form.iconName,
      target,
      targetDate: form.targetDate || null,
      planCadence: form.planCadence || null,
      planMonths: form.targetDate ? null : horizon,
      createdAt: initial?.createdAt || new Date().toISOString().slice(0, 10),
      contributions: initial?.contributions || [],
    });
    onClose();
  };

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer">
        <div className="drawer-handle" />

        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 20px 16px', borderBottom: '1px solid var(--border)' }}>
          <button onClick={onClose} style={{ width: 34, height: 34, borderRadius: 'var(--r-md)', background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, cursor: 'pointer' }}>
            <Icon name="x" size={17} color="var(--t3)" />
          </button>
          <div>
            <div style={{ fontSize: 16, fontWeight: 800, color: 'var(--t1)' }}>{isEdit ? 'Editar meta' : 'Nueva meta de ahorro'}</div>
            <div style={{ fontSize: 12, color: 'var(--t4)' }}>Un viaje, un auto, un fondo — lo que quieras juntar</div>
          </div>
        </div>

        <div style={{ padding: '18px 20px', overflowY: 'auto' }}>
          {/* Ícono */}
          <div style={secCard}>
            <div style={lbl}>Ícono</div>
            <button type="button" onClick={() => setShowIconPicker(true)}
              style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 12, padding: '10px 12px', background: 'var(--surface)', border: '1.5px solid var(--border)', borderRadius: 'var(--r-md)', cursor: 'pointer', fontFamily: 'var(--font)' }}>
              <div style={{ width: 36, height: 36, borderRadius: 'var(--r-md)', background: 'var(--accent-muted)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                <Icon name={form.iconName} size={18} color="var(--accent)" strokeWidth={1.7} />
              </div>
              <span style={{ flex: 1, textAlign: 'left', fontSize: 13, color: 'var(--t3)', fontWeight: 600 }}>Cambiar ícono</span>
              <Icon name="chevronRight" size={15} color="var(--t5)" />
            </button>
          </div>

          {/* Nombre */}
          <div style={secCard}>
            <div style={{ ...lbl, display: 'flex', justifyContent: 'space-between' }}>
              <span>Nombre <span style={{ color: 'var(--danger)' }}>*</span></span>
              <span style={{ fontWeight: 400, textTransform: 'none', letterSpacing: 0 }}>{(form.name || '').length}/60</span>
            </div>
            <input type="text" placeholder="Ej. Vacaciones, Auto, Fondo de emergencia…" value={form.name} maxLength={60}
              onChange={e => setF(f => ({ ...f, name: e.target.value }))} style={rawInput} />
          </div>

          {/* Monto objetivo */}
          <div style={secCard}>
            <div style={lbl}>Monto objetivo <span style={{ color: 'var(--danger)' }}>*</span></div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <Icon name="target" size={20} color="var(--accent)" strokeWidth={2} />
              <input type="text" inputMode="decimal" placeholder="0" value={fmtAmountSav(form.target)}
                onChange={e => setF(f => ({ ...f, target: parseAmountSav(e.target.value) }))}
                style={{ ...rawInput, fontSize: 30, fontWeight: 900, fontVariantNumeric: 'tabular-nums', flex: 1 }} />
            </div>
          </div>

          {/* Fecha objetivo (opcional) */}
          <DatePicker label="¿Para cuándo? (opcional)" value={form.targetDate}
            onChange={d => setF(f => ({ ...f, targetDate: d }))} />
          {form.targetDate && (
            <button onClick={() => setF(f => ({ ...f, targetDate: '' }))}
              style={{ background: 'none', border: 'none', color: 'var(--t5)', fontSize: 12, fontWeight: 600, cursor: 'pointer', marginTop: -8, marginBottom: 10, fontFamily: 'var(--font)' }}>
              Quitar fecha
            </button>
          )}

          {/* ── El plan de Rumbo — consejo en vivo mientras armás la meta ── */}
          {(() => {
            const target = toNumberSav(form.target);
            const alreadySaved = isEdit ? goalSaved(initial) : 0;
            const remaining = Math.max(0, target - alreadySaved);
            if (!(target > 0) || remaining <= 0) return null;

            const capNote = (perMonth) => {
              if (capacity === null) return null;
              if (capacity <= 0) return { tone: 'warning', text: 'Tus últimos meses cerraron sin margen. Cualquier aporte chico ya es ganarle al cero — arrancá suave.' };
              if (perMonth <= capacity * 0.5) return { tone: 'success', text: `Entra cómodo: es menos de la mitad de lo que te viene sobrando (~${fmt(capacity, currency)}/mes).` };
              if (perMonth <= capacity) return { tone: 'warning', text: `Entra en tu margen (~${fmt(capacity, currency)}/mes te viene sobrando), pero sin mucho aire.` };
              return { tone: 'danger', text: `Ojo: es más que los ~${fmt(capacity, currency)}/mes que te vienen sobrando. Corré la fecha o ajustá el monto.` };
            };
            const noteColor = { success: 'var(--success-text)', warning: 'var(--warning-text)', danger: 'var(--danger-text)' };

            return (
              <div style={{ background: 'var(--accent-muted)', border: '1px solid var(--accent)', borderRadius: 'var(--r-lg)', padding: '14px 16px', marginBottom: 14 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 10 }}>
                  <Icon name="compass" size={14} color="var(--accent)" strokeWidth={2} />
                  <span style={{ fontSize: 11, fontWeight: 800, color: 'var(--accent-text)', textTransform: 'uppercase', letterSpacing: '0.5px' }}>El plan de Rumbo</span>
                </div>

                {alreadySaved > 0 && (
                  <div style={{ fontSize: 11.5, color: 'var(--t3)', lineHeight: 1.5, marginBottom: 10 }}>
                    Ya llevás <b>{fmtFull(alreadySaved, currency)}</b> ahorrados — el plan es por los <b>{fmtFull(remaining, currency)}</b> que te faltan.
                  </div>
                )}

                {form.targetDate ? (() => {
                  const months = monthsUntilSav(form.targetDate);
                  const plan = planSplitSav(remaining, months);
                  const note = capNote(plan.perMonth);
                  const lblRaw = getMonthLabel(form.targetDate.slice(0, 7));
                  const lbl = lblRaw.charAt(0).toUpperCase() + lblRaw.slice(1);
                  return (
                    <>
                      <div style={{ fontSize: 12, color: 'var(--t3)', marginBottom: 10 }}>
                        Para llegar en <b>{lbl}</b> ({months} {months === 1 ? 'mes' : 'meses'}), guardá:
                      </div>
                      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 8, marginBottom: note ? 10 : 0 }}>
                        {[['day', plan.perDay, 'por día'], ['week', plan.perWeek, 'por semana'], ['month', plan.perMonth, 'por mes']].map(([k, v, l]) => {
                          const sel = form.planCadence === k;
                          return (
                            <button key={k} onClick={() => pickCadence(k)}
                              style={{ background: sel ? 'var(--accent)' : 'var(--surface)', border: `1.5px solid ${sel ? 'var(--accent)' : 'var(--border)'}`, borderRadius: 'var(--r-md)', padding: '8px 10px', textAlign: 'center', cursor: 'pointer', fontFamily: 'var(--font)' }}>
                              <div style={{ fontSize: 15, fontWeight: 900, color: sel ? '#fff' : 'var(--accent)', fontVariantNumeric: 'tabular-nums' }}>{fmtPlanSav(v, currency)}</div>
                              <div style={{ fontSize: 10, color: sel ? 'rgba(255,255,255,0.85)' : 'var(--t5)', fontWeight: 700 }}>{l}</div>
                            </button>
                          );
                        })}
                      </div>
                      {note && <div style={{ fontSize: 11.5, color: noteColor[note.tone], fontWeight: 600, lineHeight: 1.5 }}>{note.text}</div>}
                    </>
                  );
                })() : (() => {
                  // Sin fecha: el plan aparece IGUAL, con plazo elegible (arranca en 1 mes)
                  const plan = planSplitSav(remaining, horizon);
                  const note = capNote(plan.perMonth);
                  const fixLblRaw = getMonthLabel(shiftMonth(CURRENT_MONTH, horizon));
                  const fixLbl = fixLblRaw.charAt(0).toUpperCase() + fixLblRaw.slice(1);
                  // Sugerencia estrella: guardar la mitad de tu margen real
                  const suggestion = (() => {
                    if (!capacity || capacity <= 0) return null;
                    const perMonth = capacity * 0.5;
                    const months = Math.ceil(remaining / perMonth);
                    if (months < 1 || months > 48) return null;
                    const lbl = getMonthLabel(shiftMonth(CURRENT_MONTH, months));
                    return { perMonth, months, label: lbl.charAt(0).toUpperCase() + lbl.slice(1) };
                  })();
                  return (
                    <>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap', marginBottom: 10 }}>
                        <span style={{ fontSize: 12, color: 'var(--t3)' }}>Para juntarlo en</span>
                        {[1, 3, 6, 12].map(n => (
                          <button key={n} onClick={() => setHorizon(n)}
                            style={{
                              background: horizon === n ? 'var(--accent)' : 'var(--surface)',
                              color: horizon === n ? '#fff' : 'var(--t3)',
                              border: `1.5px solid ${horizon === n ? 'var(--accent)' : 'var(--border)'}`,
                              borderRadius: 'var(--r-full)', padding: '4px 11px',
                              fontSize: 12, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)',
                            }}>
                            {n === 1 ? '1 mes' : `${n} meses`}
                          </button>
                        ))}
                        <span style={{ fontSize: 12, color: 'var(--t3)' }}>necesitás guardar:</span>
                      </div>
                      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 8, marginBottom: 10 }}>
                        {[['day', plan.perDay, 'por día'], ['week', plan.perWeek, 'por semana'], ['month', plan.perMonth, 'por mes']].map(([k, v, l]) => {
                          const sel = form.planCadence === k;
                          return (
                            <button key={k} onClick={() => pickCadence(k)}
                              style={{ background: sel ? 'var(--accent)' : 'var(--surface)', border: `1.5px solid ${sel ? 'var(--accent)' : 'var(--border)'}`, borderRadius: 'var(--r-md)', padding: '8px 10px', textAlign: 'center', cursor: 'pointer', fontFamily: 'var(--font)' }}>
                              <div style={{ fontSize: 15, fontWeight: 900, color: sel ? '#fff' : 'var(--accent)', fontVariantNumeric: 'tabular-nums' }}>{fmtPlanSav(v, currency)}</div>
                              <div style={{ fontSize: 10, color: sel ? 'rgba(255,255,255,0.85)' : 'var(--t5)', fontWeight: 700 }}>{l}</div>
                            </button>
                          );
                        })}
                      </div>
                      {note && <div style={{ fontSize: 11.5, color: noteColor[note.tone], fontWeight: 600, lineHeight: 1.5, marginBottom: 10 }}>{note.text}</div>}
                      <button onClick={() => setF(f => ({ ...f, targetDate: dateInMonthsSav(horizon) }))}
                        style={{ background: 'none', border: '1.5px solid var(--accent)', borderRadius: 'var(--r-full)', color: 'var(--accent)', padding: '6px 14px', fontSize: 12, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)' }}>
                        Fijar {fixLbl} como fecha objetivo
                      </button>
                      {suggestion && (
                        <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', marginTop: 12 }}>
                          <div style={{ flex: 1, minWidth: 180, fontSize: 11.5, color: 'var(--t3)', lineHeight: 1.5 }}>
                            Consejo de Rumbo: guardando la <b>mitad de lo que te viene sobrando</b> ({fmt(suggestion.perMonth, currency)}/mes) llegás en <b>{suggestion.label}</b>.
                          </div>
                          <button onClick={() => setF(f => ({ ...f, targetDate: dateInMonthsSav(suggestion.months) }))}
                            style={{ background: 'var(--accent)', border: 'none', borderRadius: 'var(--r-full)', color: '#fff', padding: '6px 14px', fontSize: 12, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)', flexShrink: 0 }}>
                            Usar este plan
                          </button>
                        </div>
                      )}
                    </>
                  );
                })()}

                {form.planCadence && (
                  <div style={{ display: 'flex', alignItems: 'flex-start', gap: 7, marginTop: 10, fontSize: 11.5, color: 'var(--t3)', lineHeight: 1.5 }}>
                    <Icon name="bell" size={13} color="var(--accent)" strokeWidth={1.8} />
                    <span>
                      Cuota elegida: <b>{form.planCadence === 'day' ? 'por día' : form.planCadence === 'week' ? 'por semana' : 'por mes'}</b>.
                      Rumbo te la recuerda cuando abras la app si el período no está cubierto. Tocá de nuevo para desactivar.
                    </span>
                  </div>
                )}
              </div>
            );
          })()}

          <button onClick={submit}
            style={{ width: '100%', background: 'var(--accent)', border: 'none', borderRadius: 14, color: '#fff', padding: 14, fontSize: 15, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)', boxShadow: 'var(--shadow-glow)', marginTop: 4 }}>
            {isEdit ? 'Guardar cambios' : 'Crear meta'}
          </button>
        </div>
      </div>

      {showIconPicker && (
        <IconPicker
          value={form.iconName}
          onChange={ic => setF(f => ({ ...f, iconName: ic }))}
          onClose={() => setShowIconPicker(false)}
        />
      )}
    </>
  );
};

/* ── Drawer: sumar aporte a una meta ──
   `monthLeftover`: lo que sobró del mes en curso (para sugerencias).
   `initialAmount`: monto prellenado (viene del Impulso del mes). */
const ContribDrawer = ({ goal, currency, onClose, onAdd, monthLeftover = 0, initialAmount = null }) => {
  const todayISO = new Date().toISOString().slice(0, 10);
  const [form, setF] = useStSav({ amount: initialAmount ? numToRaw(Math.round(initialAmount)) : '', date: todayISO, note: '' });

  const secCard  = { background: 'var(--surface-2)', border: '1.5px solid var(--border)', borderRadius: 'var(--r-lg)', padding: '14px 18px', marginBottom: 14 };
  const lbl      = { fontSize: 11, fontWeight: 700, color: 'var(--t5)', textTransform: 'uppercase', letterSpacing: '0.4px', marginBottom: 8 };
  const rawInput = { width: '100%', background: 'none', border: 'none', outline: 'none', color: 'var(--t1)', fontSize: 15, fontFamily: 'var(--font)', fontWeight: 500 };

  const saved = goalSaved(goal);
  const remaining = Math.max(0, goal.target - saved);
  const amount = toNumberSav(form.amount);

  // Sugerencias de monto: salen del PLAN de esta meta y de TU sobrante — no de números inventados
  const daysLeft = (() => {
    const n = new Date();
    const last = new Date(n.getFullYear(), n.getMonth() + 1, 0).getDate();
    return Math.max(1, last - n.getDate() + 1);
  })();
  const planMonths = monthsUntilSav(goal.targetDate) ?? (goal.planMonths || 6);
  const perDayPlan = remaining > 0 ? planSplitSav(remaining, planMonths).perDay : 0;
  const quickOptions = [
    perDayPlan > 0 ? { label: `Al ritmo de tu plan (${fmt(perDayPlan, currency)}/día)`, value: Math.round(perDayPlan * daysLeft), hint: `${daysLeft} días` } : null,
    monthLeftover > 0 ? { label: '10% de lo que te sobró este mes', value: Math.round(monthLeftover * 0.10) } : null,
    monthLeftover > 0 ? { label: 'La mitad de lo que te sobró', value: Math.round(monthLeftover * 0.5) } : null,
  ].filter(o => o && o.value > 0);

  const pctNow  = goal.target > 0 ? Math.min(100, Math.round((saved / goal.target) * 100)) : 0;
  const pctNext = goal.target > 0 ? Math.min(100, Math.round(((saved + amount) / goal.target) * 100)) : 0;

  const submit = () => {
    if (amount <= 0) { window.showToast('Ingresá un monto', 'warning'); return; }
    onAdd(goal.id, { id: 'gc' + Date.now(), date: form.date || todayISO, amount, note: form.note.trim() || undefined });
    onClose();
    window.showToast('Aporte sumado a la meta ✓', 'success');
  };

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer">
        <div className="drawer-handle" />

        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 20px 16px', borderBottom: '1px solid var(--border)' }}>
          <button onClick={onClose} style={{ width: 34, height: 34, borderRadius: 'var(--r-md)', background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, cursor: 'pointer' }}>
            <Icon name="x" size={17} color="var(--t3)" />
          </button>
          <div>
            <div style={{ fontSize: 16, fontWeight: 800, color: 'var(--t1)' }}>Sumar plata a "{goal.name}"</div>
            <div style={{ fontSize: 12, color: 'var(--t4)' }}>
              Llevás {fmtFull(saved, currency)} de {fmtFull(goal.target, currency)}
              {remaining > 0 ? ` · te faltan ${fmtFull(remaining, currency)}` : ' · ¡meta cumplida!'}
            </div>
          </div>
        </div>

        <div style={{ padding: '18px 20px', overflowY: 'auto' }}>
          {/* Monto */}
          <div style={secCard}>
            <div style={lbl}>Monto <span style={{ color: 'var(--danger)' }}>*</span></div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <span style={{ fontSize: 22, fontWeight: 900, color: 'var(--success)', lineHeight: 1 }}>+</span>
              <input type="text" inputMode="decimal" placeholder="0" value={fmtAmountSav(form.amount)}
                onChange={e => setF(f => ({ ...f, amount: parseAmountSav(e.target.value) }))}
                style={{ ...rawInput, fontSize: 30, fontWeight: 900, fontVariantNumeric: 'tabular-nums', flex: 1 }} autoFocus />
            </div>
            {amount > 0 && pctNext > pctNow && (
              <div style={{ fontSize: 12, color: 'var(--success-text)', fontWeight: 700, marginTop: 8 }}>
                Con este aporte pasás del {pctNow}% al {pctNext}% {pctNext >= 100 ? '— ¡meta cumplida! 🎉' : 'de tu meta'}
              </div>
            )}
            {/* Ideas de monto — un toque y listo */}
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 12 }}>
              {quickOptions.map(o => (
                <button key={o.label} onClick={() => setF(f => ({ ...f, amount: numToRaw(o.value) }))}
                  style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1, background: 'var(--surface)', border: '1.5px solid var(--border)', borderRadius: 'var(--r-md)', padding: '7px 11px', cursor: 'pointer', fontFamily: 'var(--font)' }}>
                  <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--t3)' }}>{o.label}</span>
                  <span style={{ fontSize: 12, fontWeight: 800, color: 'var(--accent)', fontVariantNumeric: 'tabular-nums' }}>{fmtFull(o.value, currency)}{o.hint ? ` · ${o.hint}` : ''}</span>
                </button>
              ))}
              {remaining > 0 && (
                <button onClick={() => setF(f => ({ ...f, amount: numToRaw(remaining) }))}
                  style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 1, background: 'var(--accent-muted)', border: '1.5px solid var(--accent)', borderRadius: 'var(--r-md)', padding: '7px 11px', cursor: 'pointer', fontFamily: 'var(--font)' }}>
                  <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--accent-text)' }}>Completar la meta</span>
                  <span style={{ fontSize: 12, fontWeight: 800, color: 'var(--accent)', fontVariantNumeric: 'tabular-nums' }}>{fmtFull(remaining, currency)}</span>
                </button>
              )}
            </div>
          </div>

          {/* Fecha */}
          <DatePicker label="Fecha" value={form.date} onChange={d => setF(f => ({ ...f, date: d }))} />

          {/* Nota */}
          <div style={secCard}>
            <div style={lbl}>Nota (opcional)</div>
            <input type="text" placeholder="Ej. aguinaldo, venta, resto del mes…" value={form.note} maxLength={80}
              onChange={e => setF(f => ({ ...f, note: e.target.value }))} style={rawInput} />
          </div>

          <button onClick={submit}
            style={{ width: '100%', background: 'var(--success)', border: 'none', borderRadius: 14, color: '#fff', padding: 14, fontSize: 15, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)', marginTop: 4 }}>
            Sumar aporte
          </button>
        </div>
      </div>
    </>
  );
};

/* ── Drawer: detalle de meta — historial + acciones ── */
const GoalDetailDrawer = ({ goal, currency, capacity = null, onClose, onContribute, onEdit, onDelete, onDeleteContribution }) => {
  const saved = goalSaved(goal);
  const pct = goal.target > 0 ? Math.min(100, Math.round((saved / goal.target) * 100)) : 0;
  const done = saved >= goal.target;
  const remaining = Math.max(0, goal.target - saved);
  const contribs = [...(goal.contributions || [])].sort((a, b) => (b.date || '').localeCompare(a.date || ''));

  // El plan de Rumbo: con fecha usa la fecha; sin fecha, el plazo elegido en la meta (o 6 meses)
  const planMonths = monthsUntilSav(goal.targetDate) ?? (goal.planMonths || 6);
  const plan = remaining > 0 ? planSplitSav(remaining, planMonths) : null;
  const planNote = (() => {
    if (!plan || capacity === null) return null;
    if (capacity <= 0) return { color: 'var(--warning-text)', text: 'Venís sin margen estos meses — cualquier aporte chico suma.' };
    if (plan.perMonth > capacity) return { color: 'var(--danger-text)', text: `Necesitás más que los ~${fmt(capacity, currency)}/mes que te vienen sobrando — considerá correr la fecha.` };
    return { color: 'var(--success-text)', text: `Entra en tu margen: te vienen sobrando ~${fmt(capacity, currency)}/mes.` };
  })();

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <div className="drawer">
        <div className="drawer-handle" />

        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 20px 16px', borderBottom: '1px solid var(--border)' }}>
          <button onClick={onClose} style={{ width: 34, height: 34, borderRadius: 'var(--r-md)', background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, cursor: 'pointer' }}>
            <Icon name="x" size={17} color="var(--t3)" />
          </button>
          <div style={{ width: 40, height: 40, borderRadius: 'var(--r-md)', background: 'var(--accent-muted)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
            <Icon name={goal.iconName || 'piggyBank'} size={20} color="var(--accent)" strokeWidth={1.8} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 16, fontWeight: 800, color: 'var(--t1)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{goal.name}</div>
            <div style={{ fontSize: 12, color: 'var(--t4)' }}>
              {fmtFull(saved, currency)} de {fmtFull(goal.target, currency)} · {pct}%
            </div>
          </div>
          <button onClick={() => onEdit(goal)} title="Editar meta"
            style={{ width: 34, height: 34, borderRadius: 'var(--r-md)', background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0, cursor: 'pointer' }}>
            <Icon name="edit" size={15} color="var(--t3)" />
          </button>
        </div>

        <div style={{ padding: '18px 20px', overflowY: 'auto' }}>
          {/* Barra de progreso */}
          <div style={{ height: 10, background: 'var(--track, var(--surface-2))', borderRadius: 99, overflow: 'hidden', marginBottom: 8 }}>
            <div style={{ width: `${pct}%`, height: '100%', background: done ? 'var(--success)' : 'var(--accent)', borderRadius: 99, transition: 'width 0.4s ease' }} />
          </div>
          {done && (
            <div style={{ marginBottom: 12 }}>
              <span className="badge" style={{ background: 'var(--success-bg)', color: 'var(--success-text)' }}>¡Meta cumplida! 🎉</span>
            </div>
          )}

          {/* El plan de Rumbo para lo que falta */}
          {plan && (
            <div style={{ background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 'var(--r-lg)', padding: '12px 14px', margin: '10px 0 4px' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
                <Icon name="compass" size={13} color="var(--accent)" strokeWidth={2} />
                <span style={{ fontSize: 10, fontWeight: 800, color: 'var(--t4)', textTransform: 'uppercase', letterSpacing: '0.5px' }}>
                  El plan de Rumbo · {goal.targetDate
                    ? (() => { const l = getMonthLabel(goal.targetDate.slice(0, 7)); return `para ${l.charAt(0).toUpperCase() + l.slice(1)}`; })()
                    : `a ${planMonths} ${planMonths === 1 ? 'mes' : 'meses'}`}
                </span>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 8, marginBottom: planNote ? 8 : 0 }}>
                {[[plan.perDay, 'por día'], [plan.perWeek, 'por semana'], [plan.perMonth, 'por mes']].map(([v, l]) => (
                  <div key={l} style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', padding: '7px 9px', textAlign: 'center' }}>
                    <div style={{ fontSize: 14, fontWeight: 900, color: 'var(--accent)', fontVariantNumeric: 'tabular-nums' }}>{fmtPlanSav(v, currency)}</div>
                    <div style={{ fontSize: 10, color: 'var(--t5)', fontWeight: 700 }}>{l}</div>
                  </div>
                ))}
              </div>
              {planNote && <div style={{ fontSize: 11, color: planNote.color, fontWeight: 600, lineHeight: 1.5 }}>{planNote.text}</div>}
            </div>
          )}

          <button onClick={() => onContribute(goal)}
            style={{ width: '100%', background: 'var(--success)', border: 'none', borderRadius: 14, color: '#fff', padding: 13, fontSize: 14, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)', margin: '8px 0 18px', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
            <Icon name="plus" size={16} color="#fff" strokeWidth={2.5} />
            Sumar plata
          </button>

          {/* Historial de aportes */}
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--t5)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 10 }}>
            Historial de aportes ({contribs.length})
          </div>
          {contribs.length === 0 ? (
            <div style={{ padding: '24px 0', textAlign: 'center', color: 'var(--t5)', fontSize: 13 }}>
              Todavía no sumaste plata a esta meta.
            </div>
          ) : contribs.map(c => (
            <div key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 0', borderBottom: '1px solid var(--border)' }}>
              <div style={{ width: 32, height: 32, borderRadius: 'var(--r-md)', background: 'var(--success-bg)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                <Icon name="arrowUp" size={14} color="var(--success)" strokeWidth={2.2} />
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--success)', fontVariantNumeric: 'tabular-nums' }}>+{fmtFull(c.amount, currency)}</div>
                <div style={{ fontSize: 11, color: 'var(--t5)' }}>
                  {fmtDateLabel(c.date)}{c.note ? ` · ${c.note}` : ''}
                </div>
              </div>
              <button onClick={() => { if (window.confirm('¿Borrar este aporte?')) onDeleteContribution(goal.id, c.id); }}
                title="Borrar aporte"
                style={{ background: 'none', border: 'none', cursor: 'pointer', padding: 6, display: 'flex' }}>
                <Icon name="trash" size={14} color="var(--t5)" />
              </button>
            </div>
          ))}

          {/* Eliminar meta */}
          <button onClick={() => { if (window.confirm(`¿Eliminar la meta "${goal.name}" y todo su historial?`)) { onDelete(goal.id); onClose(); } }}
            style={{ width: '100%', background: 'none', border: '1.5px solid color-mix(in srgb, var(--danger) 30%, transparent)', borderRadius: 14, color: 'var(--danger)', padding: 12, fontSize: 13, fontWeight: 600, cursor: 'pointer', fontFamily: 'var(--font)', marginTop: 18 }}>
            Eliminar meta
          </button>
        </div>
      </div>
    </>
  );
};

/* ── Pantalla Ahorros ── */
const Savings = ({ data, setData, theme, toggleTheme }) => {
  const [showAdd, setShowAdd]         = useStSav(false);
  const [editGoal, setEditGoal]       = useStSav(null);
  const [detailId, setDetailId]       = useStSav(null);
  const [contribId, setContribId]     = useStSav(null);
  const [templateInit, setTemplateInit] = useStSav(null);
  const [impulsoAmount, setImpulsoAmount] = useStSav(null);
  const [contribAmount, setContribAmount] = useStSav(null);

  // El FAB central del bottom-nav mobile dispara este evento contextual
  useEffSav(() => {
    const handler = (e) => { if (e.detail === 'saving') setShowAdd(true); };
    window.addEventListener('rumbo:openAdd', handler);
    return () => window.removeEventListener('rumbo:openAdd', handler);
  }, []);

  const goals = data.goals || [];
  const currency = data.settings?.currency || 'ARS';

  const totalSaved  = goals.reduce((s, g) => s + goalSaved(g), 0);
  const totalTarget = goals.reduce((s, g) => s + (g.target || 0), 0);
  const totalPct = totalTarget > 0 ? Math.min(100, Math.round((totalSaved / totalTarget) * 100)) : 0;

  // Lo que sobró del mes en curso (ingresos - gastos registrados) → combustible del "Impulso"
  const monthLeftover = (() => {
    const curTx = (data.transactions || []).filter(t => (t.date || '').startsWith(CURRENT_MONTH));
    const inc = curTx.filter(t => t.type === 'income').reduce((s, t) => s + t.amount, 0);
    const exp = curTx.filter(t => t.type === 'expense').reduce((s, t) => s + t.amount, 0);
    return inc - exp;
  })();

  // Margen mensual real (promedio últimos 3 meses) — alimenta los consejos de Rumbo
  const capacity = monthlyCapacitySav(data.transactions);

  // Recordatorios: metas con cadencia elegida cuyo período actual no está cubierto
  const dueGoals = goals
    .map(g => ({ g, st: goalPlanStatus(g) }))
    .filter(x => x.st && !x.st.done && !x.st.covered);

  // Racha: meses consecutivos con al menos un aporte (contando desde este mes o el anterior)
  const streak = (() => {
    const monthsSet = new Set();
    goals.forEach(g => (g.contributions || []).forEach(c => c.date && monthsSet.add(c.date.slice(0, 7))));
    let m = CURRENT_MONTH;
    if (!monthsSet.has(m)) m = getPrevMonth(m);
    let count = 0;
    while (monthsSet.has(m)) { count++; m = getPrevMonth(m); }
    return count;
  })();

  // Plantillas de metas — ideas de un toque (se ocultan las que ya existen)
  const monthlyIncome = Number(data.settings?.monthlyIncome) || 0;
  const GOAL_TEMPLATES = [
    { name: 'Fondo de emergencia', iconName: 'shield', target: monthlyIncome > 0 ? monthlyIncome * 3 : undefined, hint: monthlyIncome > 0 ? '3 sueldos' : null },
    { name: 'Viaje',               iconName: 'plane' },
    { name: 'Auto',                iconName: 'car' },
    { name: 'Celular nuevo',       iconName: 'smartphone' },
    { name: 'Casa propia',         iconName: 'home' },
    { name: 'Estudios',            iconName: 'bookOpen' },
  ].filter(t => !goals.some(g => g.name.toLowerCase() === t.name.toLowerCase()));

  const openContrib = (goalId, amount = null) => {
    setContribAmount(amount);
    setContribId(goalId);
    setImpulsoAmount(null);
  };

  const persist = (updatedGoals) => {
    const updated = { ...data, goals: updatedGoals };
    setData(updated); saveData(updated);
  };

  const handleSaveGoal = (goal) => {
    const exists = goals.some(g => g.id === goal.id);
    persist(exists ? goals.map(g => g.id === goal.id ? goal : g) : [...goals, goal]);
    window.showToast(exists ? 'Meta actualizada ✓' : 'Meta creada ✓', 'success');
  };
  const handleDeleteGoal = (id) => {
    persist(goals.filter(g => g.id !== id));
    window.showToast('Meta eliminada', 'success');
  };
  const handleAddContribution = (goalId, contribution) => {
    persist(goals.map(g => g.id === goalId
      ? { ...g, contributions: [...(g.contributions || []), contribution] }
      : g));
  };
  const handleDeleteContribution = (goalId, contribId2) => {
    persist(goals.map(g => g.id === goalId
      ? { ...g, contributions: (g.contributions || []).filter(c => c.id !== contribId2) }
      : g));
  };

  const detailGoal  = goals.find(g => g.id === detailId) || null;
  const contribGoal = goals.find(g => g.id === contribId) || null;

  return (
    <div style={{ background: 'var(--bg)', minHeight: '100%', paddingBottom: 24 }}>

      {/* ── Header ── */}
      <div className="screen-header">
        <div>
          <h1 className="screen-title">Ahorros</h1>
          <p className="screen-sub">Tus metas y fondos</p>
        </div>
        <button className="theme-toggle" onClick={toggleTheme} title="Cambiar tema">
          <Icon name={theme === 'dark' ? 'sun' : 'moon'} size={15} color="var(--t3)" />
        </button>
      </div>

      {/* ── Hero: total ahorrado ── */}
      {goals.length > 0 && (
        <div style={{ padding: '16px 20px 0' }}>
          <div style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-xl)', boxShadow: 'var(--sh-md)', padding: '18px 20px' }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--t5)', textTransform: 'uppercase', letterSpacing: '1.2px', marginBottom: 10, textAlign: 'center' }}>
              Total ahorrado
            </div>
            <div style={{ textAlign: 'center', marginBottom: 14 }}>
              <span style={{ fontSize: 34, fontWeight: 900, color: 'var(--accent)', letterSpacing: '-1px', fontVariantNumeric: 'tabular-nums' }}>
                {fmtFull(totalSaved, currency)}
              </span>
              <span style={{ fontSize: 14, fontWeight: 600, color: 'var(--t5)', marginLeft: 8 }}>
                de {fmtFull(totalTarget, currency)}
              </span>
            </div>
            <div style={{ height: 10, background: 'var(--track, var(--surface-2))', borderRadius: 99, overflow: 'hidden' }}>
              <div style={{ width: `${totalPct}%`, height: '100%', background: 'var(--accent)', borderRadius: 99, transition: 'width 0.4s ease' }} />
            </div>
            <div style={{ fontSize: 11, color: 'var(--t5)', textAlign: 'center', marginTop: 8 }}>
              {totalPct}% del total de tus metas · {goals.length} {goals.length === 1 ? 'meta' : 'metas'}
            </div>
            {streak >= 2 && (
              <div style={{ display: 'flex', justifyContent: 'center', marginTop: 10 }}>
                <span className="badge" style={{ background: 'var(--warning-bg)', color: 'var(--warning-text)', display: 'inline-flex', alignItems: 'center', gap: 5 }}>
                  <Icon name="flame" size={12} color="var(--warning)" strokeWidth={2} />
                  {streak} meses seguidos aportando — no cortes la racha
                </span>
              </div>
            )}
          </div>
        </div>
      )}

      {/* ── Te toca guardar — recordatorios del plan elegido ── */}
      {dueGoals.length > 0 && (
        <div style={{ padding: '14px 20px 0' }}>
          <div style={{ background: 'var(--warning-bg)', border: '1px solid var(--warning)', borderRadius: 'var(--r-xl)', padding: '14px 16px' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
              <Icon name="bell" size={15} color="var(--warning)" strokeWidth={2} />
              <span style={{ fontSize: 12, fontWeight: 800, color: 'var(--warning-text)', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Te toca guardar</span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {dueGoals.map(({ g, st }) => (
                <div key={g.id} style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                  <div style={{ flex: 1, minWidth: 160, fontSize: 12.5, color: 'var(--t2)', lineHeight: 1.5 }}>
                    {st.period} · <b>{g.name}</b>: {fmtFull(st.amount, currency)} <span style={{ color: 'var(--t5)' }}>({st.label})</span>
                  </div>
                  <button onClick={() => openContrib(g.id, st.amount)}
                    style={{ display: 'flex', alignItems: 'center', gap: 5, background: 'var(--success)', border: 'none', borderRadius: 'var(--r-full)', color: '#fff', padding: '6px 13px', fontSize: 12, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)', flexShrink: 0 }}>
                    <Icon name="plus" size={12} color="#fff" strokeWidth={2.5} />
                    Sumar ahora
                  </button>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}

      {/* ── Impulso del mes: lo que te sobró, listo para empujar una meta ── */}
      {goals.length > 0 && monthLeftover > 0 && (
        <div style={{ padding: '14px 20px 0' }}>
          <div style={{ background: 'var(--accent-muted)', border: '1px solid var(--accent)', borderRadius: 'var(--r-xl)', padding: '16px 18px' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
              <Icon name="sparkles" size={15} color="var(--accent)" strokeWidth={2} />
              <span style={{ fontSize: 12, fontWeight: 800, color: 'var(--accent-text)', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Impulso del mes</span>
            </div>
            <div style={{ fontSize: 13, color: 'var(--t2)', lineHeight: 1.5, marginBottom: 12 }}>
              Este mes te sobran <b style={{ fontVariantNumeric: 'tabular-nums' }}>{fmtFull(monthLeftover, currency)}</b>.
              La plata que queda dando vueltas se gasta sola — mandale una parte a una meta:
            </div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {[0.10, 0.25, 0.50].map(p => (
                <button key={p}
                  onClick={() => {
                    const amt = Math.round(monthLeftover * p);
                    if (goals.length === 1) openContrib(goals[0].id, amt);
                    else setImpulsoAmount(impulsoAmount === amt ? null : amt);
                  }}
                  style={{
                    background: impulsoAmount === Math.round(monthLeftover * p) ? 'var(--accent)' : 'var(--surface)',
                    color: impulsoAmount === Math.round(monthLeftover * p) ? '#fff' : 'var(--t2)',
                    border: '1.5px solid var(--accent)', borderRadius: 'var(--r-full)', padding: '7px 14px',
                    fontSize: 12, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)', fontVariantNumeric: 'tabular-nums',
                  }}>
                  {Math.round(p * 100)}% · {fmt(Math.round(monthLeftover * p), currency)}
                </button>
              ))}
            </div>
            {impulsoAmount && goals.length > 1 && (
              <div style={{ marginTop: 12 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--t4)', textTransform: 'uppercase', letterSpacing: '0.4px', marginBottom: 8 }}>¿A qué meta?</div>
                <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                  {goals.map(g => (
                    <button key={g.id} onClick={() => openContrib(g.id, impulsoAmount)}
                      style={{ display: 'flex', alignItems: 'center', gap: 6, background: 'var(--surface)', border: '1.5px solid var(--border)', borderRadius: 'var(--r-md)', padding: '7px 12px', fontSize: 12, fontWeight: 700, color: 'var(--t2)', cursor: 'pointer', fontFamily: 'var(--font)' }}>
                      <Icon name={g.iconName || 'piggyBank'} size={14} color="var(--accent)" strokeWidth={1.8} />
                      {g.name}
                    </button>
                  ))}
                </div>
              </div>
            )}
          </div>
        </div>
      )}

      {/* ── Lista de metas ── */}
      <div style={{ padding: '14px 20px 0', display: 'flex', flexDirection: 'column', gap: 12 }}>
        {goals.length === 0 && (
          <div style={{ background: 'var(--surface)', border: '1px dashed var(--border)', borderRadius: 'var(--r-xl)', padding: '48px 24px', textAlign: 'center' }}>
            <Icon name="piggyBank" size={40} color="var(--t6, var(--t5))" strokeWidth={1.4} />
            <div style={{ fontSize: 15, fontWeight: 700, color: 'var(--t2)', marginTop: 12 }}>Todavía no tenés metas de ahorro</div>
            <div style={{ fontSize: 13, color: 'var(--t5)', marginTop: 4, lineHeight: 1.5 }}>
              Un viaje, un auto, un celular, un fondo de emergencia.<br />Creá tu primera meta y empezá a sumarle plata.
            </div>
            <button onClick={() => setShowAdd(true)}
              style={{ marginTop: 16, background: 'var(--accent)', border: 'none', borderRadius: 12, color: '#fff', padding: '11px 22px', fontSize: 14, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)' }}>
              Crear mi primera meta
            </button>
            {GOAL_TEMPLATES.length > 0 && (
              <div style={{ marginTop: 20 }}>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--t5)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 10 }}>O arrancá con una idea</div>
                <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'center' }}>
                  {GOAL_TEMPLATES.map(t => (
                    <button key={t.name} onClick={() => setTemplateInit({ name: t.name, iconName: t.iconName, target: t.target })}
                      style={{ display: 'flex', alignItems: 'center', gap: 6, background: 'var(--surface-2)', border: '1.5px solid var(--border)', borderRadius: 'var(--r-full)', padding: '7px 13px', fontSize: 12, fontWeight: 700, color: 'var(--t2)', cursor: 'pointer', fontFamily: 'var(--font)' }}>
                      <Icon name={t.iconName} size={14} color="var(--accent)" strokeWidth={1.8} />
                      {t.name}{t.hint ? <span style={{ color: 'var(--t5)', fontWeight: 600 }}> · {t.hint}</span> : null}
                    </button>
                  ))}
                </div>
              </div>
            )}
          </div>
        )}

        {goals.map(g => {
          const saved = goalSaved(g);
          const pct = g.target > 0 ? Math.min(100, Math.round((saved / g.target) * 100)) : 0;
          const done = saved >= g.target;
          const remaining = Math.max(0, g.target - saved);
          const months = monthsUntilSav(g.targetDate);
          const monthlyNeeded = (!done && months) ? remaining / months : null;

          // Estado del plan elegido (cadencia día/semana/mes) para esta meta
          const planSt = goalPlanStatus(g);

          // Sin fecha objetivo pero con ritmo de aportes → proyectar llegada
          const arrival = (() => {
            if (done || monthlyNeeded) return null;
            const byMonth = {};
            (g.contributions || []).forEach(c => { const k = (c.date || '').slice(0, 7); byMonth[k] = (byMonth[k] || 0) + c.amount; });
            const last3 = [CURRENT_MONTH, getPrevMonth(CURRENT_MONTH), getPrevMonth(getPrevMonth(CURRENT_MONTH))];
            const rhythm = last3.reduce((s, k) => s + (byMonth[k] || 0), 0) / 3;
            if (rhythm <= 0) return null;
            const monthsTo = Math.ceil(remaining / rhythm);
            if (monthsTo > 36) return null;
            const lbl = getMonthLabel(shiftMonth(CURRENT_MONTH, monthsTo));
            return { rhythm, label: lbl.charAt(0).toUpperCase() + lbl.slice(1) };
          })();

          return (
            <div key={g.id} onClick={() => setDetailId(g.id)}
              style={{ background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--r-xl)', boxShadow: 'var(--sh-sm)', padding: '16px 18px', cursor: 'pointer', transition: 'box-shadow 0.15s' }}
              onMouseEnter={e => e.currentTarget.style.boxShadow = 'var(--sh-md)'}
              onMouseLeave={e => e.currentTarget.style.boxShadow = 'var(--sh-sm)'}>

              <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
                <div style={{ width: 42, height: 42, borderRadius: 'var(--r-md)', background: done ? 'var(--success-bg)' : 'var(--accent-muted)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <Icon name={g.iconName || 'piggyBank'} size={20} color={done ? 'var(--success)' : 'var(--accent)'} strokeWidth={1.8} />
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 15, fontWeight: 800, color: 'var(--t1)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{g.name}</div>
                  <div style={{ fontSize: 12, color: 'var(--t4)', fontVariantNumeric: 'tabular-nums' }}>
                    {fmtFull(saved, currency)} <span style={{ color: 'var(--t5)' }}>de {fmtFull(g.target, currency)}</span>
                  </div>
                </div>
                <div style={{ textAlign: 'right', flexShrink: 0 }}>
                  <div style={{ fontSize: 18, fontWeight: 900, color: done ? 'var(--success)' : 'var(--accent)', fontVariantNumeric: 'tabular-nums' }}>{pct}%</div>
                </div>
              </div>

              <div style={{ height: 8, background: 'var(--track, var(--surface-2))', borderRadius: 99, overflow: 'hidden' }}>
                <div style={{ width: `${pct}%`, height: '100%', background: done ? 'var(--success)' : 'var(--accent)', borderRadius: 99, transition: 'width 0.4s ease' }} />
              </div>

              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 10, gap: 8, flexWrap: 'wrap' }}>
                <div style={{ fontSize: 11, color: 'var(--t5)' }}>
                  {done
                    ? '¡Meta cumplida! 🎉'
                    : (planSt && !planSt.done)
                      ? <>Tu plan: <b style={{ color: 'var(--t3)' }}>{fmtFull(planSt.amount, currency)}</b> {planSt.label} · {planSt.covered
                          ? <span style={{ color: 'var(--success-text)', fontWeight: 700 }}>✓ cubierto por ahora</span>
                          : <span style={{ color: 'var(--warning-text)', fontWeight: 700 }}>{planSt.period.toLowerCase()} te toca</span>}</>
                      : monthlyNeeded
                        ? <>Para {(() => { const l = getMonthLabel(g.targetDate.slice(0, 7)); return l.charAt(0).toUpperCase() + l.slice(1); })()} · necesitás <b style={{ color: 'var(--t3)' }}>{fmt(monthlyNeeded, currency)}/mes</b> <span style={{ opacity: 0.8 }}>(~{fmt(monthlyNeeded / 30, currency)}/día)</span></>
                        : arrival
                          ? <>A este ritmo ({fmt(arrival.rhythm, currency)}/mes) llegás en <b style={{ color: 'var(--t3)' }}>{arrival.label}</b></>
                          : <>Te faltan {fmtFull(remaining, currency)}</>}
                </div>
                <button onClick={(e) => { e.stopPropagation(); openContrib(g.id); }}
                  style={{ display: 'flex', alignItems: 'center', gap: 5, background: 'var(--success-bg)', border: '1px solid color-mix(in srgb, var(--success) 30%, transparent)', borderRadius: 'var(--r-full)', color: 'var(--success-text)', padding: '5px 12px', fontSize: 12, fontWeight: 800, cursor: 'pointer', fontFamily: 'var(--font)' }}>
                  <Icon name="plus" size={12} color="var(--success)" strokeWidth={2.5} />
                  Sumar plata
                </button>
              </div>
            </div>
          );
        })}

        {/* Ideas para la próxima meta — plantillas de un toque */}
        {goals.length > 0 && GOAL_TEMPLATES.length > 0 && (
          <div style={{ padding: '4px 2px 0' }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--t5)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 8 }}>Ideas para tu próxima meta</div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {GOAL_TEMPLATES.map(t => (
                <button key={t.name} onClick={() => setTemplateInit({ name: t.name, iconName: t.iconName, target: t.target })}
                  style={{ display: 'flex', alignItems: 'center', gap: 6, background: 'var(--surface)', border: '1.5px dashed var(--border)', borderRadius: 'var(--r-full)', padding: '7px 13px', fontSize: 12, fontWeight: 700, color: 'var(--t3)', cursor: 'pointer', fontFamily: 'var(--font)' }}>
                  <Icon name="plus" size={12} color="var(--accent)" strokeWidth={2.2} />
                  <Icon name={t.iconName} size={14} color="var(--accent)" strokeWidth={1.8} />
                  {t.name}{t.hint ? <span style={{ color: 'var(--t5)', fontWeight: 600 }}> · {t.hint}</span> : null}
                </button>
              ))}
            </div>
          </div>
        )}
      </div>

      {/* ── FAB ── */}
      <button className="fab fab-fixed" onClick={() => setShowAdd(true)} aria-label="Nueva meta">
        <Icon name="plus" size={24} color="#fff" strokeWidth={2.5} />
      </button>

      {/* ── Drawers ── */}
      {showAdd && (
        <GoalDrawer capacity={capacity} currency={currency} onClose={() => setShowAdd(false)} onSave={handleSaveGoal} />
      )}
      {templateInit && (
        <GoalDrawer initial={templateInit} capacity={capacity} currency={currency} onClose={() => setTemplateInit(null)} onSave={handleSaveGoal} />
      )}
      {editGoal && (
        <GoalDrawer initial={editGoal} capacity={capacity} currency={currency} onClose={() => setEditGoal(null)} onSave={handleSaveGoal} />
      )}
      {contribGoal && (
        <ContribDrawer goal={contribGoal} currency={currency} monthLeftover={monthLeftover} initialAmount={contribAmount}
          onClose={() => { setContribId(null); setContribAmount(null); }} onAdd={handleAddContribution} />
      )}
      {detailGoal && !contribGoal && !editGoal && (
        <GoalDetailDrawer goal={detailGoal} currency={currency} capacity={capacity}
          onClose={() => setDetailId(null)}
          onContribute={(g) => openContrib(g.id)}
          onEdit={(g) => setEditGoal(g)}
          onDelete={handleDeleteGoal}
          onDeleteContribution={handleDeleteContribution} />
      )}
    </div>
  );
};

Object.assign(window, { Savings });
