/* =====================================================================
   app.jsx
   ---------------------------------------------------------------------
   Qué hace este archivo:
   - Es la aplicación principal una vez el padre/madre o el hijo/a ya
     ha iniciado sesión: el "Banco de [nombre]".
   - FamilyEconomyApp: el componente raíz de esta pantalla. Carga los
     datos del hijo/a activo desde Firebase (a través de
     window.storage, definido en root.jsx), y los guarda cada vez que
     algo cambia.
   - ChildView: todo lo que ve el hijo/a — el panel principal, tareas,
     trabajos, banco, préstamos, mercado, donaciones e historial.
   - ParentView: todo lo que ve el padre/madre en la "Oficina" — dar de
     alta tareas y trabajos, aprobar cosas pendientes, ajustes, etc.

   Depende de:
   - app-logic.js (defaultState y las funciones de fechas/dinero)
   - ui-components.jsx (Section, Money, Stamp, iconos...)
   ===================================================================== */

function FamilyEconomyApp({ initialRole = "child", onLogout, onSwitchChild, familyId, childId }) {
  const [state, setState] = useState(null);
  const [loaded, setLoaded] = useState(false);
  const [role, setRole] = useState(initialRole);
  const [pinInput, setPinInput] = useState("");
  const [pinError, setPinError] = useState("");
  const [saveError, setSaveError] = useState("");
  const [sharedJobs, setSharedJobs] = useState([]);
  const [familyChildren, setFamilyChildren] = useState([]);
  const [officePin, setOfficePin] = useState(null);
  const skipNextSave = useRef(true);
  const brandRef = useRef(null);

  // El PIN de la Oficina es único por familia (no por hijo/a) — si todavía
  // no se ha guardado uno a nivel de familia (familias de antes de este
  // cambio), usamos el que tuviera este hijo/a como punto de partida, para
  // que nadie se quede fuera de golpe.
  useEffect(() => {
    if (!familyId) return;
    window.getOfficePin(familyId).then((pin) => {
      setOfficePin(pin || null);
    }).catch(() => setOfficePin(null));
  }, [familyId]);

  // Tablón de ofertas compartidas: vive fuera del "cajón" de este hijo/a,
  // así que se carga aparte. refreshSharedJobs se puede volver a llamar
  // en cualquier momento (por ejemplo, tras intentar aceptar una oferta)
  // para tener siempre el estado más reciente del servidor.
  const refreshSharedJobs = useCallback(async () => {
    if (!familyId) return;
    const jobs = await window.listSharedJobs(familyId);
    setSharedJobs(jobs);
  }, [familyId]);

  useEffect(() => {
    refreshSharedJobs();
    if (familyId) {
      window.listChildren(familyId).then(setFamilyChildren).catch(() => setFamilyChildren([]));
      window.bumpFamilyUsage(familyId, { reads: 1 });
    }
  }, [familyId, refreshSharedJobs]);

  // Encoge el título "El Mundo de ..." solo lo necesario para que quepa en
  // una línea junto al logo, sin tocar el tamaño del logo ni el alto de la
  // cabecera. Con nombres cortos no hace nada (se queda a 2rem, como antes).
  useEffect(() => {
    const el = brandRef.current;
    if (!el || !el.parentElement) return;
    const fit = () => {
      el.style.fontSize = "";
      const available = el.parentElement.clientWidth;
      const natural = el.scrollWidth;
      if (natural > available && natural > 0) {
        const scale = Math.max(available / natural, 0.55);
        el.style.fontSize = `${(scale * 2).toFixed(2)}rem`;
      }
    };
    fit();
    window.addEventListener("resize", fit);
    return () => window.removeEventListener("resize", fit);
  }, [state?.childName]);

  useEffect(() => {
    (async () => {
      let value = null;
      try {
        const res = await window.storage.get(STORAGE_KEY, true);
        value = res ? JSON.parse(res.value) : null;
      } catch (e) {
        value = null;
      }
      let initial = value
        ? {
            ...defaultState,
            ...value,
            tax: { ...defaultState.tax, ...(value.tax || {}) },
            streak: { ...defaultState.streak, ...(value.streak || {}) },
            donationPot: { ...defaultState.donationPot, ...(value.donationPot || {}) },
            goal: value.goal ? { ...defaultState.goal, ...value.goal } : defaultState.goal,
            milestones: { ...defaultState.milestones, ...(value.milestones || {}) },
            savingsTerms: value.savingsTerms || defaultState.savingsTerms,
            mandatoryTasks: (value.mandatoryTasks || defaultState.mandatoryTasks).map((t) =>
              t.days === undefined ? { ...t, days: [...WEEKDAYS] } : t
            ),
            savingsLocks: (value.savingsLocks || []).map((l) => {
              if (l.rate !== undefined && l.status !== undefined) return l;
              const terms = value.savingsTerms || defaultState.savingsTerms;
              const months = l.termMonths || 1;
              const term = terms.find((t) => t.months === months) || terms[0] || { rate: 0 };
              return {
                ...l,
                termMonths: months,
                rate: l.rate !== undefined ? l.rate : term.rate,
                status: l.status || "locked",
                reason: l.reason || "Depósito manual",
              };
            }),
            loans: (value.loans || []).map((l) =>
              l.status === "active" && l.lastPaymentDate === undefined ? { ...l, lastPaymentDate: dateKey(), overdueNotified: false } : l
            ),
          }
        : defaultState;
      initial = processElapsedMonths(initial);
      initial = processElapsedDays(initial);
      setState(initial);
      setLoaded(true);
    })();
  }, []);

  useEffect(() => {
    if (!loaded || !state) return;
    if (skipNextSave.current) {
      skipNextSave.current = false;
      return;
    }
    (async () => {
      try {
        const ok = await window.storage.set(STORAGE_KEY, JSON.stringify(state), true);
        setSaveError(ok ? "" : "No se pudo guardar. Los cambios podrían perderse.");
      } catch (e) {
        setSaveError("No se pudo guardar. Los cambios podrían perderse.");
      }
    })();
  }, [state, loaded]);

  const update = useCallback((fn) => setState((prev) => fn({ ...prev })), []);

  // ---- Child actions ----
  const claimJob = (id) => update((s) => {
    s.jobs = s.jobs.map((j) => (j.id === id ? { ...j, status: "claimed" } : j));
    s.totalJobsAccepted = (s.totalJobsAccepted || 0) + 1;
    s.milestones = {
      ...s.milestones,
      firstJobAccepted: true,
      tenJobsAccepted: s.milestones?.tenJobsAccepted || s.totalJobsAccepted >= 10,
      fiftyJobsAccepted: s.milestones?.fiftyJobsAccepted || s.totalJobsAccepted >= 50,
    };
    return s;
  });
  const submitJob = (id) => update((s) => { s.jobs = s.jobs.map((j) => (j.id === id ? { ...j, status: "submitted", rejectReason: null } : j)); return s; });
  const proposeNegotiation = (id, proposedReward, note) =>
    update((s) => {
      s.jobs = s.jobs.map((j) => (j.id === id ? { ...j, status: "negotiating", proposedReward, proposedNote: note } : j));
      s.milestones = { ...s.milestones, firstJobNegotiated: true };
      return s;
    });

  const requestJob = (amount) => update((s) => {
    s.jobRequest = { amount, date: todayLabel(), status: "pending" };
    s.milestones = { ...s.milestones, firstJobRequested: true };
    return pushNotification(s, `¡Tu hijo/a está buscando trabajo por ${fmt(amount)} €!`, "parent", "trabajo");
  });
  const cancelJobRequest = () => update((s) => { s.jobRequest = null; return s; });
  const acceptJobRequest = () => update((s) => { s.jobRequest = null; return s; });
  const declineJobRequest = (reason) => update((s) => {
    if (!s.jobRequest) return s;
    s.jobRequest = { ...s.jobRequest, status: "declined", declineReason: reason };
    return pushNotification(s, `Tu adulto no te va a ofertar trabajo por ahora: ${reason}`, "child", "trabajo");
  });
  const acknowledgeJobRequest = () => update((s) => { s.jobRequest = null; return s; });

  // --- Tablón de ofertas compartidas (varios hermanos pueden competir) ---
  // Estas NO tocan "state" (el cajón de este hijo/a) porque viven en un
  // sitio compartido de la familia — por eso no usan update(), hablan
  // directamente con Firebase y luego refrescan la lista local.
  const createSharedJob = async (title, reward, expiresAt, targetChildIds, targetChildNames) => {
    await window.createSharedJob(familyId, { title, reward, expiresAt, targetChildIds, targetChildNames });
    await refreshSharedJobs();
  };
  const deleteSharedJobAction = async (jobId) => {
    await window.deleteSharedJob(familyId, jobId);
    await refreshSharedJobs();
    window.trimSharedJobHistory(familyId);
  };
  // El padre/madre cambia el PIN de acceso de un hijo/a (ej. si lo olvidó).
  const resetChildPinAction = async (targetChildId, newPin) => {
    await window.resetChildPin(familyId, targetChildId, newPin);
  };
  const claimSharedJobAction = async (jobId) => {
    const result = await window.claimSharedJob(familyId, jobId, childId, state.childName);
    await refreshSharedJobs();
    if (result.ok) {
      update((s) => {
        s.totalJobsAccepted = (s.totalJobsAccepted || 0) + 1;
        s.milestones = {
          ...s.milestones,
          firstJobAccepted: true,
          tenJobsAccepted: s.milestones?.tenJobsAccepted || s.totalJobsAccepted >= 10,
          fiftyJobsAccepted: s.milestones?.fiftyJobsAccepted || s.totalJobsAccepted >= 50,
        };
        return s;
      });
    }
    return result;
  };
  // El hijo/a le regala parte de su cartera a un hermano/a — se descuenta
  // aquí mismo y se abona directamente en el estado del hermano/a, esté o
  // no con su cajón abierto en ese momento.
  const donateToSibling = async (siblingId, siblingName, amount) => {
    const a = Math.min(amount, Math.max(0, state.wallet));
    if (a <= 0) return;
    update((s) => {
      s.wallet = round2(s.wallet - a);
      s.milestones = { ...s.milestones, firstSiblingGiftSent: true };
      return addLedger(s, `Donación a ${siblingName}`, -a, "out", "donaciones");
    });
    await window.creditChildWallet(familyId, siblingId, a, `Regalo de ${state.childName}`, "firstSiblingGiftReceived", "donaciones");
  };
  const submitSharedJobAction = async (jobId) => {
    await window.submitSharedJob(familyId, jobId);
    await refreshSharedJobs();
  };
  const approveSharedJobAction = async (jobId) => {
    const job = sharedJobs.find((j) => j.id === jobId);
    await window.approveSharedJob(familyId, jobId);
    await refreshSharedJobs();
    window.trimSharedJobHistory(familyId);
    // Si el hijo/a que se quedó la oferta es el que tienes abierto ahora
    // mismo, recargamos su estado desde el servidor — si no lo hiciéramos,
    // el próximo autoguardado normal de la app podría sobrescribir (y
    // perder) el pago que Firebase acaba de aplicar por su cuenta.
    if (job && job.claimedBy === childId) {
      try {
        const res = await window.storage.get(STORAGE_KEY, true);
        if (res) setState((prev) => ({ ...prev, ...JSON.parse(res.value) }));
      } catch (e) { /* si falla, se verá correcto en la próxima carga */ }
    }
  };
  const rejectSharedJobAction = async (jobId, reason) => {
    await window.rejectSharedJob(familyId, jobId, reason);
    await refreshSharedJobs();
  };

  // El padre/madre corrige un día pasado (por ejemplo, si la tarea sí se
  // hizo pero nadie la marcó/confirmó a tiempo). Ajusta el dinero de la
  // multa y recalcula la racha completa a partir del historial corregido.
  const correctTaskHistoryEntry = (date, taskIndex) => update((s) => {
    const taskHistory = [...(s.taskHistory || [])];
    const dayIdx = taskHistory.findIndex((h) => h.date === date);
    if (dayIdx === -1) return s;
    const day = { ...taskHistory[dayIdx], tasks: [...taskHistory[dayIdx].tasks] };
    const task = { ...day.tasks[taskIndex] };
    if (!task) return s;
    const newDone = !task.done;
    const penalty = task.penalty !== undefined
      ? task.penalty
      : ((s.mandatoryTasks || []).find((t) => t.title === task.title)?.penalty ?? 0);

    if (newDone) {
      s.wallet += penalty;
      addLedger(s, `Corrección: "${task.title}" (${date}) marcada como confirmada`, penalty, "in", "tareas");
    } else {
      s.wallet -= penalty;
      addLedger(s, `Corrección: "${task.title}" (${date}) marcada como no confirmada`, -penalty, "out", "tareas");
    }

    task.done = newDone;
    day.tasks[taskIndex] = task;
    day.allDone = day.tasks.every((t) => t.done);
    taskHistory[dayIdx] = day;
    s.taskHistory = taskHistory;

    const recomputed = recomputeStreakFromHistory(taskHistory);
    const oldRecord = s.streak?.record || 0;
    s.streak = { count: recomputed.count, lastDate: recomputed.lastDate, record: Math.max(recomputed.record, oldRecord) };
    return s;
  });

  const requestRedeem = (item) => update((s) => {
    if (s.wallet < item.cost) return s;
    s.marketRequests = [{ id: uid(), itemId: item.id, itemName: item.name, cost: item.cost, status: "pending" }, ...(s.marketRequests || [])];
    return s;
  });
  // El hijo/a pide algo que no está en el mercado, poniendo su propio
  // nombre y precio. Usa el mismo camino de aprobar/rechazar que un canje
  // normal — solo cambia que no viene de un artículo ya publicado.
  const requestCustomItem = (itemName, cost) => update((s) => {
    if (s.wallet < cost) return s;
    s.marketRequests = [{ id: uid(), itemId: null, itemName, cost, status: "pending", custom: true }, ...(s.marketRequests || [])];
    return pushNotification(s, `Tu hijo/a quiere comprar algo que no está en el mercado: "${itemName}" (${fmt(cost)} €).`, "parent", "mercado");
  });

  const transferToSavings = (amount, termMonths) => update((s) => {
    const a = Math.min(amount, Math.max(0, s.wallet));
    if (a <= 0) return s;
    const months = termMonths || 1;
    const term = (s.savingsTerms || []).find((t) => t.months === months) || { months: 1, rate: 0 };
    s.wallet -= a;
    s.savings += a;
    s.savingsLocks = [
      ...(s.savingsLocks || []),
      { id: uid(), amount: a, unlockDate: addMonthsKey(dateKey(), months), createdDate: dateKey(), termMonths: months, rate: term.rate, reason: "Depósito manual", status: "locked" },
    ];
    s.milestones = { ...s.milestones, firstSaving: true };
    s = checkSavingsMilestones(s);
    return addLedger(s, `Depósito al banco (bloqueado ${months} mes${months === 1 ? "" : "es"})`, -a, "out", "banco");
  });
  const withdrawFromSavings = (amount) => update((s) => {
    const available = Math.max(0, s.savings - lockedSavingsTotal(s));
    const a = Math.min(amount, available);
    if (a <= 0) return s;
    s.savings -= a;
    s.wallet += a;
    return addLedger(s, "Retiro del banco", a, "in", "banco");
  });
  // Cuando un depósito vence, el hijo/a decide: renovarlo (vuelve a quedar
  // bloqueado con el interés y plazo actuales) o dejarlo libre (deja de
  // aparecer en la lista; el dinero ya cuenta como disponible desde antes).
  const renewDeposit = (lockId) => update((s) => {
    const lock = (s.savingsLocks || []).find((l) => l.id === lockId);
    if (!lock || lock.status !== "matured") return s;
    const months = lock.termMonths || 1;
    const term = (s.savingsTerms || []).find((t) => t.months === months) || { months, rate: 0 };
    s.savingsLocks = s.savingsLocks.map((l) =>
      l.id === lockId
        ? { ...l, status: "locked", unlockDate: addMonthsKey(dateKey(), months), rate: term.rate, createdDate: dateKey(), reason: "Renovación" }
        : l
    );
    return s;
  });
  const keepDepositFree = (lockId) => update((s) => {
    s.savingsLocks = trimDepositHistory((s.savingsLocks || []).map((l) => (l.id === lockId ? { ...l, status: "completed" } : l)));
    return s;
  });
  // Deja decidido de antemano qué hacer cuando venza (el dinero sigue
  // bloqueado y ganando interés hasta la fecha real de vencimiento).
  const setDepositPreDecision = (lockId, decision) => update((s) => {
    s.savingsLocks = (s.savingsLocks || []).map((l) => (l.id === lockId ? { ...l, preDecision: decision } : l));
    return s;
  });

  const setGoal = (name, target, photo) => update((s) => ({ ...s, goal: name.trim() ? { name: name.trim(), target, photo: photo !== undefined ? photo : (s.goal?.photo || null) } : null }));

  const requestLoan = (amount, reason) => update((s) => {
    s.loans = [{ id: uid(), amount, remaining: amount, reason, status: "requested", interestRate: s.loanInterestRate }, ...s.loans];
    s.milestones = { ...s.milestones, firstLoan: true };
    return s;
  });
  const repayLoan = (id, amount) => update((s) => {
    const loan = s.loans.find((l) => l.id === id);
    if (!loan) return s;
    const remaining = round2(loan.remaining);
    const pay = round2(Math.min(amount, s.wallet, remaining));
    if (pay <= 0) return s;
    s.wallet = round2(s.wallet - pay);
    addLedger(s, `Pago de préstamo: ${loan.reason}`, -pay, "out", "banco");
    let justPaidOff = false;
    s.loans = s.loans.map((l) => {
      if (l.id !== id) return l;
      const newRemaining = round2(remaining - pay);
      if (newRemaining <= 0.005) justPaidOff = true;
      // El bloqueo del Mercado solo se levanta si se paga TODO — un pago
      // parcial no reinicia el plazo de atraso.
      return { ...l, remaining: newRemaining <= 0.005 ? 0 : newRemaining, status: newRemaining <= 0.005 ? "paid" : "active" };
    });
    if (justPaidOff) {
      s.milestones = { ...s.milestones, firstLoanRepaid: true };
      if (loan.lastPaymentDate && daysBetween(loan.lastPaymentDate, dateKey()) < 5) {
        s.milestones.fastLoanRepay = true;
      }
    }
    s.loans = trimLoanHistory(s.loans);
    return s;
  });

  const donate = (amount) => update((s) => {
    const a = Math.min(amount, Math.max(0, s.wallet));
    if (a <= 0) return s;
    s.wallet -= a;
    s.donationPot = { ...s.donationPot, total: s.donationPot.total + a };
    s.totalDonatedToPot = round2((s.totalDonatedToPot || 0) + a);
    s.milestones = { ...s.milestones, firstDonation: true, generousHeart: s.milestones?.generousHeart || s.totalDonatedToPot >= 20 };
    return addLedger(s, `Donación: ${s.donationPot.name}`, -a, "out", "donaciones");
  });

  const toggleChildCheck = (taskId) => update((s) => {
    const entries = { ...(s.mandatoryDay.entries || {}) };
    const e = entries[taskId] || { checked: false, verified: false };
    const nowChecked = !e.checked;
    entries[taskId] = { ...e, checked: nowChecked };
    s.mandatoryDay = { ...s.mandatoryDay, entries };
    if (nowChecked) {
      const task = (s.mandatoryTasks || []).find((t) => t.id === taskId);
      if (task) pushNotification(s, `¡"${task.title}" está lista para confirmar!`, "parent", "tareas");
    }
    return s;
  });

  const dismissNotification = (id) => update((s) => {
    s.notifications = (s.notifications || []).map((n) => (n.id === id ? { ...n, read: true } : n));
    return s;
  });

  // ---- Parent actions ----
  const addJob = (title, reward, durationDays) => update((s) => {
    const expiresAt = durationDays && durationDays > 0 ? addDaysKey(dateKey(), durationDays) : null;
    s.jobs = [{ id: uid(), title, reward, status: "available", expiresAt }, ...s.jobs];
    return s;
  });
  const deleteJob = (id) => update((s) => { s.jobs = trimJobHistory(s.jobs.map((j) => (j.id === id ? { ...j, status: "cancelled" } : j))); return s; });
  const approveJob = (id) => update((s) => {
    const job = s.jobs.find((j) => j.id === id);
    if (!job) return s;
    s.wallet += job.reward;
    addLedger(s, `Trabajo aprobado: ${job.title}`, job.reward, "in", "trabajo");
    s.jobs = trimJobHistory(s.jobs.map((j) => (j.id === id ? { ...j, status: "approved" } : j)));
    s.milestones = { ...s.milestones, firstJobPaid: true };
    if (job.reward > (s.maxJobPayout || 0)) {
      s.maxJobPayout = job.reward;
      s.milestones.payoutRecord = true;
    }
    return s;
  });
  // Al rechazar, el trabajo NO vuelve a estar libre para nadie más — sigue
  // asignado al mismo hijo/a (status "claimed"), con el motivo guardado,
  // para que pueda intentarlo de nuevo sabiendo qué corregir.
  const rejectJob = (id, reason) => update((s) => { s.jobs = s.jobs.map((j) => (j.id === id ? { ...j, status: "claimed", rejectReason: reason } : j)); return s; });
  const acceptNegotiation = (id) => update((s) => {
    const job = s.jobs.find((j) => j.id === id);
    const finalReward = job ? (job.proposedReward ?? job.reward) : 0;
    s.jobs = s.jobs.map((j) => (j.id === id ? { ...j, reward: j.proposedReward ?? j.reward, status: "claimed", proposedReward: undefined, proposedNote: undefined } : j));
    if (finalReward > (s.maxNegotiatedReward || 0)) {
      s.maxNegotiatedReward = finalReward;
      s.milestones = { ...s.milestones, negotiationRecord: true };
    }
    return s;
  });
  const declineNegotiation = (id) => update((s) => {
    s.jobs = s.jobs.map((j) => (j.id === id ? { ...j, status: "available", proposedReward: undefined, proposedNote: undefined } : j));
    return s;
  });

  const addMarketItem = (name, cost, emoji) => update((s) => { s.market = [...s.market, { id: uid(), name, cost, emoji: emoji || "🎁" }]; return s; });
  const deleteMarketItem = (id) => update((s) => { s.market = s.market.filter((m) => m.id !== id); return s; });
  const approveRedeem = (id) => update((s) => {
    const req = (s.marketRequests || []).find((r) => r.id === id);
    if (!req || s.wallet < req.cost) return s;
    s.wallet -= req.cost;
    addLedger(s, `Canje aprobado: ${req.itemName}`, -req.cost, "out", "mercado");
    s.marketRequests = trimMarketRequestHistory(s.marketRequests.map((r) => (r.id === id ? { ...r, status: "approved" } : r)));
    s.totalRedeemsApproved = (s.totalRedeemsApproved || 0) + 1;
    s.milestones = {
      ...s.milestones,
      firstRedeem: req.custom ? s.milestones?.firstRedeem : true,
      firstCustomRedeem: req.custom ? true : s.milestones?.firstCustomRedeem,
      tenRedeems: s.milestones?.tenRedeems || s.totalRedeemsApproved >= 10,
    };
    if (req.cost > (s.maxRedeemCost || 0)) {
      s.maxRedeemCost = req.cost;
      s.milestones.bigSpender = true;
    }
    return pushNotification(s, `¡Tu canje de "${req.itemName}" fue aprobado! 🎉`, "child", "mercado");
  });
  const declineRedeem = (id) => update((s) => {
    const req = (s.marketRequests || []).find((r) => r.id === id);
    if (!req) return s;
    s.marketRequests = trimMarketRequestHistory(s.marketRequests.map((r) => (r.id === id ? { ...r, status: "declined" } : r)));
    return pushNotification(s, `Tu canje de "${req.itemName}" fue rechazado.`, "child", "mercado");
  });

  const applyFine = (reason, amount) => update((s) => {
    const appealDays = s.fineAppealDays || 3;
    s.fines = [
      { id: uid(), reason, amount, remaining: amount, status: "pending", date: todayLabel(), dateKey: dateKey(), appealDeadline: addDaysKey(dateKey(), appealDays) },
      ...(s.fines || []),
    ];
    return pushNotification(s, `Nueva multa: ${reason} (${fmt(amount)} €). Tienes ${appealDays} día${appealDays === 1 ? "" : "s"} para apelarla antes de que se cobre sola.`, "child", "ayuntamiento");
  });
  const repayFine = (id, amount) => update((s) => {
    const fine = (s.fines || []).find((f) => f.id === id);
    if (!fine) return s;
    const pay = Math.min(amount, s.wallet, fine.remaining);
    if (pay <= 0) return s;
    s.wallet -= pay;
    addLedger(s, `Pago de multa: ${fine.reason}`, -pay, "out", "ayuntamiento");
    let justPaidOff = false;
    s.fines = s.fines.map((f) => {
      if (f.id !== id) return f;
      const remaining = f.remaining - pay;
      if (remaining <= 0) justPaidOff = true;
      return { ...f, remaining: remaining <= 0 ? 0 : remaining, status: remaining <= 0 ? "paid" : "pending" };
    });
    if (justPaidOff) s.milestones = { ...s.milestones, firstFinePaid: true };
    s.fines = trimFineHistory(s.fines);
    return s;
  });
  // El hijo/a apela una multa en vez de pagarla directamente.
  const disputeFine = (id, reason) => update((s) => {
    s.fines = (s.fines || []).map((f) => (f.id === id ? { ...f, status: "disputed", disputeReason: reason } : f));
    return pushNotification(s, `Tu hijo/a ha apelado una multa: "${reason}"`, "parent", "ayuntamiento");
  });
  // El padre/madre da la razón al hijo/a: se perdona la multa, sin pagar nada.
  const approveFineDispute = (id) => update((s) => {
    const fine = (s.fines || []).find((f) => f.id === id);
    if (!fine) return s;
    s.fines = trimFineHistory(s.fines.map((f) => (f.id === id ? { ...f, status: "waived", remaining: 0 } : f)));
    s.milestones = { ...s.milestones, firstDisputeWon: true };
    return pushNotification(s, `Tu apelación fue aceptada: la multa "${fine.reason}" queda perdonada.`, "child", "ayuntamiento");
  });
  // El padre/madre no da la razón: la multa vuelve a estar pendiente de pago.
  const rejectFineDispute = (id, reason) => update((s) => {
    const fine = (s.fines || []).find((f) => f.id === id);
    if (!fine) return s;
    s.fines = s.fines.map((f) => (f.id === id ? { ...f, status: "pending", disputeReason: null, disputeRejectReason: reason } : f));
    return pushNotification(s, `Tu apelación de "${fine.reason}" no fue aceptada: "${reason}"`, "child", "ayuntamiento");
  });

  // Buzón de peticiones: el hijo/a le escribe algo al padre/madre (una
  // idea, una queja, una petición) — no es una solicitud con aprobar/
  // rechazar, solo un mensaje que el padre marca como leído.
  const sendPetition = (text) => update((s) => {
    s.petitions = [{ id: uid(), text, date: todayLabel(), status: "pending" }, ...(s.petitions || [])];
    s.milestones = { ...s.milestones, firstPetition: true };
    return pushNotification(s, `Tu hijo/a te ha escrito algo en el buzón del Ayuntamiento.`, "parent", "ayuntamiento");
  });
  const markPetitionRead = (id) => update((s) => {
    s.petitions = trimPetitionHistory((s.petitions || []).map((p) => (p.id === id ? { ...p, status: "read" } : p)));
    return s;
  });

  const approveLoan = (id) => update((s) => {
    const loan = s.loans.find((l) => l.id === id);
    if (!loan) return s;
    s.wallet = round2(s.wallet + loan.amount);
    addLedger(s, `Préstamo aprobado: ${loan.reason}`, loan.amount, "in", "banco");
    // El interés mínimo de 1 mes se cobra ya aquí, no al pagar — así lo que
    // se debe ("remaining") es correcto desde el primer día, sin sorpresas
    // en el momento de devolverlo.
    const minInterest = round2(loan.amount * (loan.interestRate || 0));
    let totalRemaining = loan.amount;
    if (minInterest > 0) {
      totalRemaining = round2(loan.amount + minInterest);
      addLedger(s, `Interés mínimo de préstamo (1 mes): ${loan.reason}`, minInterest, "debt", "banco");
    }
    s.loans = s.loans.map((l) =>
      l.id === id
        ? { ...l, status: "active", remaining: totalRemaining, totalInterestPaid: minInterest, lastPaymentDate: dateKey(), overdueNotified: false, interestChargedCount: minInterest > 0 ? 1 : 0 }
        : l
    );
    return s;
  });
  const rejectLoan = (id) => update((s) => { s.loans = s.loans.map((l) => (l.id === id ? { ...l, status: "rejected" } : l)); return s; });

  // El padre/madre registra un regalo de un familiar (abuelo/a, tío/a...)
  // que no usa la app. Se aplica el impuesto configurable antes de sumarlo
  // a la cartera del hijo/a.
  const addFamilyGift = (giverName, grossAmount) => update((s) => {
    const rate = s.familyGiftTaxRate || 0;
    const tax = round2(grossAmount * rate);
    const net = round2(grossAmount - tax);
    s.wallet = round2(s.wallet + net);
    s.familyGifts = trimFamilyGiftHistory([{ id: uid(), giverName, gross: grossAmount, tax, net, date: todayLabel() }, ...(s.familyGifts || [])]);
    s.milestones = { ...s.milestones, firstFamilyGift: true };
    addLedger(s, `Regalo de ${giverName}${tax > 0 ? ` (impuesto: -${fmt(tax)} €)` : ""}`, net, "in", "donaciones");
    return pushNotification(s, `¡${giverName} te ha dado ${fmt(net)} € de regalo!`, "child", "donaciones");
  });

  const emptyPot = () => update((s) => {
    if (s.donationPot.total <= 0) return s;
    addLedger(s, `${s.donationPot.name} entregado (${fmt(s.donationPot.total)} €)`, 0, "neutral", "donaciones");
    s.donationHistory = trimDonationHistory([{ id: uid(), name: s.donationPot.name, amount: s.donationPot.total, date: todayLabel() }, ...(s.donationHistory || [])]);
    s.donationPot = { ...s.donationPot, total: 0 };
    return s;
  });

  const addMandatoryTask = (title, penalty, emoji, days) => update((s) => { s.mandatoryTasks = [...s.mandatoryTasks, { id: uid(), title, penalty, emoji: emoji || "✅", days: days && days.length ? days : [...WEEKDAYS] }]; return s; });
  const updateMandatoryTaskDays = (taskId, days) => update((s) => {
    s.mandatoryTasks = s.mandatoryTasks.map((t) => (t.id === taskId ? { ...t, days } : t));
    return s;
  });
  const updateMandatoryTask = (taskId, updates) => update((s) => {
    s.mandatoryTasks = s.mandatoryTasks.map((t) => (t.id === taskId ? { ...t, ...updates } : t));
    return s;
  });
  const deleteMandatoryTask = (id) => update((s) => {
    s.mandatoryTasks = s.mandatoryTasks.filter((t) => t.id !== id);
    const entries = { ...(s.mandatoryDay.entries || {}) };
    delete entries[id];
    s.mandatoryDay = { ...s.mandatoryDay, entries };
    return s;
  });
  const confirmTask = (taskId) => update((s) => {
    const entries = { ...(s.mandatoryDay.entries || {}) };
    const wasAlreadyVerified = entries[taskId]?.verified;
    entries[taskId] = { checked: true, verified: true };
    s.mandatoryDay = { ...s.mandatoryDay, entries };
    if (!wasAlreadyVerified) {
      s.totalTasksCompleted = (s.totalTasksCompleted || 0) + 1;
      s.milestones = {
        ...s.milestones,
        hundredTasks: s.milestones?.hundredTasks || s.totalTasksCompleted >= 100,
        fiveHundredTasks: s.milestones?.fiveHundredTasks || s.totalTasksCompleted >= 500,
      };
    }
    return s;
  });
  const cancelTask = (taskId) => update((s) => {
    const entries = { ...(s.mandatoryDay.entries || {}) };
    entries[taskId] = { checked: false, verified: false };
    s.mandatoryDay = { ...s.mandatoryDay, entries };
    return s;
  });

  const saveSettings = (settings) => update((s) => ({ ...s, ...settings }));

  if (!loaded || !state) {
    return (
      <div className="eco-app loading-screen">
        <Loader2 className="spin" size={28} />
        <p className="eco-mono">Abriendo la caja fuerte…</p>
      </div>
    );
  }

  const pendingCount =
    state.jobs.filter((j) => j.status === "submitted" || j.status === "negotiating").length +
    state.loans.filter((l) => l.status === "requested").length +
    (state.marketRequests || []).filter((r) => r.status === "pending").length +
    state.mandatoryTasks.filter((t) => {
      const e = state.mandatoryDay.entries[t.id];
      return e && e.checked && !e.verified;
    }).length;

  return (
    <div className="eco-app">
      <header className="eco-header">
        <div className="eco-header-top">
          <div className="eco-header-text">
            <p className="header-slogan">APRENDE JUGANDO</p>
            <h1 ref={brandRef} className="eco-display brand">El Mundo de {state.childName}</h1>
          </div>
          <div className="eco-header-logo"><Logo size={72} /></div>
        </div>
        <div className="window-tabs">
          <button className={`window-tab ${role === "child" ? "active" : ""}`} onClick={() => setRole("child")}>
            <Unlock size={14} /> Mi rincón
          </button>
          <button className={`window-tab ${role !== "child" ? "active" : ""}`} onClick={() => setRole(role === "child" ? "gate" : role)}>
            <Lock size={14} /> Oficina · Adultos
            {pendingCount > 0 && <span className="badge">{pendingCount}</span>}
          </button>
          {role !== "child" && onSwitchChild && (
            <button className="window-tab" onClick={onSwitchChild} title="Cambiar de hijo/a">
              🧒⇄
            </button>
          )}
          {onLogout && (
            <button className="window-tab" onClick={onLogout} title="Cerrar sesión">
              ⎋
            </button>
          )}
        </div>
      </header>

      {saveError && <div className="save-warning eco-mono">{saveError}</div>}

      {role === "child" && (
        <ChildView
          state={state}
          childId={childId}
          familyChildren={familyChildren}
          sharedJobs={sharedJobs}
          onClaimSharedJob={claimSharedJobAction}
          onSubmitSharedJob={submitSharedJobAction}
          onRefreshSharedJobs={refreshSharedJobs}
          onDonateToSibling={donateToSibling}
          onClaim={claimJob}
          onSubmit={submitJob}
          onNegotiate={proposeNegotiation}
          onRequestJob={requestJob}
          onCancelJobRequest={cancelJobRequest}
          onAcknowledgeJobRequest={acknowledgeJobRequest}
          onRequestRedeem={requestRedeem}
          onRequestCustomItem={requestCustomItem}
          onDeposit={transferToSavings}
          onRenewDeposit={renewDeposit}
          onKeepDepositFree={keepDepositFree}
          onSetDepositPreDecision={setDepositPreDecision}
          onWithdraw={withdrawFromSavings}
          onSetGoal={setGoal}
          onRequestLoan={requestLoan}
          onRepayLoan={repayLoan}
          onRepayFine={repayFine}
          onDisputeFine={disputeFine}
          onSendPetition={sendPetition}
          onDonate={donate}
          onToggleChildCheck={toggleChildCheck}
          onDismissNotification={dismissNotification}
        />
      )}

      {role === "gate" && (
        <div className="card gate-card">
          <Gavel size={26} />
          <h2 className="eco-display">Acceso de la Oficina</h2>
          <p className="section-sub">Introduce el PIN de adultos para entrar.</p>
          <input
            className="pin-input eco-mono"
            type="password"
            inputMode="numeric"
            maxLength={8}
            value={pinInput}
            onChange={(e) => { setPinInput(e.target.value); setPinError(""); }}
            placeholder="••••"
          />
          {pinError && <p className="pin-error eco-mono">{pinError}</p>}
          <div className="row-buttons">
            <button
              className="btn primary"
              onClick={() => {
                const effectivePin = officePin || state.parentPin;
                if (pinInput === effectivePin) { setRole("parent"); setPinInput(""); }
                else setPinError("PIN incorrecto. Inténtalo de nuevo.");
              }}
            >
              Entrar
            </button>
            <button className="btn ghost" onClick={() => { setRole("child"); setPinInput(""); setPinError(""); }}>
              Cancelar
            </button>
          </div>
          <p className="tiny-hint">PIN de fábrica: 0000 (cámbialo en Ajustes).</p>
        </div>
      )}

      {role === "parent" && (
        <ParentView
          state={state}
          childId={childId}
          familyId={familyId}
          familyChildren={familyChildren}
          onResetChildPin={resetChildPinAction}
          sharedJobs={sharedJobs}
          onCreateSharedJob={createSharedJob}
          onDeleteSharedJob={deleteSharedJobAction}
          onApproveSharedJob={approveSharedJobAction}
          onRejectSharedJob={rejectSharedJobAction}
          onAddJob={addJob}
          onDeleteJob={deleteJob}
          onApproveJob={approveJob}
          onRejectJob={rejectJob}
          onAcceptNegotiation={acceptNegotiation}
          onAcceptJobRequest={acceptJobRequest}
          onCorrectTaskHistoryEntry={correctTaskHistoryEntry}
          onDeclineJobRequest={declineJobRequest}
          onDeclineNegotiation={declineNegotiation}
          onAddMarketItem={addMarketItem}
          onDeleteMarketItem={deleteMarketItem}
          onApproveRedeem={approveRedeem}
          onDeclineRedeem={declineRedeem}
          onApplyFine={applyFine}
          onApproveFineDispute={approveFineDispute}
          onRejectFineDispute={rejectFineDispute}
          onMarkPetitionRead={markPetitionRead}
          onApproveLoan={approveLoan}
          onRejectLoan={rejectLoan}
          onEmptyPot={emptyPot}
          onAddFamilyGift={addFamilyGift}
          onSetOfficePin={async (pin) => { await window.setOfficePin(familyId, pin); setOfficePin(pin); }}
          onAddMandatoryTask={addMandatoryTask}
          onUpdateMandatoryTaskDays={updateMandatoryTaskDays}
          onUpdateMandatoryTask={updateMandatoryTask}
          onDeleteMandatoryTask={deleteMandatoryTask}
          onConfirmTask={confirmTask}
          onCancelTask={cancelTask}
          onDismissNotification={dismissNotification}
          onSaveSettings={saveSettings}
        />
      )}

      <footer className="eco-footer eco-mono">
        Los datos de esta banca familiar se guardan en este dispositivo compartido — visibles para quien abra esta misma ventana.
      </footer>
    </div>
  );
}

const SCREENS = [
  { key: "tasks", label: "Tareas", icon: ClipboardCheck, color: "var(--accent-blue)" },
  { key: "jobs", label: "Trabajos", icon: Briefcase, color: "var(--accent-yellow)" },
  { key: "bank", label: "Banco", icon: Landmark, color: "var(--secondary)" },
  { key: "townhall", label: "Ayuntamiento", icon: TownHall, color: "#8B7BD8" },
  { key: "market", label: "Mercado", icon: ShoppingBag, color: "var(--primary)" },
  { key: "badges", label: "Insignias", icon: Award, color: "#FFB74D" },
  { key: "donations", label: "Donar", icon: HeartHandshake, color: "var(--accent-blue)" },
  { key: "ledger", label: "Historial", icon: ScrollText, color: "#B8B0D8" },
];

const PARENT_SCREENS = [
  { key: "tasks", label: "Tareas", icon: ClipboardCheck, color: "var(--accent-blue)" },
  { key: "jobs", label: "Trabajos", icon: Briefcase, color: "var(--accent-yellow)" },
  { key: "bank", label: "Banco", icon: Landmark, color: "var(--secondary)" },
  { key: "townhall", label: "Ayuntamiento", icon: TownHall, color: "#8B7BD8" },
  { key: "market", label: "Mercado", icon: ShoppingBag, color: "var(--primary)" },
  { key: "donations", label: "Bote solidario", icon: HeartHandshake, color: "var(--accent-blue)" },
  { key: "ledger", label: "Historial", icon: ScrollText, color: "#B8B0D8" },
  { key: "settings", label: "Ajustes", icon: Settings, color: "#B8B0D8" },
];

// Un icono distinto según lo largo que sea el plazo, para verlo de un
// vistazo sin tener que leer el número: el más corto = semilla, el más
// largo = árbol, los de en medio = brote.
function termIcon(months, terms) {
  const sorted = [...(terms || [])].sort((a, b) => a.months - b.months);
  if (sorted.length === 0) return "💰";
  if (months === sorted[0].months) return "🌱";
  if (months === sorted[sorted.length - 1].months) return "🌳";
  return "🌿";
}

function ChildView({
  state, childId, familyChildren, sharedJobs, onClaimSharedJob, onSubmitSharedJob, onRefreshSharedJobs, onDonateToSibling,
  onClaim, onSubmit, onNegotiate, onRequestRedeem, onRequestCustomItem, onDeposit, onWithdraw, onSetGoal, onRenewDeposit, onKeepDepositFree, onSetDepositPreDecision,
  onRequestLoan, onRepayLoan, onRepayFine, onDisputeFine, onSendPetition, onDonate, onToggleChildCheck, onDismissNotification,
  onRequestJob, onCancelJobRequest, onAcknowledgeJobRequest,
}) {
  const [screen, setScreen] = useState("dashboard");
  const [jobRequestAmount, setJobRequestAmount] = useState("");
  const [bankTab, setBankTab] = useState("goal");
  const [loansHistoryExpanded, setLoansHistoryExpanded] = useState(false);
  const [depositAmt, setDepositAmt] = useState("");
  const [depositTerm, setDepositTerm] = useState(state.savingsTerms?.[0]?.months || 1);
  const [depositsExpanded, setDepositsExpanded] = useState(false);
  const [withdrawAmt, setWithdrawAmt] = useState("");
  const [negotiatingId, setNegotiatingId] = useState(null);
  const [negoAmount, setNegoAmount] = useState("");
  const [negoNote, setNegoNote] = useState("");
  const [goalEdit, setGoalEdit] = useState(false);
  const [goalName, setGoalName] = useState(state.goal?.name || "");
  const [goalTarget, setGoalTarget] = useState(state.goal?.target || "");
  const [goalPhoto, setGoalPhoto] = useState(state.goal?.photo || null);
  const [loanAmount, setLoanAmount] = useState("");
  const [loanReason, setLoanReason] = useState("");
  const [repayAmounts, setRepayAmounts] = useState({});
  const [fineRepayAmounts, setFineRepayAmounts] = useState({});
  const [disputingFineId, setDisputingFineId] = useState(null);
  const [petitionText, setPetitionText] = useState("");
  const [customItemName, setCustomItemName] = useState("");
  const [customItemCost, setCustomItemCost] = useState("");
  const [petitionsHistoryExpanded, setPetitionsHistoryExpanded] = useState(false);
  const [disputeReasonInput, setDisputeReasonInput] = useState("");
  const [finesHistoryExpanded, setFinesHistoryExpanded] = useState(false);
  const [donateAmt, setDonateAmt] = useState("");
  const [siblingTarget, setSiblingTarget] = useState("");
  const [siblingAmt, setSiblingAmt] = useState("");
  const [giftsHistoryExpanded, setGiftsHistoryExpanded] = useState(false);
  const [donationHistoryExpanded, setDonationHistoryExpanded] = useState(false);
  const [streakExpanded, setStreakExpanded] = useState(false);
  const [calendarExpanded, setCalendarExpanded] = useState(false);
  const [claimingSharedId, setClaimingSharedId] = useState(null);
  const [claimError, setClaimError] = useState("");
  const [jobsHistoryExpandedChild, setJobsHistoryExpandedChild] = useState(false);
  const [ledgerFilter, setLedgerFilter] = useState("todo");
  const [requestsHistoryExpanded, setRequestsHistoryExpanded] = useState(false);

  const today = dateKey();
  const available = state.jobs.filter((j) => j.status === "available");
  const mine = state.jobs.filter((j) => j.status === "claimed" || j.status === "submitted" || j.status === "negotiating");
  const sharedOpen = (sharedJobs || []).filter((j) => j.status === "open" && (j.targetChildIds || []).includes(childId) && (!j.expiresAt || j.expiresAt >= today));
  const mySharedInProgress = (sharedJobs || []).filter((j) => j.claimedBy === childId && (j.status === "claimed" || j.status === "submitted"));
  const net = state.wallet + state.savings;
  const goalPct = state.goal ? (state.savings / state.goal.target) * 100 : 0;
  const myLoans = state.loans.filter((l) => l.status !== "rejected");
  const overdueLoan = state.loans.find(
    (l) => l.status === "active" && l.remaining > 0 && l.lastPaymentDate && daysBetween(l.lastPaymentDate, today) >= (state.loanOverdueDays || 30)
  );
  const activeLoans = state.loans.filter((l) => l.status === "active");
  const pendingFines = (state.fines || []).filter((f) => f.status === "pending" || f.status === "disputed");
  const unread = (state.notifications || []).filter((n) => !n.read && n.for !== "parent");
  const lockedTotal = lockedSavingsTotal(state);
  const availableSavings = Math.max(0, state.savings - lockedTotal);
  const pendingRequests = (state.marketRequests || []).filter((r) => r.status === "pending").length;
  const tasksToday = (state.mandatoryTasks || []).filter((t) => taskAppliesOn(t, today));
  const tasksPending = tasksToday.filter((t) => !(state.mandatoryDay.entries[t.id] || {}).verified).length;
  const unlockedBadges = BADGES.filter((b) => state.milestones?.[b.key]).length;

  const badges = {
    tasks: tasksPending,
    jobs: mine.length,
    bank: (state.savingsLocks || []).filter((l) => l.status === "matured").length + activeLoans.length,
    townhall: pendingFines.length,
    market: pendingRequests,
    badges: 0,
  };

  const notifBanner = unread.length > 0 && (
    <div className="notif-stack">
      {unread.map((n) => (
        <div className="notif-card" key={n.id}>
          <Bell size={15} />
          <span>{n.message}</span>
          <button className="icon-btn" onClick={() => onDismissNotification(n.id)}><X size={15} /></button>
        </div>
      ))}
    </div>
  );

  if (screen !== "dashboard") {
    const meta = SCREENS.find((s) => s.key === screen);
    return (
      <div className="stack">
        {notifBanner}
        <button className="btn ghost small back-btn" onClick={() => setScreen("dashboard")}>
          <ArrowLeft size={14} /> Volver al panel
        </button>

        {screen === "tasks" && (
          <>
            <Section icon={ClipboardCheck} title="Tareas y rachas" subtitle="Marca las tareas al completarlas — un adulto debe confirmarlas para que no te descuenten dinero. Cúmplelas varios días seguidos para ganar un extra." accent="var(--accent-blue)" />
            {state.streakLevelsActive !== false && tasksToday.length > 0 && (() => {
              const count = state.streak?.count || 0;
              const record = state.streak?.record || 0;
              const levels = [...(state.streakLevels || [])].sort((a, b) => a.days - b.days);
              const next = nextStreakLevel(count, levels);
              return (
                <div className="streak-reward-card">
                  {count > 0 ? (
                    <p className="streak-reward-title">🔥 ¡Llevas {count} día{count === 1 ? "" : "s"} seguido{count === 1 ? "" : "s"}! No {count === 1 ? "lo" : "los"} pierdas</p>
                  ) : (
                    <p className="streak-reward-title">🔥 ¡Empieza hoy tu racha!</p>
                  )}
                  <p className="streak-reward-text">
                    {next ? (
                      <>Consigue <b>{next.days - count} día{next.days - count === 1 ? "" : "s"} más</b> para desbloquear el nivel {levels.findIndex((l) => l === next)} y ganar <b>{fmt(next.amount)} €</b>.</>
                    ) : levels.length > 0 ? (
                      <>¡Has alcanzado el nivel máximo! Sigue así para mantener tu récord.</>
                    ) : (
                      <>Pide a tu adulto que configure los niveles de racha.</>
                    )}
                  </p>
                  {record > 0 && <p className="streak-record-badge">🏆 Tu récord: {record} día{record === 1 ? "" : "s"}</p>}
                  <button className="text-link streak-toggle" onClick={() => setStreakExpanded(!streakExpanded)}>
                    {streakExpanded ? "Ocultar niveles" : "Ver niveles"}
                  </button>
                  {streakExpanded && (
                    <div className="streak-levels">
                      {levels.map((lvl, i) => {
                        const done = count >= lvl.days;
                        const isNext = next && next.days === lvl.days;
                        return (
                          <div className="streak-level-row" key={i}>
                            <span className="streak-level-icon">{done ? "✅" : isNext ? "▶️" : "🔒"}</span>
                            <span className="streak-level-text">Nivel {i}: {lvl.days} días</span>
                            <Money value={lvl.amount} size="sm" />
                          </div>
                        );
                      })}
                    </div>
                  )}
                </div>
              );
            })()}
            {tasksToday.length === 0 && (
              <p className="empty-hint">
                {state.mandatoryTasks.length === 0 ? "No hay tareas obligatorias configuradas todavía." : "Hoy no tienes ninguna tarea obligatoria. ¡Día libre!"}
              </p>
            )}
            {tasksToday.length > 0 && (
              <div className="card">
                <p className="card-title" style={{ marginTop: 0 }}>Tareas programadas para hoy ({todayLabel()})</p>
                <p className="section-sub" style={{ marginBottom: "0.7rem" }}>Marca las tareas que hayas hecho para que tu padre o madre las confirme.</p>
                <div className="task-list">
                  {tasksToday.map((t) => {
                    const e = state.mandatoryDay.entries[t.id] || { checked: false, verified: false };
                    return (
                      <div className="task-row" key={t.id}>
                        <span className="task-emoji">{t.emoji || "✅"}</span>
                        <span className="task-title">{t.title}</span>
                        <span className="tiny-hint">−{fmt(t.penalty)} € si no se confirma</span>
                        {e.verified ? (
                          <Stamp text="CONFIRMADO" tone="green" />
                        ) : e.checked ? (
                          <>
                            <Stamp text="PENDIENTE DE CONFIRMAR" tone="orange" />
                            <button className="btn ghost small" onClick={() => onToggleChildCheck(t.id)}>Cancelar</button>
                          </>
                        ) : (
                          <>
                            <Stamp text="PENDIENTE DE HACER" tone="red" />
                            <button className="btn primary small" onClick={() => onToggleChildCheck(t.id)}>Realizado</button>
                          </>
                        )}
                      </div>
                    );
                  })}
            </div>
              </div>
            )}

            <div className="card calendar-card">
              <p className="card-title" style={{ marginTop: 0 }}>Calendario de cumplimiento</p>
              <p className="section-sub" style={{ marginBottom: "0.7rem" }}>Aquí abajo puedes ver qué días cumpliste tus tareas y cuáles tienes programadas para los próximos.</p>
              <button className="text-link" onClick={() => setCalendarExpanded(!calendarExpanded)}>
                {calendarExpanded ? "Ocultar calendario" : "Ver calendario"}
              </button>
              {calendarExpanded && (() => {
                const days = [];
                for (let i = 7; i >= 1; i--) days.push({ date: addDaysKey(today, -i), kind: "past" });
                days.push({ date: today, kind: "today" });
                for (let i = 1; i <= 6; i++) days.push({ date: addDaysKey(today, i), kind: "future" });
                const historyByDate = {};
                (state.taskHistory || []).forEach((h) => { historyByDate[h.date] = h; });
                return (
                  <div className="calendar-list">
                    {days.map((d) => {
                      const wd = weekdayCodeForDate(d.date);
                      let chips = [];
                      if (d.kind === "past") {
                        const h = historyByDate[d.date];
                        chips = h && h.tasks ? h.tasks.map((t) => ({ ...t, cls: t.done ? "done" : "fail" })) : [];
                      } else if (d.kind === "today") {
                        chips = tasksToday.map((t) => {
                          const e = state.mandatoryDay.entries[t.id] || {};
                          const cls = e.verified ? "done" : e.checked ? "pending" : "fail";
                          return { title: t.title, emoji: t.emoji || "✅", cls };
                        });
                      } else {
                        chips = (state.mandatoryTasks || [])
                          .filter((t) => taskAppliesOn(t, d.date))
                          .map((t) => ({ title: t.title, emoji: t.emoji || "✅", cls: "future" }));
                      }
                      return (
                        <div className={`calendar-day-row ${d.kind === "today" ? "cal-today" : ""}`} key={d.date}>
                          <span className="calendar-day-label">{wd}</span>
                          <div className="calendar-day-chips">
                            {chips.length === 0 && <span className="tiny-hint">Sin tareas</span>}
                            {chips.map((c, i) => (
                              <span className={`cal-task-chip ${c.cls}`} key={i}>{c.emoji} {c.title}</span>
                            ))}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                );
              })()}
            </div>
          </>
        )}

        {screen === "jobs" && (
          <>
            <Section icon={Briefcase} title="Bolsa de trabajo" subtitle="Elige una oferta, negocia el precio o complétala para cobrar. También podrás solicitar ofertas de trabajo a tus padres." accent="var(--accent-yellow)" />

            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>¿Buscas trabajo?</p>
              {state.jobRequest && state.jobRequest.status === "declined" ? (
                <>
                  <p className="tiny-hint" style={{ marginTop: 0 }}>
                    Tu solicitud de <b>{fmt(state.jobRequest.amount)} €</b> no se va a convertir en oferta por ahora: <i>"{state.jobRequest.declineReason}"</i>
                  </p>
                  <button className="btn primary small" onClick={onAcknowledgeJobRequest}>Entendido</button>
                </>
              ) : state.jobRequest ? (
                <>
                  <p className="tiny-hint" style={{ marginTop: 0 }}>
                    Le has dicho a tu adulto que buscas trabajo por <b>{fmt(state.jobRequest.amount)} €</b>. Espera a que te publique una oferta.
                  </p>
                  <button className="btn ghost small" onClick={onCancelJobRequest}>Cancelar solicitud</button>
                </>
              ) : (
                <>
                  <p className="tiny-hint" style={{ marginTop: 0 }}>Dile a tu adulto que quieres ganar dinero — él decidirá qué trabajo ofrecerte.</p>
                  <div className="input-row">
                    <input className="amount-input eco-mono" type="number" min="0" placeholder="0,00" value={jobRequestAmount} onChange={(e) => setJobRequestAmount(e.target.value)} />
                    <button
                      className="btn primary small"
                      onClick={() => { if (!jobRequestAmount) return; onRequestJob(parseFloat(jobRequestAmount)); setJobRequestAmount(""); }}
                    >
                      Estoy buscando trabajo
                    </button>
                  </div>
                </>
              )}
            </div>

            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>Ofertas de trabajo publicadas</p>
              <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Acepta una oferta tal cual está, o proponle otro precio a tu adulto antes de aceptarla.</p>
              {sharedOpen.length > 0 && (
                <p className="tiny-hint" style={{ marginTop: 0 }}>Las marcadas con 🏆 también las puede ver algún hermano/a — ¡el primero que las acepte se las lleva!</p>
              )}
              {claimError && <div className="error-box">{claimError}</div>}
              {available.length === 0 && sharedOpen.length === 0 && <p className="empty-hint">No hay ofertas disponibles ahora mismo.</p>}
              <div className="task-list">
                {sharedOpen.map((j) => {
                  const daysLeft = j.expiresAt ? daysBetween(today, j.expiresAt) : null;
                  const others = (j.targetChildNames || []).filter((n) => n !== state.childName);
                  return (
                    <div className="task-row" key={j.id}>
                      <span className="task-emoji">🏆</span>
                      <span className="task-title">{j.title}</span>
                      <Money value={j.reward} size="sm" />
                      {others.length > 0 && <span className="tiny-hint">También la ve: {others.join(", ")}</span>}
                      {daysLeft !== null && (
                        <span className={`countdown-chip ${daysLeft <= 1 ? "urgent" : ""}`}>
                          ⏳ {daysLeft <= 0 ? "Caduca hoy" : `Caduca en ${daysLeft} día${daysLeft === 1 ? "" : "s"}`}
                        </span>
                      )}
                      <button
                        className="btn primary small"
                        disabled={claimingSharedId === j.id}
                        onClick={async () => {
                          setClaimingSharedId(j.id); setClaimError("");
                          const result = await onClaimSharedJob(j.id);
                          setClaimingSharedId(null);
                          if (!result.ok) setClaimError("¡Vaya! Tu hermano/a se te ha adelantado con esta oferta.");
                        }}
                      >
                        {claimingSharedId === j.id ? "Aceptando…" : "Aceptar"}
                      </button>
                    </div>
                  );
                })}
                {available.map((j) => {
                  const daysLeft = j.expiresAt ? daysBetween(today, j.expiresAt) : null;
                  return negotiatingId === j.id ? (
                    <div className="task-row job-nego-row" key={j.id}>
                      <span className="task-emoji">💼</span>
                      <span className="task-title">{j.title}</span>
                      <div className="nego-form">
                        <input className="amount-input eco-mono" type="number" min="0" placeholder="Tu precio" value={negoAmount} onChange={(e) => setNegoAmount(e.target.value)} />
                        <input className="text-input" placeholder="Motivo (opcional)" value={negoNote} onChange={(e) => setNegoNote(e.target.value)} />
                        <div className="row-buttons tight">
                          <button
                            className="btn primary small"
                            onClick={() => {
                              if (!negoAmount) return;
                              onNegotiate(j.id, parseFloat(negoAmount), negoNote.trim());
                              setNegotiatingId(null); setNegoAmount(""); setNegoNote("");
                            }}
                          >
                            Enviar
                          </button>
                          <button className="btn ghost small" onClick={() => setNegotiatingId(null)}>Cancelar</button>
                        </div>
                      </div>
                    </div>
                  ) : (
                    <div className="task-row" key={j.id}>
                      <span className="task-emoji">💼</span>
                      <span className="task-title">{j.title}</span>
                      <Money value={j.reward} size="sm" />
                      {daysLeft !== null && (
                        <span className={`countdown-chip ${daysLeft <= 1 ? "urgent" : ""}`}>
                          ⏳ {daysLeft <= 0 ? "Caduca hoy" : `Caduca en ${daysLeft} día${daysLeft === 1 ? "" : "s"}`}
                        </span>
                      )}
                      <div className="row-buttons tight">
                        <button className="btn primary small" onClick={() => onClaim(j.id)}>Aceptar</button>
                        <button className="btn ghost small" onClick={() => setNegotiatingId(j.id)}>
                          <MessageSquare size={13} /> Negociar
                        </button>
                      </div>
                    </div>
                  );
                })}
              </div>

              {(() => {
                const privateHistory = state.jobs.filter((j) => j.status === "approved" || j.status === "expired" || j.status === "cancelled");
                const sharedHistory = (sharedJobs || []).filter(
                  (j) => (j.targetChildIds || []).includes(childId) && (j.status === "approved" || j.status === "cancelled" || (j.status === "open" && j.expiresAt && j.expiresAt < today))
                );
                if (privateHistory.length === 0 && sharedHistory.length === 0) return null;
                return (
                  <>
                    <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setJobsHistoryExpandedChild(!jobsHistoryExpandedChild)}>
                      {jobsHistoryExpandedChild ? "Ocultar historial" : "Ver historial*"}
                    </button>
                    {jobsHistoryExpandedChild && (
                      <>
                        <div className="table" style={{ marginTop: "0.5rem" }}>
                          {privateHistory.map((j) => (
                            <div className="table-row" key={j.id}>
                              <span>{j.title}</span>
                              <Money value={j.reward} size="sm" />
                              <Stamp
                                text={j.status === "approved" ? "PAGADA" : j.status === "expired" ? "CADUCADA" : "CANCELADA"}
                                tone={j.status === "approved" ? "green" : "red"}
                              />
                            </div>
                          ))}
                          {sharedHistory.map((j) => (
                            <div className="table-row" key={j.id}>
                              <span>🏆 {j.title}{j.claimedByName && <span className="tiny-hint"> · se la quedó {j.claimedByName}</span>}</span>
                              <Money value={j.reward} size="sm" />
                              <Stamp
                                text={j.status === "approved" ? "PAGADA" : j.status === "cancelled" ? "CANCELADA" : "CADUCADA"}
                                tone={j.status === "approved" ? "green" : "red"}
                              />
                            </div>
                          ))}
                        </div>
                        <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 ofertas resueltas.</p>
                      </>
                    )}
                  </>
                );
              })()}
            </div>

            {(mine.length > 0 || mySharedInProgress.length > 0) && (
              <div className="card">
                <p className="card-title" style={{ marginTop: 0 }}>Mis trabajos en curso</p>
                <div className="task-list">
                  {mine.map((j) => (
                    <div className="task-row" key={j.id}>
                      <span className="task-emoji">💼</span>
                      <span className="task-title">{j.title}</span>
                      <Money value={j.status === "negotiating" ? j.proposedReward : j.reward} size="sm" />
                      {j.status === "claimed" && j.rejectReason && (
                        <p className="reject-comment">Tu adulto no lo aprobó: <i>"{j.rejectReason}"</i> — puedes intentarlo de nuevo.</p>
                      )}
                      {j.status === "claimed" && <button className="btn primary small" onClick={() => onSubmit(j.id)}>Marcar como hecho</button>}
                      {j.status === "submitted" && <Stamp text="ESPERANDO REVISIÓN" tone="gold" />}
                      {j.status === "negotiating" && <Stamp text="EN NEGOCIACIÓN" tone="gold" />}
                    </div>
                  ))}
                  {mySharedInProgress.map((j) => (
                    <div className="task-row" key={j.id}>
                      <span className="task-emoji">🏆</span>
                      <span className="task-title">{j.title}</span>
                      <Money value={j.reward} size="sm" />
                      {j.status === "claimed" && j.rejectReason && (
                        <p className="reject-comment">Tu adulto no lo aprobó: <i>"{j.rejectReason}"</i> — puedes intentarlo de nuevo.</p>
                      )}
                      {j.status === "claimed" && <button className="btn primary small" onClick={() => onSubmitSharedJob(j.id)}>Marcar como hecho</button>}
                      {j.status === "submitted" && <Stamp text="ESPERANDO REVISIÓN" tone="gold" />}
                    </div>
                  ))}
                </div>
              </div>
            )}
          </>
        )}

        {screen === "bank" && (
          <>
          <Section icon={Landmark} title="Banco" subtitle="Ahorra para tu meta, invierte tu dinero para que crezca, o pide un préstamo — todo desde aquí." accent="var(--secondary)" />

          <div className="window-tabs" style={{ marginBottom: "0.8rem" }}>
            <button className={`window-tab ${bankTab === "goal" ? "active" : ""}`} onClick={() => setBankTab("goal")}>Ahorro</button>
            <button className={`window-tab ${bankTab === "invest" ? "active" : ""}`} onClick={() => setBankTab("invest")}>Inversión</button>
            <button className={`window-tab ${bankTab === "loans" ? "active" : ""}`} onClick={() => setBankTab("loans")}>Préstamo</button>
          </div>

          {bankTab === "goal" && (
          <div className="card">
            <p className="card-title" style={{ marginTop: 0 }}>Tu meta de ahorro</p>
            <p className="section-sub" style={{ marginBottom: "0.7rem" }}>Ponle nombre y una foto a lo que quieres conseguir, y sigue tu progreso aquí cada vez que ahorres.</p>
            <div className="goal-block">
              {state.goal && !goalEdit ? (
                <>
                  {state.goal.photo && <img className="goal-photo-img" src={state.goal.photo} alt={state.goal.name} />}
                  <div className="goal-header">
                    <span className="eco-mono eyebrow">META: {state.goal.name.toUpperCase()}</span>
                    <button className="text-link" onClick={() => setGoalEdit(true)}>editar</button>
                  </div>
                  <ProgressBar pct={goalPct} />
                  <p className="tiny-hint">
                    {fmt(state.savings)} € de {fmt(state.goal.target)} € {goalPct >= 100 && <Stamp text="¡META CONSEGUIDA!" tone="green" />}
                  </p>
                </>
              ) : (
                <div className="form-row">
                  {goalPhoto && <img className="goal-photo-img" src={goalPhoto} alt="Vista previa" />}
                  <input className="text-input" placeholder="Nombre de la meta" value={goalName} onChange={(e) => setGoalName(e.target.value)} />
                  <input className="amount-input eco-mono" type="number" min="0" placeholder="Objetivo" value={goalTarget} onChange={(e) => setGoalTarget(e.target.value)} />
                  <label className="btn ghost small photo-upload-btn">
                    <Camera size={14} /> {goalPhoto ? "Cambiar foto" : "Añadir foto"}
                    <input
                      type="file" accept="image/*" style={{ display: "none" }}
                      onChange={(e) => {
                        const file = e.target.files && e.target.files[0];
                        if (!file) return;
                        const reader = new FileReader();
                        reader.onload = () => {
                          const img = new Image();
                          img.onload = () => {
                            const canvas = document.createElement("canvas");
                            const maxW = 400;
                            const scale = Math.min(1, maxW / img.width);
                            canvas.width = img.width * scale;
                            canvas.height = img.height * scale;
                            const ctx = canvas.getContext("2d");
                            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
                            setGoalPhoto(canvas.toDataURL("image/jpeg", 0.7));
                          };
                          img.src = reader.result;
                        };
                        reader.readAsDataURL(file);
                      }}
                    />
                  </label>
                  <button className="btn primary small" onClick={() => { onSetGoal(goalName, parseFloat(goalTarget) || 0, goalPhoto); setGoalEdit(false); }}>Guardar meta</button>
                </div>
              )}
            </div>
          </div>
          )}

          {bankTab === "invest" && (
          <>
            {(state.savingsLocks || []).filter((l) => l.status === "matured").length > 0 && (
              <div className="card">
                <p className="card-title" style={{ marginTop: 0 }}>Tus depósitos vencidos</p>
                <p className="tiny-hint" style={{ marginTop: 0 }}>Ya han cumplido su plazo. Elige: renovarlos con el interés actual, o dejarlos libres para gastar.</p>
                <div className="task-list">
                  {state.savingsLocks.filter((l) => l.status === "matured").map((l) => {
                    const term = state.savingsTerms.find((t) => t.months === (l.termMonths || 1)) || { months: 1, rate: 0 };
                    return (
                      <div className="task-row" key={l.id}>
                        <span className="task-emoji">{termIcon(l.termMonths || 1, state.savingsTerms)}</span>
                        <span className="task-title">{fmt(l.amount)} € · vencido a {l.termMonths || 1}m</span>
                        <div className="row-buttons tight">
                          <button className="btn primary small" onClick={() => onRenewDeposit(l.id)}>Renovar ({(term.rate * 100).toFixed(1)}%)</button>
                          <button className="btn ghost small" onClick={() => onKeepDepositFree(l.id)}>Dejar libre</button>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            <div className="card">
            <p className="card-title" style={{ marginTop: 0 }}>Haz crecer tu dinero</p>
            <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Cuanto más tiempo dejes tu dinero quieto, más crece: el interés es mensual y se suma cada mes, también sobre lo que ya has ganado. Eso sí, mientras tanto se queda bloqueado, como el propio depósito.</p>

            <div className="streak-reward-card">
              <p className="streak-reward-title">🧮 Calcula cuánto ganarías</p>
              {(() => {
                const term = state.savingsTerms.find((t) => t.months === depositTerm) || { rate: 0 };
                const base = depositAmt && parseFloat(depositAmt) > 0 ? parseFloat(depositAmt) : 10;
                const final = base * Math.pow(1 + term.rate, depositTerm);
                const gain = final - base;
                const unlock = addMonthsKey(dateKey(), depositTerm);
                return (
                  <p className="streak-reward-text">
                    {!depositAmt && "Por ejemplo, si"} {depositAmt ? "Si depositas" : "depositas"} <b>{fmt(base)} €</b> a {depositTerm} mes{depositTerm === 1 ? "" : "es"} ({(term.rate * 100).toFixed(1)}% mensual),
                    el {unlock.slice(8, 10)}/{unlock.slice(5, 7)} tendrías <b>{fmt(final)} €</b> (ganas {fmt(gain)} €).
                  </p>
                );
              })()}
            </div>

            <div className="bank-form">
              <label className="tiny-label">Depositar</label>
              <div className="input-row">
                <input className="amount-input eco-mono" type="number" min="0" placeholder="0,00" value={depositAmt} onChange={(e) => setDepositAmt(e.target.value)} />
                <button className="btn primary small" onClick={() => { onDeposit(parseFloat(depositAmt) || 0, depositTerm); setDepositAmt(""); }}>Ingresar</button>
              </div>
              <div className="weekday-row" style={{ marginTop: "0.4rem" }}>
                {state.savingsTerms.map((t) => (
                  <label className={`term-chip ${depositTerm === t.months ? "active" : ""}`} key={t.months}>
                    <input type="radio" checked={depositTerm === t.months} style={{ display: "none" }} onChange={() => setDepositTerm(t.months)} />
                    {termIcon(t.months, state.savingsTerms)} {t.months}m · {(t.rate * 100).toFixed(1)}%/mes
                  </label>
                ))}
              </div>
            </div>
            </div>

            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>Retirar dinero</p>
              <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Aquí puedes ver de un vistazo cuánto de tu ahorro está disponible para gastar ahora mismo, y sacarlo a tu cartera cuando quieras.</p>
              <div className="bank-form">
                <label className="tiny-label">Retirar (disponible: {fmt(availableSavings)} €)</label>
                <div className="input-row">
                  <input className="amount-input eco-mono" type="number" min="0" placeholder="0,00" value={withdrawAmt} onChange={(e) => setWithdrawAmt(e.target.value)} />
                  <button className="btn ghost small" onClick={() => { onWithdraw(parseFloat(withdrawAmt) || 0); setWithdrawAmt(""); }}>Retirar</button>
                </div>
              </div>
              <div style={{ marginTop: "0.5rem" }}>
                <ProgressBar pct={state.savings > 0 ? (availableSavings / state.savings) * 100 : 100} />
                <p className="tiny-hint" style={{ marginTop: "0.2rem" }}>Disponible: {fmt(availableSavings)} € de {fmt(state.savings)} € totales{lockedTotal > 0 && ` (bloqueado: ${fmt(lockedTotal)} €)`}</p>
              </div>
            </div>

            {(state.savingsLocks || []).filter((l) => l.status === "locked").length > 0 && (
              <div className="card">
                <p className="card-title" style={{ marginTop: 0 }}>Mis depósitos activos</p>
                <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Estos siguen bloqueados ahora mismo, ganando interés hasta su fecha de liberación.</p>
                <div className="calendar-list">
                  {state.savingsLocks.filter((l) => l.status === "locked").map((l) => {
                    const daysLeft = daysBetween(today, l.unlockDate);
                    const inNoticeWindow = daysLeft <= (state.savingsNoticeDays || 0) && daysLeft > 0;
                    const monthsLeft = Math.max(1, Math.round(daysLeft / 30.44));
                    const rate = l.rate || 0;
                    const projectedFinal = l.amount * Math.pow(1 + rate, monthsLeft);
                    const benefit = projectedFinal - l.amount;
                    return (
                      <div className="calendar-day-row" key={l.id} style={{ flexWrap: "wrap" }}>
                        <span className="calendar-day-label">{termIcon(l.termMonths || 1, state.savingsTerms)} {l.termMonths || 1}m</span>
                        <div className="calendar-day-chips">
                          <span className="cal-task-chip fail">{fmt(l.amount)} € · libre el {l.unlockDate.slice(8, 10)}/{l.unlockDate.slice(5, 7)}</span>
                          {rate > 0 && <span className="cal-task-chip done">+{fmt(benefit)} € si lo mantienes hasta el final</span>}
                        </div>
                        {inNoticeWindow && (
                          l.preDecision ? (
                            <p className="tiny-hint" style={{ flexBasis: "100%", marginTop: "0.3rem" }}>
                              Vence en {daysLeft} día{daysLeft === 1 ? "" : "s"} · ya decidiste: <b>{l.preDecision === "renew" ? "renovarlo" : "dejarlo libre"}</b>{" "}
                              <button className="text-link" style={{ fontSize: "0.68rem" }} onClick={() => onSetDepositPreDecision(l.id, null)}>cambiar</button>
                            </p>
                          ) : (
                            <div className="row-buttons tight" style={{ flexBasis: "100%", marginTop: "0.3rem" }}>
                              <span className="tiny-hint" style={{ flexBasis: "100%" }}>Vence en {daysLeft} día{daysLeft === 1 ? "" : "s"} — ¿ya sabes qué quieres hacer?</span>
                              <button className="btn primary small" onClick={() => onSetDepositPreDecision(l.id, "renew")}>Renovar cuando venza</button>
                              <button className="btn ghost small" onClick={() => onSetDepositPreDecision(l.id, "free")}>Dejarlo libre cuando venza</button>
                            </div>
                          )
                        )}
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {(state.savingsLocks || []).filter((l) => l.status === "completed").length > 0 && (
              <div className="card">
                <p className="card-title" style={{ marginTop: 0 }}>Historial de depósitos</p>
                <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Depósitos que ya se liberaron y decidiste no volver a meter a plazo.</p>
                <button className="text-link" onClick={() => setDepositsExpanded(!depositsExpanded)}>
                  {depositsExpanded ? "Ocultar historial" : "Ver historial*"}
                </button>
                {depositsExpanded && (
                  <>
                    <div className="calendar-list" style={{ marginTop: "0.5rem" }}>
                      {state.savingsLocks.filter((l) => l.status === "completed").slice(0, 50).map((l) => (
                        <div className="calendar-day-row" key={l.id}>
                          <span className="calendar-day-label">{termIcon(l.termMonths || 1, state.savingsTerms)} {l.termMonths || 1}m</span>
                          <div className="calendar-day-chips">
                            <span className="cal-task-chip done">{fmt(l.amount)} € · liberado</span>
                          </div>
                        </div>
                      ))}
                    </div>
                    <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo los últimos 50 depósitos.</p>
                  </>
                )}
              </div>
            )}
          </>
          )}

          {bankTab === "loans" && (
          <>
          <div className="card">
            <p className="card-title" style={{ marginTop: 0 }}>Pide un adelanto</p>
            <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Si necesitas dinero para algo ya, pídelo aquí y devuélvelo poco a poco desde tu cartera. Como en un préstamo de verdad, cuanto más tardes en devolverlo, más pagarás de más.</p>

            <div className="streak-reward-card">
              <p className="streak-reward-title">🧮 Calcula cuánto tendrías que devolver</p>
              <p className="streak-reward-text">
                El interés de los préstamos es del <b>{(state.loanInterestRate * 100).toFixed(1)}% mensual</b>. Se te cobra un mes de interés en cuanto te lo dan (aunque lo devuelvas al momento), y sigue creciendo cada mes que tardes en devolverlo.
              </p>
              {(() => {
                const base = loanAmount && parseFloat(loanAmount) > 0 ? parseFloat(loanAmount) : 10;
                const overdueDays = state.loanOverdueDays || 30;
                const monthsEquivalent = Math.max(1, Math.round(overdueDays / 30));
                const rate = state.loanInterestRate || 0;
                const owedFromDay1 = base * (1 + rate);
                const owedIfFullTerm = base * Math.pow(1 + rate, monthsEquivalent);
                return (
                  <div className="table" style={{ marginTop: "0.5rem" }}>
                    <div className="table-row">
                      <span>Si pides {fmt(base)} €, ya debes desde el primer día</span>
                      <Money value={-owedFromDay1} size="sm" />
                    </div>
                    {monthsEquivalent > 1 && (
                      <div className="table-row">
                        <span>Si tardas los {overdueDays} días de plazo en devolverlo</span>
                        <Money value={-owedIfFullTerm} size="sm" />
                      </div>
                    )}
                  </div>
                );
              })()}
            </div>

            <p className="tiny-hint">
              ⚠ Tienes <b>{state.loanOverdueDays || 30} días</b> desde que te lo dan para devolverlo <b>entero</b>. Si no lo pagas del todo a tiempo, no podrás comprar en el Mercado ni pedir otro préstamo hasta que lo saldes por completo — un pago parcial no lo desbloquea.
            </p>

            {overdueLoan && (
              <div className="error-box">
                Tienes el préstamo "{overdueLoan.reason}" sin devolver del todo desde hace {state.loanOverdueDays || 30} días. Págalo entero para poder comprar y pedir otro préstamo.
              </div>
            )}
            <div className="form-row">
              <input className="amount-input eco-mono" type="number" min="0" placeholder="Cantidad" value={loanAmount} onChange={(e) => setLoanAmount(e.target.value)} disabled={!!overdueLoan} />
              <input className="text-input" placeholder="¿Para qué lo necesitas?" value={loanReason} onChange={(e) => setLoanReason(e.target.value)} disabled={!!overdueLoan} />
              <button
                className="btn primary small"
                disabled={!!overdueLoan}
                onClick={() => { if (!loanAmount || !loanReason.trim()) return; onRequestLoan(parseFloat(loanAmount), loanReason.trim()); setLoanAmount(""); setLoanReason(""); }}
              >
                Pedir préstamo
              </button>
            </div>
          </div>

          {myLoans.filter((l) => l.status !== "paid").length > 0 && (
            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>Mis préstamos activos</p>
              <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Pendientes de aprobar o de devolver. Recuerda: solo devolverlo entero levanta el bloqueo del Mercado.</p>
              <div className="table">
                {myLoans.filter((l) => l.status !== "paid").map((l) => {
                  const daysSincePayment = l.status === "active" && l.lastPaymentDate ? daysBetween(l.lastPaymentDate, today) : null;
                  const isOverdue = daysSincePayment !== null && daysSincePayment >= (state.loanOverdueDays || 30);
                  return (
                  <div className="table-row loan-row" key={l.id}>
                    <span>{l.reason}</span>
                    <Money value={-l.remaining} size="sm" />
                    {l.status === "requested" && <Stamp text="PENDIENTE" tone="gold" />}
                    {daysSincePayment !== null && (
                      <span className={`countdown-chip ${isOverdue ? "urgent" : ""}`}>
                        {isOverdue ? "⚠ Atrasado" : `⏳ ${(state.loanOverdueDays || 30) - daysSincePayment} día${((state.loanOverdueDays || 30) - daysSincePayment) === 1 ? "" : "s"} para devolverlo entero`}
                      </span>
                    )}
                    {l.status === "active" && (
                      <div className="input-row">
                        <input
                          className="amount-input eco-mono small-input"
                          type="number" min="0" placeholder="Pagar"
                          value={repayAmounts[l.id] || ""}
                          onChange={(e) => setRepayAmounts({ ...repayAmounts, [l.id]: e.target.value })}
                        />
                        <button className="btn ghost small" onClick={() => { onRepayLoan(l.id, parseFloat(repayAmounts[l.id]) || 0); setRepayAmounts({ ...repayAmounts, [l.id]: "" }); }}>
                          Pagar
                        </button>
                      </div>
                    )}
                  </div>
                  );
                })}
              </div>
            </div>
          )}

          {myLoans.filter((l) => l.status === "paid").length > 0 && (
            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>Historial de préstamos</p>
              <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Préstamos que ya devolviste por completo.</p>
              <button className="text-link" onClick={() => setLoansHistoryExpanded(!loansHistoryExpanded)}>
                {loansHistoryExpanded ? "Ocultar historial" : "Ver historial*"}
              </button>
              {loansHistoryExpanded && (
                <>
                  <div className="table" style={{ marginTop: "0.5rem" }}>
                    {myLoans.filter((l) => l.status === "paid").slice(0, 50).map((l) => (
                      <div className="table-row loan-row" key={l.id}>
                        <span>{l.reason} <span className="tiny-hint">· pediste {fmt(l.amount)} €, interés {fmt(l.totalInterestPaid || 0)} €</span></span>
                        <Money value={-(l.amount + (l.totalInterestPaid || 0))} size="sm" />
                        <Stamp text="PAGADO" tone="green" />
                      </div>
                    ))}
                  </div>
                  <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo los últimos 50 préstamos pagados.</p>
                </>
              )}
            </div>
          )}
          </>
          )}
          </>
        )}

        {screen === "townhall" && (
          <>
            <Section icon={TownHall} title="Ayuntamiento" subtitle="Impuestos del pueblo, la comisaría de multas y las normas de casa." accent="#8B7BD8" />

            {state.townRules && state.townRules.trim() && (
              <div className="streak-reward-card">
                <p className="streak-reward-title">📜 Normas del pueblo</p>
                <p className="streak-reward-text" style={{ whiteSpace: "pre-wrap" }}>{state.townRules}</p>
              </div>
            )}

            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>Impuestos</p>
              <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Se cobran solos cada mes desde tu cartera, para pagar los gastos de la casa.</p>
              {state.tax.active ? (
                <div className="table-row">
                  <span>Próximo impuesto en {daysUntilNextTax()} día{daysUntilNextTax() === 1 ? "" : "s"}</span>
                  <Money value={state.tax.amount} size="sm" />
                </div>
              ) : (
                <p className="empty-hint">Los impuestos están desactivados ahora mismo.</p>
              )}
              <p className="tiny-hint">Se pagan por: {state.tax.concept || "gastos de la casa"}</p>
            </div>

            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}><PoliceIcon size={14} /> Comisaría</p>
              <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Aquí verás las multas que te apliquen — puedes pagarlas o apelarlas si no estás de acuerdo.</p>
              {pendingFines.length === 0 && <p className="empty-hint">No tienes ninguna multa pendiente. ¡Buen trabajo!</p>}
              <div className="table">
                {pendingFines.map((f) => {
                  const daysLeftToAppeal = f.appealDeadline ? daysBetween(today, f.appealDeadline) : null;
                  return (
                  <div className="table-row loan-row" key={f.id} style={{ flexWrap: "wrap" }}>
                    <span>{f.reason}</span>
                    <Money value={-f.remaining} size="sm" />
                    {f.status === "pending" && daysLeftToAppeal !== null && (
                      <span className={`countdown-chip ${daysLeftToAppeal <= 1 ? "urgent" : ""}`}>
                        ⏳ {daysLeftToAppeal <= 0 ? "Último día para apelar" : `${daysLeftToAppeal} día${daysLeftToAppeal === 1 ? "" : "s"} para apelar`}
                      </span>
                    )}
                    {f.status === "disputed" ? (
                      <Stamp text="APELACIÓN EN REVISIÓN" tone="orange" />
                    ) : disputingFineId === f.id ? (
                      <div className="input-row" style={{ flexBasis: "100%", marginTop: "0.3rem" }}>
                        <input className="text-input" placeholder="¿Por qué no estás de acuerdo?" value={disputeReasonInput} onChange={(e) => setDisputeReasonInput(e.target.value)} />
                        <button
                          className="btn primary small"
                          onClick={() => {
                            if (!disputeReasonInput.trim()) return;
                            onDisputeFine(f.id, disputeReasonInput.trim());
                            setDisputingFineId(null); setDisputeReasonInput("");
                          }}
                        >
                          Enviar apelación
                        </button>
                        <button className="btn ghost small" onClick={() => { setDisputingFineId(null); setDisputeReasonInput(""); }}>Cancelar</button>
                      </div>
                    ) : (
                      <>
                        {f.disputeRejectReason && (
                          <p className="reject-comment">Tu apelación no fue aceptada: <i>"{f.disputeRejectReason}"</i></p>
                        )}
                        <div className="input-row">
                          <input
                            className="amount-input eco-mono small-input"
                            type="number" min="0" placeholder="Pagar"
                            value={fineRepayAmounts[f.id] || ""}
                            onChange={(e) => setFineRepayAmounts({ ...fineRepayAmounts, [f.id]: e.target.value })}
                          />
                          <button className="btn ghost small" onClick={() => { onRepayFine(f.id, parseFloat(fineRepayAmounts[f.id]) || 0); setFineRepayAmounts({ ...fineRepayAmounts, [f.id]: "" }); }}>
                            Pagar
                          </button>
                          <button className="btn ghost small" onClick={() => setDisputingFineId(f.id)}>Apelar</button>
                        </div>
                      </>
                    )}
                  </div>
                  );
                })}
              </div>
              {(state.fines || []).filter((f) => f.status === "paid" || f.status === "waived").length > 0 && (
                <>
                  <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setFinesHistoryExpanded(!finesHistoryExpanded)}>
                    {finesHistoryExpanded ? "Ocultar historial" : "Ver historial*"}
                  </button>
                  {finesHistoryExpanded && (
                    <>
                      <div className="table" style={{ marginTop: "0.5rem" }}>
                        {(state.fines || []).filter((f) => f.status === "paid" || f.status === "waived").slice(0, 50).map((f) => (
                          <div className="table-row loan-row" key={f.id}>
                            <span>{f.reason}</span>
                            <Money value={-f.amount} size="sm" />
                            <Stamp text={f.status === "paid" ? "PAGADA" : "PERDONADA"} tone="green" />
                          </div>
                        ))}
                      </div>
                      <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 multas resueltas.</p>
                    </>
                  )}
                </>
              )}
            </div>

            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>✉️ Buzón de peticiones</p>
              <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Escríbele algo a tu padre o madre — una idea, una queja, lo que quieras.</p>
              <div className="form-row">
                <input className="text-input" placeholder="Escribe tu mensaje..." value={petitionText} onChange={(e) => setPetitionText(e.target.value)} />
                <button
                  className="btn primary small"
                  onClick={() => { if (!petitionText.trim()) return; onSendPetition(petitionText.trim()); setPetitionText(""); }}
                >
                  Enviar
                </button>
              </div>
              {(state.petitions || []).filter((p) => p.status === "pending").length > 0 && (
                <div className="table" style={{ marginTop: "0.5rem" }}>
                  {(state.petitions || []).filter((p) => p.status === "pending").map((p) => (
                    <div className="table-row" key={p.id}>
                      <span>{p.text}</span>
                      <Stamp text="ENVIADA" tone="gold" />
                    </div>
                  ))}
                </div>
              )}
              {(state.petitions || []).filter((p) => p.status === "read").length > 0 && (
                <>
                  <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setPetitionsHistoryExpanded(!petitionsHistoryExpanded)}>
                    {petitionsHistoryExpanded ? "Ocultar historial" : "Ver historial*"}
                  </button>
                  {petitionsHistoryExpanded && (
                    <>
                      <div className="table" style={{ marginTop: "0.5rem" }}>
                        {(state.petitions || []).filter((p) => p.status === "read").slice(0, 50).map((p) => (
                          <div className="table-row" key={p.id}>
                            <span>{p.text}</span>
                            <Stamp text="LEÍDA" tone="green" />
                          </div>
                        ))}
                      </div>
                      <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 peticiones leídas.</p>
                    </>
                  )}
                </>
              )}
            </div>
          </>
        )}

        {screen === "badges" && (
          <>
          <Section icon={Award} title="Insignias" subtitle="Logros que vas desbloqueando, más allá de la racha." accent="#FFB74D" />

          {(() => {
            const streakLevelsAllDone = (state.streakLevels || []).every((lvl) => (state.streak?.record || 0) >= lvl.days);
            const allMilestonesDone = BADGES.every((b) => !!state.milestones?.[b.key]);
            const everythingDone = streakLevelsAllDone && allMilestonesDone;
            return (
              <div className={`badge-tile trophy-tile ${everythingDone ? "unlocked" : "locked"}`}>
                <span className="badge-emoji">🏅</span>
                <p className="badge-title">Maestro/a de Clevank World</p>
                <p className="badge-desc">Desbloqueaste absolutamente todas las insignias de la app.</p>
              </div>
            );
          })()}

          <div className="card">
            <p className="card-title" style={{ marginTop: 0 }}>Tareas</p>
            <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Un nivel de racha por cada plazo que haya configurado tu adulto, más otros logros de constancia.</p>
            <div className="badge-grid">
              {[...(state.streakLevels || [])].sort((a, b) => a.days - b.days).map((lvl, i) => {
                const unlocked = (state.streak?.record || 0) >= lvl.days;
                return (
                  <div key={i} className={`badge-tile ${unlocked ? "unlocked" : "locked"}`}>
                    <span className="badge-emoji">{i === state.streakLevels.length - 1 ? "🏆" : "🔥"}</span>
                    <p className="badge-title">Nivel {i}</p>
                    <p className="badge-desc">{lvl.days} días de racha seguidos ({fmt(lvl.amount)} €).</p>
                  </div>
                );
              })}
              {BADGES.filter((b) => b.section === "tareas").map((b) => {
                const unlocked = !!state.milestones?.[b.key];
                return (
                  <div key={b.key} className={`badge-tile ${unlocked ? "unlocked" : "locked"}`}>
                    <span className="badge-emoji">{b.emoji}</span>
                    <p className="badge-title">{b.title}</p>
                    <p className="badge-desc">{b.desc}</p>
                  </div>
                );
              })}
              {(() => {
                const streakLevelsAllDone = (state.streakLevels || []).every((lvl) => (state.streak?.record || 0) >= lvl.days);
                const sectionBadges = BADGES.filter((b) => b.section === "tareas");
                const complete = streakLevelsAllDone && sectionBadges.every((b) => !!state.milestones?.[b.key]);
                return (
                  <div className={`badge-tile ${complete ? "unlocked" : "locked"}`}>
                    <span className="badge-emoji">🗂️</span>
                    <p className="badge-title">Coleccionista</p>
                    <p className="badge-desc">Consigue todas las insignias de Tareas.</p>
                  </div>
                );
              })()}
            </div>
          </div>

          {BADGE_SECTIONS.filter((sec) => sec.key !== "tareas").map((sec) => (
            <div className="card" key={sec.key}>
              <p className="card-title" style={{ marginTop: 0 }}>{sec.label}</p>
              <div className="badge-grid">
                {BADGES.filter((b) => b.section === sec.key).map((b) => {
                  const unlocked = !!state.milestones?.[b.key];
                  let desc = b.desc;
                  if (b.key === "negotiationRecord" && unlocked) desc = `Tu récord: ${fmt(state.maxNegotiatedReward || 0)} € en una negociación.`;
                  if (b.key === "payoutRecord" && unlocked) desc = `Tu récord: ${fmt(state.maxJobPayout || 0)} € en un solo trabajo.`;
                  if (b.key === "bigSpender" && unlocked) desc = `Tu récord: ${fmt(state.maxRedeemCost || 0)} € en un solo capricho.`;
                  return (
                    <div key={b.key} className={`badge-tile ${unlocked ? "unlocked" : "locked"}`}>
                      <span className="badge-emoji">{b.emoji}</span>
                      <p className="badge-title">{b.title}</p>
                      <p className="badge-desc">{desc}</p>
                    </div>
                  );
                })}
                {(() => {
                  const sectionBadges = BADGES.filter((b) => b.section === sec.key);
                  const complete = sectionBadges.length > 0 && sectionBadges.every((b) => !!state.milestones?.[b.key]);
                  return (
                    <div className={`badge-tile ${complete ? "unlocked" : "locked"}`}>
                      <span className="badge-emoji">🗂️</span>
                      <p className="badge-title">Coleccionista</p>
                      <p className="badge-desc">Consigue todas las insignias de {sec.label}.</p>
                    </div>
                  );
                })()}
              </div>
            </div>
          ))}
          </>
        )}

        {screen === "market" && (
          <>
          <Section icon={ShoppingBag} title="Mercado de caprichos" subtitle="Pide un canje — tu adulto debe autorizarlo." accent="var(--primary)" />
            {overdueLoan && (
              <div className="error-box">
                Tienes el préstamo "{overdueLoan.reason}" sin ningún pago desde hace {state.loanOverdueDays || 30} días. Págalo (aunque sea una parte, desde Banco → Préstamo) para poder comprar en el Mercado.
              </div>
            )}
            <div className="market-grid">
              {state.market.map((m) => {
                const can = !overdueLoan && state.wallet >= m.cost;
                return (
                  <div className="market-card" key={m.id}>
                    <span className="market-emoji">{m.emoji}</span>
                    <p className="job-title">{m.name}</p>
                    <Money value={m.cost} />
                    <button className="btn primary small" disabled={!can} onClick={() => onRequestRedeem(m)}>{can ? "Pedir canje" : overdueLoan ? "Préstamo atrasado" : "Falta saldo"}</button>
                  </div>
                );
              })}
            </div>

            <div className="card">
              <p className="card-title" style={{ marginTop: 0 }}>¿No está lo que buscas?</p>
              <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Pide cualquier otra cosa poniendo su nombre y el precio que crees justo — tu adulto decidirá si te lo da.</p>
              <div className="form-row">
                <input className="text-input" placeholder="¿Qué quieres?" value={customItemName} onChange={(e) => setCustomItemName(e.target.value)} />
                <input className="amount-input eco-mono" type="number" min="0" placeholder="Precio" value={customItemCost} onChange={(e) => setCustomItemCost(e.target.value)} />
                <button
                  className="btn primary small"
                  disabled={!!overdueLoan || !customItemCost || parseFloat(customItemCost) > state.wallet}
                  onClick={() => { if (!customItemName.trim() || !customItemCost) return; onRequestCustomItem(customItemName.trim(), parseFloat(customItemCost)); setCustomItemName(""); setCustomItemCost(""); }}
                >
                  Pedir
                </button>
              </div>
              {overdueLoan && <p className="tiny-hint">No puedes pedir nada mientras tengas un préstamo atrasado.</p>}
              {!overdueLoan && customItemCost && parseFloat(customItemCost) > state.wallet && <p className="tiny-hint">Te falta saldo para pedir eso.</p>}
            </div>

            {(state.marketRequests || []).length > 0 && (
              <div className="card">
                <p className="card-title" style={{ marginTop: 0 }}>Mis solicitudes</p>
                {(state.marketRequests || []).filter((r) => r.status === "pending").length === 0 && (
                  <p className="empty-hint">No tienes ninguna solicitud pendiente.</p>
                )}
                <div className="table">
                  {(state.marketRequests || []).filter((r) => r.status === "pending").map((r) => (
                    <div className="table-row" key={r.id}>
                      <span>{r.itemName}{r.custom && <span className="tiny-hint"> (petición tuya)</span>}</span>
                      <Money value={-r.cost} size="sm" />
                      <Stamp text="PENDIENTE" tone="gold" />
                    </div>
                  ))}
                </div>
                {(state.marketRequests || []).filter((r) => r.status === "approved" || r.status === "declined").length > 0 && (
                  <>
                    <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setRequestsHistoryExpanded(!requestsHistoryExpanded)}>
                      {requestsHistoryExpanded ? "Ocultar historial" : "Ver historial*"}
                    </button>
                    {requestsHistoryExpanded && (
                      <>
                        <div className="table" style={{ marginTop: "0.5rem" }}>
                          {(state.marketRequests || []).filter((r) => r.status === "approved" || r.status === "declined").slice(0, 50).map((r) => (
                            <div className="table-row" key={r.id}>
                              <span>{r.itemName}{r.custom && <span className="tiny-hint"> (petición tuya)</span>}</span>
                              <Money value={-r.cost} size="sm" />
                              <Stamp text={r.status === "approved" ? "APROBADO" : "RECHAZADO"} tone={r.status === "approved" ? "green" : "red"} />
                            </div>
                          ))}
                        </div>
                        <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 solicitudes resueltas.</p>
                      </>
                    )}
                  </>
                )}
              </div>
            )}
          </>
        )}

        {screen === "donations" && (
          <>
          <Section icon={HeartHandshake} title="Regalos y donaciones" subtitle="Dinero que te dan, que le das a tus hermanos/as, o que donas a una buena causa." accent="var(--accent-blue)" />

          <div className="card">
            <p className="card-title" style={{ marginTop: 0 }}>🎁 Regalos de familiares</p>
            <p className="section-sub" style={{ marginBottom: "0.5rem" }}>
              Si un abuelo, tío o cualquier familiar te da dinero, pídele a tu padre o madre que lo registre aquí — ellos son quienes lo añaden, porque tu familiar no usa esta app.
              {state.familyGiftTaxRate > 0 && ` Se descuenta un impuesto del ${(state.familyGiftTaxRate * 100).toFixed(0)}% antes de sumarlo a tu cartera, igual que con los demás impuestos del pueblo.`}
            </p>
            {(state.familyGifts || []).length === 0 && <p className="empty-hint">Todavía no has recibido ningún regalo registrado.</p>}
            {(state.familyGifts || []).length > 0 && (
              <div className="table">
                {state.familyGifts.slice(0, 5).map((g) => (
                  <div className="table-row" key={g.id}>
                    <span>{g.giverName} <span className="tiny-hint">· {g.date}</span></span>
                    <Money value={g.net} size="sm" />
                  </div>
                ))}
              </div>
            )}
            {(state.familyGifts || []).length > 5 && (
              <>
                <button className="text-link" style={{ marginTop: "0.5rem" }} onClick={() => setGiftsHistoryExpanded(!giftsHistoryExpanded)}>
                  {giftsHistoryExpanded ? "Ocultar historial" : "Ver historial*"}
                </button>
                {giftsHistoryExpanded && (
                  <>
                    <div className="table" style={{ marginTop: "0.5rem" }}>
                      {state.familyGifts.slice(5, 50).map((g) => (
                        <div className="table-row" key={g.id}>
                          <span>{g.giverName} <span className="tiny-hint">· {g.date}</span></span>
                          <Money value={g.net} size="sm" />
                        </div>
                      ))}
                    </div>
                    <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo los últimos 50 regalos.</p>
                  </>
                )}
              </>
            )}
          </div>

          <div className="card">
            <p className="card-title" style={{ marginTop: 0 }}>🤍 Dar a un hermano/a</p>
            <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Comparte parte de tu cartera con un hermano/a — se lo llevará al momento, sin devolución.</p>
            {(familyChildren || []).filter((c) => c.id !== childId).length === 0 ? (
              <p className="empty-hint">No tienes hermanos/as en la familia todavía.</p>
            ) : (
              <div className="form-row">
                <select className="text-input" value={siblingTarget} onChange={(e) => setSiblingTarget(e.target.value)}>
                  <option value="">Elige a quién...</option>
                  {familyChildren.filter((c) => c.id !== childId).map((c) => (
                    <option key={c.id} value={c.id}>{c.name}</option>
                  ))}
                </select>
                <input className="amount-input eco-mono" type="number" min="0" placeholder="0,00" value={siblingAmt} onChange={(e) => setSiblingAmt(e.target.value)} />
                <button
                  className="btn primary small"
                  disabled={!siblingTarget || !siblingAmt || parseFloat(siblingAmt) > state.wallet}
                  onClick={() => {
                    const sibling = familyChildren.find((c) => c.id === siblingTarget);
                    if (!sibling) return;
                    onDonateToSibling(sibling.id, sibling.name, parseFloat(siblingAmt) || 0);
                    setSiblingAmt(""); setSiblingTarget("");
                  }}
                >
                  Regalar
                </button>
              </div>
            )}
            {siblingAmt && parseFloat(siblingAmt) > state.wallet && <p className="tiny-hint">Te falta saldo para eso.</p>}
          </div>

          <div className="card">
            <p className="card-title" style={{ marginTop: 0 }}>💗 {state.donationPot.name}</p>
            <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Dona parte de tu cartera a una buena causa — tu adulto se encarga de entregarlo cuando corresponda.</p>
            <p className="tiny-hint">Acumulado: <Money value={state.donationPot.total} size="sm" /></p>
            {state.donationPot.target > 0 && (
              <div style={{ marginTop: "0.3rem", marginBottom: "0.5rem" }}>
                <ProgressBar pct={(state.donationPot.total / state.donationPot.target) * 100} />
                <p className="tiny-hint" style={{ marginTop: "0.2rem" }}>Meta: {fmt(state.donationPot.total)} € de {fmt(state.donationPot.target)} €</p>
              </div>
            )}
            <div className="input-row">
              <input className="amount-input eco-mono" type="number" min="0" placeholder="0,00" value={donateAmt} onChange={(e) => setDonateAmt(e.target.value)} />
              <button className="btn primary small" onClick={() => { onDonate(parseFloat(donateAmt) || 0); setDonateAmt(""); }}>Donar</button>
            </div>
            {(state.donationHistory || []).length > 0 && (
              <>
                <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setDonationHistoryExpanded(!donationHistoryExpanded)}>
                  {donationHistoryExpanded ? "Ocultar historial" : "Ver historial*"}
                </button>
                {donationHistoryExpanded && (
                  <>
                    <div className="table" style={{ marginTop: "0.5rem" }}>
                      {state.donationHistory.slice(0, 50).map((h) => (
                        <div className="table-row" key={h.id}>
                          <span>{h.name} <span className="tiny-hint">· {h.date}</span></span>
                          <Money value={h.amount} size="sm" />
                        </div>
                      ))}
                    </div>
                    <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 entregas.</p>
                  </>
                )}
              </>
            )}
          </div>
          </>
        )}

        {screen === "ledger" && (
          <Section icon={ScrollText} title="Libro mayor" subtitle="Tus últimos movimientos." accent="#B8B0D8">
            <div className="weekday-row" style={{ marginBottom: "0.6rem" }}>
              {LEDGER_FILTERS.map((f) => (
                <button
                  key={f.key}
                  className={`term-chip ${ledgerFilter === f.key ? "active" : ""}`}
                  onClick={() => setLedgerFilter(f.key)}
                >
                  {f.label}
                </button>
              ))}
            </div>
            <LedgerList entries={ledgerFilter === "todo" ? state.ledger : state.ledger.filter((e) => e.category === ledgerFilter)} />
          </Section>
        )}
      </div>
    );
  }

  return (
    <div className="stack">
      {notifBanner}

      <div className="card balance-card">
        <div className="balance-block">
          <p className="eco-mono eyebrow">CARTERA</p>
          <Money value={state.wallet} size="lg" />
        </div>
        <div className="balance-divider" />
        <div className="balance-block">
          <p className="eco-mono eyebrow">AHORROS</p>
          <Money value={state.savings} size="lg" />
        </div>
        <div className="balance-divider" />
        <div className="balance-block">
          <p className="eco-mono eyebrow">PATRIMONIO TOTAL</p>
          <Money value={net} size="lg" />
        </div>
        <div className="balance-divider" />
        <div className="balance-block">
          <p className="eco-mono eyebrow">RACHA</p>
          <span className="eco-mono money lg streak-value"><Flame size={16} /> {state.streak?.count || 0}</span>
        </div>
      </div>

      <div className="card">
        <div className="section-head">
          <div className="section-icon" style={{ background: "var(--secondary)" }}><ScrollText size={18} color="#2b2410" /></div>
          <div>
            <h2 className="eco-display section-title">Cómo ha crecido tu dinero</h2>
            <p className="section-sub">Cartera + ahorros, día a día.</p>
          </div>
        </div>
        <NetWorthChart history={state.netWorthHistory} onClick={() => setScreen("ledger")} walletValue={state.wallet} />
      </div>

      <div className="icon-grid">
        {SCREENS.map((s) => (
          <button key={s.key} className="icon-tile" style={{ "--tile-color": s.color }} onClick={() => setScreen(s.key)}>
            {badges[s.key] > 0 && <span className="tile-badge">{badges[s.key]}</span>}
            <span className="tile-icon"><s.icon size={26} strokeWidth={2.3} /></span>
            <span className="tile-label">{s.label}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

function ParentView({
  state, childId, familyId, familyChildren, onResetChildPin, sharedJobs, onCreateSharedJob, onDeleteSharedJob, onApproveSharedJob, onRejectSharedJob,
  onAddJob, onDeleteJob, onApproveJob, onRejectJob, onAcceptNegotiation, onDeclineNegotiation,
  onAddMarketItem, onDeleteMarketItem, onApproveRedeem, onDeclineRedeem, onApplyFine, onApproveFineDispute, onRejectFineDispute, onMarkPetitionRead, onApproveLoan, onRejectLoan,
  onEmptyPot, onAddFamilyGift, onSetOfficePin, onAddMandatoryTask, onUpdateMandatoryTaskDays, onUpdateMandatoryTask, onDeleteMandatoryTask, onConfirmTask, onCancelTask,
  onSaveSettings, onDismissNotification, onAcceptJobRequest, onDeclineJobRequest, onCorrectTaskHistoryEntry,
}) {
  const [screen, setScreen] = useState("dashboard");
  const [taskEmoji, setTaskEmoji] = useState("✅");
  const [taskDays, setTaskDays] = useState([...WEEKDAYS]);
  const [jobTargets, setJobTargets] = useState(childId ? [childId] : []);
  const [creatingSharedJob, setCreatingSharedJob] = useState(false);
  const [jobsHistoryExpanded, setJobsHistoryExpanded] = useState(false);
  const [rejectingJobId, setRejectingJobId] = useState(null);
  const [rejectingJobType, setRejectingJobType] = useState(null);
  const [rejectJobReason, setRejectJobReason] = useState("");
  const [editingTaskId, setEditingTaskId] = useState(null);
  const [editEmoji, setEditEmoji] = useState("");
  const [editTitle, setEditTitle] = useState("");
  const [editPenalty, setEditPenalty] = useState("");
  const [jobTitle, setJobTitle] = useState("");
  const [jobReward, setJobReward] = useState("");
  const [jobDays, setJobDays] = useState("3");
  const [decliningRequest, setDecliningRequest] = useState(false);
  const [parentBankTab, setParentBankTab] = useState("savings");
  const [correctionExpanded, setCorrectionExpanded] = useState(false);
  const [declineReason, setDeclineReason] = useState("");
  const [itemName, setItemName] = useState("");
  const [itemCost, setItemCost] = useState("");
  const [itemEmoji, setItemEmoji] = useState("");
  const [fineReason, setFineReason] = useState("");
  const [fineAppealDays, setFineAppealDays] = useState((state.fineAppealDays ?? 3).toString());
  const [fineAmount, setFineAmount] = useState("");
  const [rejectingFineId, setRejectingFineId] = useState(null);
  const [fineRejectReasonInput, setFineRejectReasonInput] = useState("");
  const [finesHistoryExpandedParent, setFinesHistoryExpandedParent] = useState(false);
  const [petitionsHistoryExpandedParent, setPetitionsHistoryExpandedParent] = useState(false);
  const [requestsHistoryExpandedParent, setRequestsHistoryExpandedParent] = useState(false);
  const [loansHistoryExpandedParent, setLoansHistoryExpandedParent] = useState(false);
  const [townRules, setTownRules] = useState(state.townRules || "");
  const [taskTitle, setTaskTitle] = useState("");
  const [taskPenalty, setTaskPenalty] = useState("");
  const [childName, setChildName] = useState(state.childName);
  const [taxAmount, setTaxAmount] = useState(state.tax.amount);
  const [taxActive, setTaxActive] = useState(state.tax.active);
  const [taxConcept, setTaxConcept] = useState(state.tax.concept || "");
  const [taxNoticeDays, setTaxNoticeDays] = useState((state.taxNoticeDays ?? 3).toString());
  const [savingsTermPcts, setSavingsTermPcts] = useState(state.savingsTerms.map((t) => (t.rate * 100).toString()));
  const [savingsTermMonths, setSavingsTermMonths] = useState(state.savingsTerms.map((t) => t.months.toString()));
  const [savingsNoticeDays, setSavingsNoticeDays] = useState((state.savingsNoticeDays ?? 5).toString());
  const [loanInterestPct, setLoanInterestPct] = useState((state.loanInterestRate * 100).toString());
  const [loanOverdueDays, setLoanOverdueDays] = useState((state.loanOverdueDays ?? 30).toString());
  const [streakLevels, setStreakLevels] = useState(state.streakLevels.map((l) => ({ ...l })));
  const [streakActive, setStreakActive] = useState(state.streakLevelsActive !== false);
  const [potName, setPotName] = useState(state.donationPot.name);
  const [potTarget, setPotTarget] = useState((state.donationPot.target || 0).toString());
  const [giftGiver, setGiftGiver] = useState("");
  const [giftAmount, setGiftAmount] = useState("");
  const [giftTaxPct, setGiftTaxPct] = useState(((state.familyGiftTaxRate || 0) * 100).toString());
  const [giftsHistoryExpandedParent, setGiftsHistoryExpandedParent] = useState(false);
  const [donationHistoryExpandedParent, setDonationHistoryExpandedParent] = useState(false);
  const [newPin, setNewPin] = useState("");
  const [childPinInputs, setChildPinInputs] = useState({});
  const [notifPrefs, setNotifPrefs] = useState(state.notificationPrefs || {});
  const [ledgerFilter, setLedgerFilter] = useState("todo");
  const [childPinStatus, setChildPinStatus] = useState({});
  const [familyNameInput, setFamilyNameInput] = useState("");
  const [currentFamilyName, setCurrentFamilyName] = useState("");
  useEffect(() => {
    if (!familyId || !window.getFamilyName) return;
    window.getFamilyName(familyId).then((name) => {
      setCurrentFamilyName(name);
      setFamilyNameInput(name);
    }).catch(() => {});
  }, [familyId]);
  const [backupBusy, setBackupBusy] = useState(false);
  const [emailVerified] = useState(() => (typeof window !== "undefined" && window.isCurrentUserEmailVerified ? window.isCurrentUserEmailVerified() : true));
  const [resendBusy, setResendBusy] = useState(false);
  const [resendMsg, setResendMsg] = useState("");

  const submitted = state.jobs.filter((j) => j.status === "submitted");
  const negotiating = state.jobs.filter((j) => j.status === "negotiating");
  const others = state.jobs.filter((j) => j.status !== "submitted" && j.status !== "negotiating");
  const requestedLoans = state.loans.filter((l) => l.status === "requested");
  const activeLoans = state.loans.filter((l) => l.status === "active");
  const pendingRedeems = (state.marketRequests || []).filter((r) => r.status === "pending");
  const taxDays = daysUntilNextTax();
  const today = dateKey();
  const todaysMandatoryTasks = state.mandatoryTasks.filter((t) => taskAppliesOn(t, today));
  const tasksPendingReview = state.mandatoryTasks.filter((t) => {
    const e = state.mandatoryDay.entries[t.id];
    return e && e.checked && !e.verified;
  }).length;
  const unreadParent = (state.notifications || []).filter((n) => !n.read && n.for === "parent");

  const badges = {
    tasks: tasksPendingReview,
    jobs: submitted.length + negotiating.length + (state.jobRequest && state.jobRequest.status === "pending" ? 1 : 0) + (sharedJobs || []).filter((j) => j.status === "submitted").length,
    bank: requestedLoans.length,
    townhall: (state.fines || []).filter((f) => f.status === "disputed").length + (state.petitions || []).filter((p) => p.status === "pending").length,
    market: pendingRedeems.length,
    donations: 0,
    ledger: 0,
    settings: 0,
  };

  const balanceCard = (
    <div className="card balance-card">
      <div className="balance-block"><p className="eco-mono eyebrow">CARTERA</p><Money value={state.wallet} size="lg" /></div>
      <div className="balance-divider" />
      <div className="balance-block"><p className="eco-mono eyebrow">AHORROS</p><Money value={state.savings} size="lg" /></div>
      <div className="balance-divider" />
      <div className="balance-block"><p className="eco-mono eyebrow">POR REVISAR</p><span className="eco-mono money lg">{submitted.length + negotiating.length + requestedLoans.length + pendingRedeems.length + tasksPendingReview}</span></div>
      <div className="balance-divider" />
      <div className="balance-block"><p className="eco-mono eyebrow">RACHA</p><span className="eco-mono money lg streak-value"><Flame size={16} /> {state.streak?.count || 0}</span></div>
    </div>
  );

  const notifBannerParent = unreadParent.length > 0 && (
    <div className="notif-stack">
      {unreadParent.map((n) => (
        <div className="notif-card" key={n.id}>
          <Bell size={15} />
          <span>{n.message}</span>
          <button className="icon-btn" onClick={() => onDismissNotification(n.id)}><X size={15} /></button>
        </div>
      ))}
    </div>
  );

  if (screen !== "dashboard") {
    return (
      <div className="stack">
        {notifBannerParent}
        <button className="btn ghost small back-btn" onClick={() => setScreen("dashboard")}>
          <ArrowLeft size={14} /> Volver al panel
        </button>

        {screen === "tasks" && (
          <>
      <Section icon={ClipboardCheck} title="Tareas y rachas" subtitle="Crea, revisa y modifica tareas de tu hijo/a. Se descuenta el importe de la multa que has definido en la tarea si no confirmas su cumplimiento antes de que acabe el día." accent="var(--accent-blue)" />

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Estado y revisión de las tareas de hoy ({todayLabel()})</p>
        <p className="section-sub" style={{ marginBottom: "0.7rem" }}>Revisa y confirma si tu hijo/a ha realizado la tarea correctamente para que no se le descuente dinero de su cartera.</p>
        {todaysMandatoryTasks.length === 0 && <p className="empty-hint">Ninguna tarea programada para hoy.</p>}
        <div className="task-list">
          {todaysMandatoryTasks.map((t) => {
            const e = state.mandatoryDay.entries[t.id] || { checked: false, verified: false };
            return (
              <div className="task-row" key={t.id}>
                <span className="task-emoji">{t.emoji || "✅"}</span>
                <span className="task-title">{t.title}</span>
                <span className="tiny-hint">−{fmt(t.penalty)} € si no se confirma</span>
                {e.verified ? (
                  <Stamp text="CONFIRMADO" tone="green" />
                ) : e.checked ? (
                  <Stamp text="PENDIENTE DE CONFIRMAR" tone="orange" />
                ) : (
                  <Stamp text="PENDIENTE DE HACER" tone="red" />
                )}
                <div className="row-buttons tight">
                  {!e.verified && <button className="btn primary small" onClick={() => onConfirmTask(t.id)}>Confirmar</button>}
                  {(e.checked || e.verified) && <button className="btn danger small" onClick={() => onCancelTask(t.id)}>Cancelar</button>}
                </div>
              </div>
            );
          })}
        </div>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Corregir días anteriores</p>
        <p className="tiny-hint" style={{ marginTop: 0 }}>
          Si un día se te olvidó confirmar (o rechazar) una tarea a tiempo, tócala aquí para corregirla — se ajusta el dinero y la racha automáticamente.
        </p>
        <button className="text-link" onClick={() => setCorrectionExpanded(!correctionExpanded)}>
          {correctionExpanded ? "Ocultar" : "Ver días anteriores"}
        </button>
        {correctionExpanded && (() => {
          const pastDays = [...(state.taskHistory || [])]
            .filter((h) => h.tasks && h.tasks.length > 0)
            .sort((a, b) => (a.date < b.date ? 1 : -1))
            .slice(0, 14);
          if (pastDays.length === 0) return <p className="empty-hint">Todavía no hay días pasados con tareas guardadas.</p>;
          return (
            <>
            <p className="tiny-hint" style={{ marginTop: "0.4rem" }}>Toca una tarea para cambiar su estado (confirmada ↔ no confirmada).</p>
            <div className="calendar-list">
              {pastDays.map((day) => (
                <div className="calendar-day-row" key={day.date}>
                  <span className="calendar-day-label">{weekdayCodeForDate(day.date)} {day.date.slice(8, 10)}/{day.date.slice(5, 7)}</span>
                  <div className="calendar-day-chips">
                    {day.tasks.map((t, i) => (
                      <button
                        key={i}
                        type="button"
                        className={`cal-task-chip clickable ${t.done ? "done" : "fail"}`}
                        onClick={() => onCorrectTaskHistoryEntry(day.date, i)}
                        title={t.done ? "Marcar como NO confirmada" : "Marcar como confirmada"}
                      >
                        {t.emoji} {t.title}
                      </button>
                    ))}
                  </div>
                </div>
              ))}
            </div>
            </>
          );
        })()}
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>
          Configuración de las tareas
          <InfoTooltip text="Crea o modifica una tarea que tu hijo/a debe cumplir. Elige un icono, ponle un título y establece la multa en € que se descontará si tu hijo no hace la tarea o no la confirmas antes de que acabe el día. Con las casillas L-M-X-J-V-S-D eliges en qué días debe hacer esa tarea. Ej: Icono de ducha, 'Ducharse', multa 0.20 €, activa de L a V — de lunes a viernes tendrá que ducharse y tú confirmarlo, o se le descontará 0,20 € automáticamente al finalizar el día." />
        </p>

        <p className="config-subhead">
          Creación de nuevas tareas
        </p>
        <div className="form-row">
          <EmojiPickerButton value={taskEmoji} onChange={setTaskEmoji} palette={TASK_EMOJI_PALETTE} />
          <input className="emoji-input" placeholder="Icono" value={taskEmoji} onChange={(e) => setTaskEmoji(e.target.value)} />
          <input className="text-input" placeholder="Título (ej. Ducharse)" value={taskTitle} onChange={(e) => setTaskTitle(e.target.value)} />
          <input className="amount-input eco-mono" type="number" min="0" placeholder="Multa si falla" value={taskPenalty} onChange={(e) => setTaskPenalty(e.target.value)} />
        </div>
        <div className="weekday-row">
          {WEEKDAYS.map((wd) => (
            <label className={`weekday-chip ${taskDays.includes(wd) ? "active" : ""}`} key={wd}>
              <input
                type="checkbox" checked={taskDays.includes(wd)} style={{ display: "none" }}
                onChange={() => setTaskDays(taskDays.includes(wd) ? taskDays.filter((d) => d !== wd) : [...taskDays, wd])}
              />
              {wd}
            </label>
          ))}
        </div>
        <button
          className="btn primary small"
          style={{ marginTop: "0.6rem" }}
          onClick={() => {
            if (!taskTitle.trim() || !taskPenalty) return;
            onAddMandatoryTask(taskTitle.trim(), parseFloat(taskPenalty), taskEmoji, taskDays);
            setTaskTitle(""); setTaskPenalty(""); setTaskEmoji("✅"); setTaskDays([...WEEKDAYS]);
          }}
        >
          <Plus size={14} /> Añadir
        </button>

        <div className="config-divider" />

        <p className="config-subhead">Modificar tareas existentes</p>
        {state.mandatoryTasks.length === 0 && <p className="empty-hint">No has creado ninguna tarea todavía.</p>}
        <div className="task-list">
          {state.mandatoryTasks.map((t) => (
            <div className="task-row" key={t.id}>
              {editingTaskId === t.id ? (
                <>
                  <EmojiPickerButton value={editEmoji} onChange={setEditEmoji} palette={TASK_EMOJI_PALETTE} />
                  <input className="emoji-input" placeholder="Icono" value={editEmoji} onChange={(e) => setEditEmoji(e.target.value)} />
                  <input className="text-input" placeholder="Título" value={editTitle} onChange={(e) => setEditTitle(e.target.value)} />
                  <input className="amount-input eco-mono" type="number" min="0" placeholder="Multa" value={editPenalty} onChange={(e) => setEditPenalty(e.target.value)} />
                  <div className="row-buttons tight">
                    <button
                      className="btn primary small"
                      onClick={() => {
                        if (!editTitle.trim() || !editPenalty) return;
                        onUpdateMandatoryTask(t.id, { title: editTitle.trim(), penalty: parseFloat(editPenalty), emoji: editEmoji || "✅" });
                        setEditingTaskId(null);
                      }}
                    >
                      Guardar
                    </button>
                    <button className="btn ghost small" onClick={() => setEditingTaskId(null)}>Cancelar</button>
                  </div>
                </>
              ) : (
                <>
                  <span className="task-emoji">{t.emoji || "✅"}</span>
                  <span className="task-title">{t.title}</span>
                  <Money value={-t.penalty} size="sm" />
                  <div className="weekday-row">
                    {WEEKDAYS.map((wd) => {
                      const active = (t.days || WEEKDAYS).includes(wd);
                      return (
                        <label className={`weekday-chip small ${active ? "active" : ""}`} key={wd}>
                          <input
                            type="checkbox" checked={active} style={{ display: "none" }}
                            onChange={() => {
                              const current = t.days || [...WEEKDAYS];
                              const nextDays = active ? current.filter((d) => d !== wd) : [...current, wd];
                              onUpdateMandatoryTaskDays(t.id, nextDays);
                            }}
                          />
                          {wd}
                        </label>
                      );
                    })}
                  </div>
                  <button
                    className="icon-btn"
                    onClick={() => {
                      setEditingTaskId(t.id);
                      setEditEmoji(t.emoji || "✅");
                      setEditTitle(t.title);
                      setEditPenalty(String(t.penalty));
                    }}
                  >
                    <Edit size={15} />
                  </button>
                  <button
                    className="icon-btn"
                    onClick={() => { if (window.confirm(`¿Seguro que quieres borrar la tarea "${t.title}"?`)) onDeleteMandatoryTask(t.id); }}
                  >
                    <Trash2 size={15} />
                  </button>
                </>
              )}
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>
          Niveles de racha
          <InfoTooltip text="Cada día que tu hijo/a cumple TODAS sus tareas, suma un día de racha. Al llegar a los días de racha de un nivel, gana automáticamente el premio definido para ese nivel como si fuera una paga. Ej: Nivel 0 = 3 días seguidos → 2 €. Si un día no confirmas ninguna tarea, la racha vuelve a 0 — pero el récord conseguido no se pierde, aunque la racha actual se rompa. La columna de la izquierda es para definir los días y la de la derecha para definir el importe en €" />
        </p>
        <label className="checkbox-label" style={{ marginBottom: "0.6rem" }}>
          <input type="checkbox" checked={streakActive} onChange={(e) => setStreakActive(e.target.checked)} /> Niveles activos
        </label>
        <div className="table">
          {streakLevels.map((lvl, i) => (
            <div className="table-row" key={i}>
              <span>Nivel {i}</span>
              <div className="input-row">
                <input
                  className="amount-input eco-mono small-input" type="number" min="1" placeholder="Días"
                  value={lvl.days}
                  onChange={(e) => {
                    const next = [...streakLevels];
                    next[i] = { ...next[i], days: parseInt(e.target.value) || 0 };
                    setStreakLevels(next);
                  }}
                />
                <input
                  className="amount-input eco-mono small-input" type="number" min="0" placeholder="€"
                  value={lvl.amount}
                  onChange={(e) => {
                    const next = [...streakLevels];
                    next[i] = { ...next[i], amount: parseFloat(e.target.value) || 0 };
                    setStreakLevels(next);
                  }}
                />
              </div>
              <button className="icon-btn" onClick={() => setStreakLevels(streakLevels.filter((_, idx) => idx !== i))}><Trash2 size={15} /></button>
            </div>
          ))}
        </div>
        <button
          className="btn ghost small"
          style={{ marginTop: "0.6rem" }}
          onClick={() => setStreakLevels([...streakLevels, { days: 1, amount: 0 }])}
        >
          <Plus size={14} /> Añadir nivel
        </button>
        <button
          className="btn primary small"
          style={{ marginTop: "0.6rem", marginLeft: "0.5rem" }}
          onClick={() => {
            const cleaned = streakLevels
              .filter((l) => l.days > 0)
              .sort((a, b) => a.days - b.days);
            onSaveSettings({ streakLevels: cleaned, streakLevelsActive: streakActive });
          }}
        >
          Guardar niveles
        </button>
      </div>
          </>
        )}

        {screen === "jobs" && (
          <>
      <Section icon={Briefcase} title="Trabajos" subtitle="Publica ofertas de trabajo para que tus hijos/as puedan ganar dinero. Tus hijos también te podrán hacer solicitudes de oferta de trabajo, acepta o rechaza sus solicitudes." accent="var(--accent-yellow)" />

      {state.jobRequest && state.jobRequest.status === "pending" && (
        <div className="card">
          <p className="card-title" style={{ marginTop: 0 }}>¡Tu hijo/a busca trabajo!</p>
          <p className="tiny-hint" style={{ marginTop: 0 }}>Ha pedido ganar <b>{fmt(state.jobRequest.amount)} €</b>. Puedes aceptar y ofertarle un trabajo por ese importe (o el que tú decidas) más abajo, o rechazar la solicitud explicando por qué.</p>
          {!decliningRequest ? (
            <div className="row-buttons tight">
              <button
                className="btn primary small"
                onClick={() => { setJobReward(String(state.jobRequest.amount)); onAcceptJobRequest(); }}
              >
                Aceptar y usar este importe
              </button>
              <button className="btn ghost small" onClick={() => setDecliningRequest(true)}>Rechazar</button>
            </div>
          ) : (
            <div className="form-row">
              <input className="text-input" placeholder="Motivo (ej. ahora no hay tareas disponibles)" value={declineReason} onChange={(e) => setDeclineReason(e.target.value)} />
              <button
                className="btn danger small"
                onClick={() => {
                  if (!declineReason.trim()) return;
                  onDeclineJobRequest(declineReason.trim());
                  setDecliningRequest(false); setDeclineReason("");
                }}
              >
                Confirmar rechazo
              </button>
              <button className="btn ghost small" onClick={() => { setDecliningRequest(false); setDeclineReason(""); }}>Cancelar</button>
            </div>
          )}
        </div>
      )}

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>
          Nueva oferta de trabajo
          <InfoTooltip text="Publica una oferta de trabajo a tus hijos. En el campo recompensa debes poner el importe en € por el que publicas la oferta de trabajo y en el campo días de validez los días que estará visible la oferta de trabajo. Es posible publicar la oferta de trabajo a más de un hijo a la vez, pero solo la ejecutará el que primero la acepte." />
        </p>
        <div className="form-row">
          <input className="text-input" placeholder="Título (ej. Sacar la basura)" value={jobTitle} onChange={(e) => setJobTitle(e.target.value)} />
          <input className="amount-input eco-mono" type="number" min="0" placeholder="Recompensa" value={jobReward} onChange={(e) => setJobReward(e.target.value)} />
          <div>
            <label className="tiny-label">Días de validez</label>
            <input className="amount-input eco-mono small-input" type="number" min="1" placeholder="Días" value={jobDays} onChange={(e) => setJobDays(e.target.value)} />
          </div>
        </div>
        {familyChildren.length > 1 && (
          <div className="form-row">
            <span className="tiny-label" style={{ width: "100%" }}>Publicar a (si eliges varios, competirán por ella):</span>
            {familyChildren.map((c) => (
              <label className="checkbox-label" key={c.id}>
                <input
                  type="checkbox"
                  checked={jobTargets.includes(c.id)}
                  onChange={() => setJobTargets(jobTargets.includes(c.id) ? jobTargets.filter((id) => id !== c.id) : [...jobTargets, c.id])}
                />
                {c.name}
              </label>
            ))}
          </div>
        )}
        <button
          className="btn primary small"
          disabled={creatingSharedJob}
          onClick={async () => {
            if (!jobTitle.trim() || !jobReward) return;
            const targets = jobTargets.length > 0 ? jobTargets : [childId];
            if (targets.length === 1 && targets[0] === childId) {
              onAddJob(jobTitle.trim(), parseFloat(jobReward), parseInt(jobDays) || null);
            } else {
              setCreatingSharedJob(true);
              const expiresAt = jobDays && parseInt(jobDays) > 0 ? addDaysKey(dateKey(), parseInt(jobDays)) : null;
              const names = targets.map((id) => familyChildren.find((c) => c.id === id)?.name || "?");
              await onCreateSharedJob(jobTitle.trim(), parseFloat(jobReward), expiresAt, targets, names);
              setCreatingSharedJob(false);
            }
            setJobTitle(""); setJobReward("");
          }}
        >
          <Plus size={14} /> {creatingSharedJob ? "Publicando…" : "Publicar"}
        </button>
      </div>

      <div className="card">
        {negotiating.length > 0 && (
          <>
            <p className="subheading" style={{ marginTop: 0 }}>Propuestas de negociación</p>
            <div className="job-grid">
              {negotiating.map((j) => (
                <div className="job-card" key={j.id}>
                  <p className="job-title">{j.title}</p>
                  <p className="tiny-hint">Oferta original: {fmt(j.reward)} €</p>
                  <Money value={j.proposedReward} />
                  {j.proposedNote && <p className="tiny-hint">"{j.proposedNote}"</p>}
                  <div className="row-buttons tight">
                    <button className="btn primary small" onClick={() => onAcceptNegotiation(j.id)}><Check size={14} /> Aceptar</button>
                    <button className="btn ghost small" onClick={() => onDeclineNegotiation(j.id)}><X size={14} /> Rechazar</button>
                  </div>
                </div>
              ))}
            </div>
          </>
        )}

        {submitted.length > 0 && (
          <>
            <p className="subheading">Pendientes de revisión</p>
            <div className="job-grid">
              {submitted.map((j) => (
                <div className="job-card" key={j.id}>
                  <p className="job-title">{j.title}</p>
                  <Money value={j.reward} />
                  {rejectingJobId === j.id && rejectingJobType === "private" ? (
                    <div className="nego-form">
                      <input className="text-input" placeholder="Motivo del rechazo" value={rejectJobReason} onChange={(e) => setRejectJobReason(e.target.value)} />
                      <div className="row-buttons tight">
                        <button
                          className="btn danger small"
                          onClick={() => {
                            if (!rejectJobReason.trim()) return;
                            onRejectJob(j.id, rejectJobReason.trim());
                            setRejectingJobId(null); setRejectJobReason("");
                          }}
                        >
                          Confirmar rechazo
                        </button>
                        <button className="btn ghost small" onClick={() => { setRejectingJobId(null); setRejectJobReason(""); }}>Cancelar</button>
                      </div>
                    </div>
                  ) : (
                    <div className="row-buttons tight">
                      <button className="btn primary small" onClick={() => onApproveJob(j.id)}><Check size={14} /> Aprobar</button>
                      <button className="btn ghost small" onClick={() => { setRejectingJobId(j.id); setRejectingJobType("private"); }}><X size={14} /> Rechazar</button>
                    </div>
                  )}
                </div>
              ))}
            </div>
          </>
        )}

        <p className="card-title">Todas las ofertas</p>
        <p className="section-sub" style={{ marginBottom: "0.7rem" }}>En la siguiente tabla se puede ver el estado de las ofertas de trabajo publicadas en el tablón de los niños/as. Además podrá ver el histórico de todas las ofertas de trabajo ofertadas.</p>
        <div className="table">
          {others.filter((j) => j.status === "available" || j.status === "claimed").map((j) => {
            const daysLeft = j.status === "available" && j.expiresAt ? daysBetween(dateKey(), j.expiresAt) : null;
            return (
            <div className="table-row" key={j.id}>
              <span>{j.title}{daysLeft !== null && <span className="tiny-hint"> · caduca en {daysLeft <= 0 ? "menos de 1 día" : `${daysLeft} día${daysLeft === 1 ? "" : "s"}`}</span>}</span>
              <Money value={j.reward} size="sm" />
              <Stamp text={j.status === "available" ? "DISPONIBLE" : "ACEPTADA"} tone="gold" />
              <button
                className="icon-btn"
                onClick={() => { if (window.confirm(`¿Seguro que quieres cancelar la oferta "${j.title}"?`)) onDeleteJob(j.id); }}
              >
                <Trash2 size={15} />
              </button>
            </div>
            );
          })}
          {sharedJobs.filter((j) => j.status === "open" || j.status === "claimed" || j.status === "submitted").map((j) => (
            <div className="table-row" key={j.id}>
              <span>
                {j.title}
                <span className="tiny-hint"> · para {(j.targetChildNames || []).join(", ")}</span>
                {j.claimedByName && <span className="tiny-hint"> · se la quedó {j.claimedByName}</span>}
              </span>
              <Money value={j.reward} size="sm" />
              <Stamp
                text={j.status === "open" ? "DISPONIBLE" : j.status === "claimed" ? "ACEPTADA" : "POR REVISAR"}
                tone={j.status === "submitted" ? "orange" : "gold"}
              />
              {rejectingJobId === j.id && rejectingJobType === "shared" ? (
                <div className="input-row">
                  <input className="text-input" placeholder="Motivo del rechazo" value={rejectJobReason} onChange={(e) => setRejectJobReason(e.target.value)} />
                  <button
                    className="btn danger small"
                    onClick={() => {
                      if (!rejectJobReason.trim()) return;
                      onRejectSharedJob(j.id, rejectJobReason.trim());
                      setRejectingJobId(null); setRejectJobReason("");
                    }}
                  >
                    Confirmar
                  </button>
                  <button className="btn ghost small" onClick={() => { setRejectingJobId(null); setRejectJobReason(""); }}>Cancelar</button>
                </div>
              ) : (
                <div className="row-buttons tight">
                  {j.status === "submitted" && (
                    <button className="btn primary small" onClick={() => onApproveSharedJob(j.id)}><Check size={14} /></button>
                  )}
                  {j.status === "submitted" && (
                    <button className="btn ghost small" onClick={() => { setRejectingJobId(j.id); setRejectingJobType("shared"); }}><X size={14} /></button>
                  )}
                  <button
                    className="icon-btn"
                    onClick={() => { if (window.confirm(`¿Seguro que quieres cancelar la oferta "${j.title}"?`)) onDeleteSharedJob(j.id); }}
                  >
                    <Trash2 size={15} />
                  </button>
                </div>
              )}
            </div>
          ))}
        </div>

        <button className="text-link" style={{ marginTop: "0.8rem" }} onClick={() => setJobsHistoryExpanded(!jobsHistoryExpanded)}>
          {jobsHistoryExpanded ? "Ocultar historial" : "Ver historial*"}
        </button>
        {jobsHistoryExpanded && (() => {
          const todayKey = dateKey();
          const privateHistory = others.filter((j) => j.status === "approved" || j.status === "expired" || j.status === "cancelled");
          const sharedHistory = sharedJobs.filter((j) => j.status === "approved" || j.status === "cancelled" || (j.status === "open" && j.expiresAt && j.expiresAt < todayKey));
          if (privateHistory.length === 0 && sharedHistory.length === 0) {
            return <p className="empty-hint" style={{ marginTop: "0.6rem" }}>Todavía no hay ofertas pagadas, caducadas o canceladas.</p>;
          }
          return (
            <>
            <div className="table" style={{ marginTop: "0.6rem" }}>
              {privateHistory.map((j) => (
                <div className="table-row" key={j.id}>
                  <span>{j.title}</span>
                  <Money value={j.reward} size="sm" />
                  <Stamp
                    text={j.status === "approved" ? "PAGADA" : j.status === "expired" ? "CADUCADA" : "CANCELADA"}
                    tone={j.status === "approved" ? "green" : "red"}
                  />
                </div>
              ))}
              {sharedHistory.map((j) => (
                <div className="table-row" key={j.id}>
                  <span>{j.title}<span className="tiny-hint"> · para {(j.targetChildNames || []).join(", ")}</span></span>
                  <Money value={j.reward} size="sm" />
                  <Stamp
                    text={j.status === "approved" ? "PAGADA" : j.status === "cancelled" ? "CANCELADA" : "CADUCADA"}
                    tone={j.status === "approved" ? "green" : "red"}
                  />
                </div>
              ))}
            </div>
            <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 ofertas resueltas (pagadas, caducadas o canceladas).</p>
            </>
          );
        })()}
      </div>
          </>
        )}

        {screen === "market" && (
          <>
      <Section icon={ShoppingBag} title="Mercado" subtitle="Publica caprichos y autoriza los canjes." accent="var(--primary)" />
      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Solicitudes de canje</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Aprueba o rechaza lo que tu hijo/a pide del mercado, y consulta el historial de lo ya resuelto.</p>
        {pendingRedeems.length === 0 && <p className="empty-hint">No hay solicitudes pendientes.</p>}
        <div className="table">
          {pendingRedeems.map((r) => (
            <div className="table-row" key={r.id}>
              <span>{r.itemName}{r.custom && <span className="tiny-hint"> · petición personalizada de tu hijo/a</span>}</span>
              <Money value={-r.cost} size="sm" />
              {state.wallet < r.cost ? (
                <span className="tiny-hint">⚠ saldo insuficiente</span>
              ) : (
                <div className="row-buttons tight">
                  <button className="btn primary small" onClick={() => onApproveRedeem(r.id)}><Check size={14} /></button>
                </div>
              )}
              <button className="icon-btn" onClick={() => onDeclineRedeem(r.id)}><X size={15} /></button>
            </div>
          ))}
        </div>
        {(state.marketRequests || []).filter((r) => r.status === "approved" || r.status === "declined").length > 0 && (
          <>
            <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setRequestsHistoryExpandedParent(!requestsHistoryExpandedParent)}>
              {requestsHistoryExpandedParent ? "Ocultar historial" : "Ver historial*"}
            </button>
            {requestsHistoryExpandedParent && (
              <>
                <div className="table" style={{ marginTop: "0.5rem" }}>
                  {(state.marketRequests || []).filter((r) => r.status === "approved" || r.status === "declined").slice(0, 50).map((r) => (
                    <div className="table-row" key={r.id}>
                      <span>{r.itemName}{r.custom && <span className="tiny-hint"> · petición personalizada</span>}</span>
                      <Money value={-r.cost} size="sm" />
                      <Stamp text={r.status === "approved" ? "APROBADO" : "RECHAZADO"} tone={r.status === "approved" ? "green" : "red"} />
                    </div>
                  ))}
                </div>
                <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 solicitudes resueltas.</p>
              </>
            )}
          </>
        )}
      </div>
          </>
        )}

        {screen === "bank" && (
          <>
      <Section icon={Landmark} title="Banco" subtitle="Configura el ahorro y los préstamos de tu hijo/a — todo desde aquí." accent="var(--secondary)" />

      <div className="window-tabs" style={{ marginBottom: "0.8rem" }}>
        <button className={`window-tab ${parentBankTab === "savings" ? "active" : ""}`} onClick={() => setParentBankTab("savings")}>Ahorro</button>
        <button className={`window-tab ${parentBankTab === "loans" ? "active" : ""}`} onClick={() => setParentBankTab("loans")}>Préstamos</button>
      </div>

      {parentBankTab === "savings" && (
      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Plazos e interés</p>
        <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Tu hijo/a elige el plazo al depositar. El % es mensual y se aplica cada mes mientras el depósito siga bloqueado (sobre lo ya ganado también), así que cuanto más tiempo lo deje quieto, más interés gana.</p>
        <div className="settings-grid">
          {savingsTermPcts.map((pct, i) => (
            <div key={i}>
              <label className="tiny-label">Plazo {i + 1}: meses y % de interés mensual</label>
              <div className="input-row">
                <input
                  className="amount-input eco-mono small-input" type="number" min="1" placeholder="Meses"
                  value={savingsTermMonths[i]}
                  onChange={(e) => {
                    const next = [...savingsTermMonths];
                    next[i] = e.target.value;
                    setSavingsTermMonths(next);
                  }}
                />
                <input
                  className="amount-input eco-mono small-input" type="number" min="0" step="0.1" placeholder="%"
                  value={pct}
                  onChange={(e) => {
                    const next = [...savingsTermPcts];
                    next[i] = e.target.value;
                    setSavingsTermPcts(next);
                  }}
                />
              </div>
            </div>
          ))}
          <div>
            <label className="tiny-label">Aviso previo al vencer (días)</label>
            <input className="amount-input eco-mono" type="number" min="0" value={savingsNoticeDays} onChange={(e) => setSavingsNoticeDays(e.target.value)} />
          </div>
        </div>
        <button
          className="btn primary small"
          style={{ marginTop: "0.75rem" }}
          onClick={() => onSaveSettings({
            savingsTerms: savingsTermPcts.map((pct, i) => ({ months: parseInt(savingsTermMonths[i]) || 1, rate: (parseFloat(pct) || 0) / 100 })),
            savingsNoticeDays: parseInt(savingsNoticeDays) || 0,
          })}
        >
          Guardar intereses
        </button>
      </div>
      )}

      {parentBankTab === "loans" && (
      <>
      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Interés de préstamos</p>
        <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Cuando tu hijo/a pida un préstamo, esto es lo que le costará cada mes hasta que lo devuelva.</p>
        <div className="settings-grid">
          <div>
            <label className="tiny-label">Interés de préstamos mensual (%)</label>
            <input className="amount-input eco-mono" type="number" min="0" step="0.1" value={loanInterestPct} onChange={(e) => setLoanInterestPct(e.target.value)} />
          </div>
          <div>
            <label className="tiny-label">Días sin pagar para considerarlo atrasado</label>
            <input className="amount-input eco-mono" type="number" min="1" value={loanOverdueDays} onChange={(e) => setLoanOverdueDays(e.target.value)} />
          </div>
        </div>
        <p className="tiny-hint">Mientras un préstamo esté atrasado, tu hijo/a no podrá comprar en el Mercado ni pedir otro préstamo, hasta que pague algo.</p>
        <button
          className="btn primary small"
          style={{ marginTop: "0.6rem" }}
          onClick={() => onSaveSettings({ loanInterestRate: (parseFloat(loanInterestPct) || 0) / 100, loanOverdueDays: parseInt(loanOverdueDays) || 30 })}
        >
          Guardar interés
        </button>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Préstamos</p>
        <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Solicitudes pendientes de aprobar y deudas que tu hijo/a todavía está devolviendo.</p>
        {requestedLoans.length > 0 && (
          <>
            <p className="subheading">Solicitudes</p>
            <div className="table">
              {requestedLoans.map((l) => (
                <div className="table-row" key={l.id}>
                  <span>{l.reason}</span>
                  <Money value={l.amount} size="sm" />
                  <div className="row-buttons tight">
                    <button className="btn primary small" onClick={() => onApproveLoan(l.id)}><Check size={14} /></button>
                    <button className="btn ghost small" onClick={() => onRejectLoan(l.id)}><X size={14} /></button>
                  </div>
                </div>
              ))}
            </div>
          </>
        )}
        <p className="subheading">Deudas activas</p>
        {activeLoans.length === 0 && <p className="empty-hint">No hay préstamos activos.</p>}
        <div className="table">
          {activeLoans.map((l) => (
            <div className="table-row" key={l.id}>
              <span>{l.reason}</span>
              <Money value={-l.remaining} size="sm" />
              <span className="tiny-hint">{(l.interestRate * 100).toFixed(1)}%/mes</span>
            </div>
          ))}
        </div>
      </div>

      {(state.loans || []).filter((l) => l.status === "paid").length > 0 && (
        <div className="card">
          <p className="card-title" style={{ marginTop: 0 }}>Historial de préstamos</p>
          <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Préstamos que tu hijo/a ya devolvió por completo.</p>
          <button className="text-link" onClick={() => setLoansHistoryExpandedParent(!loansHistoryExpandedParent)}>
            {loansHistoryExpandedParent ? "Ocultar historial" : "Ver historial*"}
          </button>
          {loansHistoryExpandedParent && (
            <>
              <div className="table" style={{ marginTop: "0.5rem" }}>
                {(state.loans || []).filter((l) => l.status === "paid").slice(0, 50).map((l) => (
                  <div className="table-row" key={l.id}>
                    <span>{l.reason} <span className="tiny-hint">· pidió {fmt(l.amount)} €, interés {fmt(l.totalInterestPaid || 0)} €</span></span>
                    <Money value={-(l.amount + (l.totalInterestPaid || 0))} size="sm" />
                    <Stamp text="PAGADO" tone="green" />
                  </div>
                ))}
              </div>
              <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo los últimos 50 préstamos pagados.</p>
            </>
          )}
        </div>
      )}
      </>
      )}
          </>
        )}

        {screen === "market" && (
          <div className="card">
        <p className="subheading" style={{ marginTop: 0 }}>Publicar capricho nuevo</p>
        <div className="form-row">
          <input className="text-input" placeholder="Nombre del premio" value={itemName} onChange={(e) => setItemName(e.target.value)} />
          <input className="amount-input eco-mono" type="number" min="0" placeholder="Coste" value={itemCost} onChange={(e) => setItemCost(e.target.value)} />
          <input className="emoji-input" placeholder="Icono" value={itemEmoji} onChange={(e) => setItemEmoji(e.target.value)} />
          <EmojiPickerButton value={itemEmoji} onChange={setItemEmoji} palette={MARKET_EMOJI_PALETTE} />
          <button className="btn primary small" onClick={() => { if (!itemName.trim() || !itemCost) return; onAddMarketItem(itemName.trim(), parseFloat(itemCost), itemEmoji.trim()); setItemName(""); setItemCost(""); setItemEmoji(""); }}>
            <Plus size={14} /> Añadir
          </button>
        </div>
        <div className="table">
          {state.market.map((m) => (
            <div className="table-row" key={m.id}>
              <span>{m.emoji} {m.name}</span>
              <Money value={m.cost} size="sm" />
              <button className="icon-btn" onClick={() => onDeleteMarketItem(m.id)}><Trash2 size={15} /></button>
            </div>
          ))}
        </div>
      </div>
        )}

        {screen === "townhall" && (
          <>
      <Section icon={TownHall} title="Ayuntamiento" subtitle="Impuestos, multas y las normas de casa." accent="#8B7BD8" />

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>📜 Normas del pueblo</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Escribe aquí las normas de casa — tu hijo/a las podrá consultar en su propio Ayuntamiento.</p>
        <textarea
          className="text-input"
          style={{ width: "100%", minHeight: "80px", resize: "vertical", fontFamily: "inherit" }}
          placeholder="Ej. Recoger la mesa después de comer, no gritar en casa..."
          value={townRules}
          onChange={(e) => setTownRules(e.target.value)}
        />
        <button className="btn primary small" style={{ marginTop: "0.5rem" }} onClick={() => onSaveSettings({ townRules })}>
          Guardar normas
        </button>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Impuestos</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Se cobran solos cada mes de la cartera de tu hijo/a, para los gastos que tú definas.</p>
        <div className="settings-grid">
          <div>
            <label className="tiny-label">Impuesto mensual</label>
            <div className="input-row">
              <input className="amount-input eco-mono" type="number" min="0" value={taxAmount} onChange={(e) => setTaxAmount(e.target.value)} />
              <label className="checkbox-label"><input type="checkbox" checked={taxActive} onChange={(e) => setTaxActive(e.target.checked)} /> Activo</label>
            </div>
          </div>
          <div>
            <label className="tiny-label">¿En qué se paga el impuesto?</label>
            <input className="text-input" value={taxConcept} onChange={(e) => setTaxConcept(e.target.value)} placeholder="Ej. luz, agua y comida" />
          </div>
          <div>
            <label className="tiny-label">Aviso previo al cobrar (días)</label>
            <input className="amount-input eco-mono" type="number" min="0" value={taxNoticeDays} onChange={(e) => setTaxNoticeDays(e.target.value)} />
          </div>
        </div>
        <p className="tiny-hint">Próximo cobro en {taxDays} día{taxDays === 1 ? "" : "s"}{!state.tax.active && " (desactivado)"}.</p>
        <button
          className="btn primary small"
          onClick={() => onSaveSettings({
            tax: { amount: parseFloat(taxAmount) || 0, active: taxActive, concept: taxConcept.trim() || state.tax.concept },
            taxNoticeDays: parseInt(taxNoticeDays) || 0,
          })}
        >
          Guardar impuesto
        </button>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Multas</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Aplica una multa cuando haga falta, revisa las apelaciones y el historial de lo ya resuelto.</p>
        <div className="form-row">
          <input className="text-input" placeholder="Motivo (ej. no recoger los juguetes)" value={fineReason} onChange={(e) => setFineReason(e.target.value)} />
          <input className="amount-input eco-mono" type="number" min="0" placeholder="Importe" value={fineAmount} onChange={(e) => setFineAmount(e.target.value)} />
          <button className="btn danger small" onClick={() => { if (!fineReason.trim() || !fineAmount) return; onApplyFine(fineReason.trim(), parseFloat(fineAmount)); setFineReason(""); setFineAmount(""); }}>
            Aplicar multa
          </button>
        </div>
        <div className="input-row" style={{ marginTop: "0.4rem" }}>
          <label className="tiny-label">Días para apelar antes de que se cobre sola</label>
          <input className="amount-input eco-mono small-input" type="number" min="0" value={fineAppealDays} onChange={(e) => setFineAppealDays(e.target.value)} />
          <button className="btn ghost small" onClick={() => onSaveSettings({ fineAppealDays: parseInt(fineAppealDays) || 0 })}>Guardar</button>
        </div>

        {(state.fines || []).filter((f) => f.status === "disputed").length > 0 && (
          <>
            <p className="subheading">Apelaciones pendientes</p>
            <div className="table">
              {(state.fines || []).filter((f) => f.status === "disputed").map((f) => (
                <div className="table-row" key={f.id} style={{ flexWrap: "wrap" }}>
                  <span>{f.reason} <span className="tiny-hint">· dice: "{f.disputeReason}"</span></span>
                  <Money value={-f.remaining} size="sm" />
                  {rejectingFineId === f.id ? (
                    <div className="input-row" style={{ flexBasis: "100%", marginTop: "0.3rem" }}>
                      <input className="text-input" placeholder="Motivo del rechazo" value={fineRejectReasonInput} onChange={(e) => setFineRejectReasonInput(e.target.value)} />
                      <button
                        className="btn danger small"
                        onClick={() => {
                          if (!fineRejectReasonInput.trim()) return;
                          onRejectFineDispute(f.id, fineRejectReasonInput.trim());
                          setRejectingFineId(null); setFineRejectReasonInput("");
                        }}
                      >
                        Confirmar
                      </button>
                      <button className="btn ghost small" onClick={() => { setRejectingFineId(null); setFineRejectReasonInput(""); }}>Cancelar</button>
                    </div>
                  ) : (
                    <div className="row-buttons tight">
                      <button className="btn primary small" onClick={() => onApproveFineDispute(f.id)}>Dar la razón (perdonar)</button>
                      <button className="btn ghost small" onClick={() => setRejectingFineId(f.id)}>Rechazar</button>
                    </div>
                  )}
                </div>
              ))}
            </div>
          </>
        )}

        <p className="subheading">Pendientes de pago</p>
        {(state.fines || []).filter((f) => f.status === "pending").length === 0 && <p className="empty-hint">No se han aplicado multas todavía.</p>}
        <div className="table">
          {(state.fines || []).filter((f) => f.status === "pending").map((f) => (
            <div className="table-row" key={f.id}>
              <span>{f.reason}</span>
              <Money value={-f.remaining} size="sm" />
              <Stamp text="PENDIENTE" tone="gold" />
            </div>
          ))}
        </div>

        {(state.fines || []).filter((f) => f.status === "paid" || f.status === "waived").length > 0 && (
          <>
            <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setFinesHistoryExpandedParent(!finesHistoryExpandedParent)}>
              {finesHistoryExpandedParent ? "Ocultar historial" : "Ver historial*"}
            </button>
            {finesHistoryExpandedParent && (
              <>
                <div className="table" style={{ marginTop: "0.5rem" }}>
                  {(state.fines || []).filter((f) => f.status === "paid" || f.status === "waived").slice(0, 50).map((f) => (
                    <div className="table-row" key={f.id}>
                      <span>{f.reason}</span>
                      <Money value={-f.amount} size="sm" />
                      <Stamp text={f.status === "paid" ? "PAGADA" : "PERDONADA"} tone="green" />
                    </div>
                  ))}
                </div>
                <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 multas resueltas.</p>
              </>
            )}
          </>
        )}
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>✉️ Buzón de peticiones</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Aquí verás lo que tu hijo/a te escriba — ideas, quejas o peticiones.</p>
        {(state.petitions || []).filter((p) => p.status === "pending").length === 0 && (
          <p className="empty-hint">No tienes ningún mensaje nuevo.</p>
        )}
        <div className="table">
          {(state.petitions || []).filter((p) => p.status === "pending").map((p) => (
            <div className="table-row" key={p.id}>
              <span>{p.text} <span className="tiny-hint">· {p.date}</span></span>
              <button className="btn ghost small" onClick={() => onMarkPetitionRead(p.id)}>Marcar como leída</button>
            </div>
          ))}
        </div>
        {(state.petitions || []).filter((p) => p.status === "read").length > 0 && (
          <>
            <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setPetitionsHistoryExpandedParent(!petitionsHistoryExpandedParent)}>
              {petitionsHistoryExpandedParent ? "Ocultar historial" : "Ver historial*"}
            </button>
            {petitionsHistoryExpandedParent && (
              <>
                <div className="table" style={{ marginTop: "0.5rem" }}>
                  {(state.petitions || []).filter((p) => p.status === "read").slice(0, 50).map((p) => (
                    <div className="table-row" key={p.id}>
                      <span>{p.text} <span className="tiny-hint">· {p.date}</span></span>
                      <Stamp text="LEÍDA" tone="green" />
                    </div>
                  ))}
                </div>
                <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 peticiones leídas.</p>
              </>
            )}
          </>
        )}
      </div>
          </>
        )}

        {screen === "donations" && (
          <>
      <Section icon={HeartHandshake} title="Regalos y donaciones" subtitle="Registra los regalos de familiares, y gestiona el bote solidario." accent="var(--accent-blue)" />

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>🎁 Registrar un regalo familiar</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Cuando un abuelo, tío u otro familiar le dé dinero a tu hijo/a, regístralo aquí — se aplica el impuesto que definas antes de sumarlo a su cartera.</p>
        <div className="form-row">
          <input className="text-input" placeholder="¿Quién se lo dio? (ej. Abuela Pili)" value={giftGiver} onChange={(e) => setGiftGiver(e.target.value)} />
          <input className="amount-input eco-mono" type="number" min="0" placeholder="Importe" value={giftAmount} onChange={(e) => setGiftAmount(e.target.value)} />
          <button
            className="btn primary small"
            onClick={() => { if (!giftGiver.trim() || !giftAmount) return; onAddFamilyGift(giftGiver.trim(), parseFloat(giftAmount)); setGiftGiver(""); setGiftAmount(""); }}
          >
            Registrar regalo
          </button>
        </div>
        <div className="input-row" style={{ marginTop: "0.4rem" }}>
          <label className="tiny-label">Impuesto sobre regalos familiares (%)</label>
          <input className="amount-input eco-mono small-input" type="number" min="0" step="1" value={giftTaxPct} onChange={(e) => setGiftTaxPct(e.target.value)} />
          <button className="btn ghost small" onClick={() => onSaveSettings({ familyGiftTaxRate: (parseFloat(giftTaxPct) || 0) / 100 })}>Guardar</button>
        </div>
        {(state.familyGifts || []).length > 0 && (
          <>
            <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setGiftsHistoryExpandedParent(!giftsHistoryExpandedParent)}>
              {giftsHistoryExpandedParent ? "Ocultar historial" : "Ver historial*"}
            </button>
            {giftsHistoryExpandedParent && (
              <>
                <div className="table" style={{ marginTop: "0.5rem" }}>
                  {state.familyGifts.slice(0, 50).map((g) => (
                    <div className="table-row" key={g.id}>
                      <span>{g.giverName} <span className="tiny-hint">· {g.date} · bruto {fmt(g.gross)} €, impuesto {fmt(g.tax)} €</span></span>
                      <Money value={g.net} size="sm" />
                    </div>
                  ))}
                </div>
                <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo los últimos 50 regalos.</p>
              </>
            )}
          </>
        )}
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>💗 Bote solidario</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Ponle nombre y, si quieres, una meta — tu hijo/a verá una barra de progreso, igual que con su ahorro.</p>
        <p className="tiny-hint">Acumulado: <Money value={state.donationPot.total} size="sm" /></p>
        <div className="form-row">
          <input className="text-input" placeholder="Nombre del bote" value={potName} onChange={(e) => setPotName(e.target.value)} />
          <input className="amount-input eco-mono" type="number" min="0" placeholder="Meta (opcional)" value={potTarget} onChange={(e) => setPotTarget(e.target.value)} />
          <button
            className="btn primary small"
            onClick={() => onSaveSettings({ donationPot: { ...state.donationPot, name: potName.trim() || state.donationPot.name, target: parseFloat(potTarget) || 0 } })}
          >
            Guardar
          </button>
          <button className="btn ghost small" onClick={onEmptyPot}>Vaciar bote (ya entregado)</button>
        </div>
        {(state.donationHistory || []).length > 0 && (
          <>
            <button className="text-link" style={{ marginTop: "0.6rem" }} onClick={() => setDonationHistoryExpandedParent(!donationHistoryExpandedParent)}>
              {donationHistoryExpandedParent ? "Ocultar historial" : "Ver historial*"}
            </button>
            {donationHistoryExpandedParent && (
              <>
                <div className="table" style={{ marginTop: "0.5rem" }}>
                  {state.donationHistory.slice(0, 50).map((h) => (
                    <div className="table-row" key={h.id}>
                      <span>{h.name} <span className="tiny-hint">· {h.date}</span></span>
                      <Money value={h.amount} size="sm" />
                    </div>
                  ))}
                </div>
                <p className="tiny-hint" style={{ marginTop: "0.5rem" }}>*Se muestran como máximo las últimas 50 entregas.</p>
              </>
            )}
          </>
        )}
      </div>
          </>
        )}

        {screen === "settings" && (
          <>
      <Section icon={Settings} title="Datos de la familia" accent="#B8B0D8" />

      {!emailVerified && (
        <div className="card" style={{ background: "#FFF3C4", borderColor: "#E5C15C" }}>
          <p className="card-title" style={{ marginTop: 0 }}>⚠️ Tu email todavía no está verificado</p>
          <p className="section-sub" style={{ marginBottom: "0.5rem" }}>
            Te enviamos un correo de confirmación al crear la cuenta. <b>Revisa también la carpeta de SPAM o correo no deseado</b> — suele acabar ahí. Verificarlo te hace falta para recuperar la contraseña si algún día la olvidas.
          </p>
          <button
            className="btn ghost small"
            disabled={resendBusy}
            onClick={async () => {
              setResendBusy(true); setResendMsg("");
              try { await window.resendVerificationEmail(); setResendMsg("Correo reenviado — revisa tu bandeja (y el SPAM)."); }
              catch (e) { setResendMsg("No se pudo reenviar. Inténtalo de nuevo en unos minutos."); }
              setResendBusy(false);
            }}
          >
            Reenviar correo de verificación
          </button>
          {resendMsg && <p className="tiny-hint" style={{ marginTop: "0.4rem" }}>{resendMsg}</p>}
        </div>
      )}

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Datos de la familia</p>
        <p className="section-sub" style={{ marginBottom: "0.6rem" }}>Familia actual: <b>{currentFamilyName || "(sin nombre)"}</b></p>
        <div className="settings-grid">
          <div>
            <label className="tiny-label">Nombre de la familia</label>
            <input className="text-input" placeholder="Nombre de la familia" value={familyNameInput} onChange={(e) => setFamilyNameInput(e.target.value)} />
          </div>
          <div>
            <label className="tiny-label">Nombre del niño/a</label>
            <input className="text-input" value={childName} onChange={(e) => setChildName(e.target.value)} />
          </div>
          <div>
            <label className="tiny-label">Nuevo PIN para entrar en la Oficina (menú de padres/madres)</label>
            <input className="text-input eco-mono" maxLength={8} placeholder="Dejar en blanco para no cambiar" value={newPin} onChange={(e) => setNewPin(e.target.value)} />
          </div>
        </div>
        <button
          className="btn primary small"
          style={{ marginTop: "0.75rem" }}
          onClick={async () => {
            if (familyNameInput.trim() && familyNameInput.trim() !== currentFamilyName) {
              await window.renameFamily(familyId, familyNameInput.trim());
              setCurrentFamilyName(familyNameInput.trim());
            }
            if (newPin.trim()) {
              await onSetOfficePin(newPin.trim());
            }
            onSaveSettings({ childName: childName.trim() || state.childName });
            setNewPin("");
          }}
        >
          Guardar cambios
        </button>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Acceso de los hijos/as</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>El código de cada hijo/a se ve aquí en todo momento, por si lo necesitáis para entrar desde otro dispositivo. Si un hijo/a olvida su PIN, puedes ponerle uno nuevo.</p>
        {(familyChildren || []).length === 0 && <p className="empty-hint">Todavía no hay hijos/as en la familia.</p>}
        <div className="table">
          {(familyChildren || []).map((c) => (
            <div key={c.id} className="table-row" style={{ flexWrap: "wrap" }}>
              <span>{c.name}</span>
              <span className="eco-mono" style={{ letterSpacing: "0.15em", fontWeight: 800 }}>{c.code}</span>
              <div className="input-row" style={{ flexBasis: "100%", marginTop: "0.3rem" }}>
                <input
                  className="text-input eco-mono" maxLength={8} placeholder="Nuevo PIN"
                  value={childPinInputs[c.id] || ""}
                  onChange={(e) => setChildPinInputs({ ...childPinInputs, [c.id]: e.target.value })}
                />
                <button
                  className="btn ghost small"
                  onClick={async () => {
                    const pin = (childPinInputs[c.id] || "").trim();
                    if (!pin) return;
                    try {
                      await onResetChildPin(c.id, pin);
                      setChildPinStatus({ ...childPinStatus, [c.id]: "✔ PIN actualizado." });
                      setChildPinInputs({ ...childPinInputs, [c.id]: "" });
                    } catch (e) {
                      setChildPinStatus({ ...childPinStatus, [c.id]: "No se pudo cambiar el PIN. Inténtalo de nuevo." });
                    }
                  }}
                >
                  Cambiar PIN
                </button>
              </div>
              {childPinStatus[c.id] && <p className="tiny-hint" style={{ flexBasis: "100%" }}>{childPinStatus[c.id]}</p>}
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Avisos que recibe tu hijo/a</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Desmarca lo que no quieras que le llegue como notificación (el resto de la app sigue funcionando igual, solo se calla el aviso).</p>
        <div className="settings-grid">
          {BADGE_SECTIONS.map((sec) => (
            <label className="checkbox-label" key={sec.key}>
              <input
                type="checkbox"
                checked={notifPrefs[sec.key] !== false}
                onChange={(e) => setNotifPrefs({ ...notifPrefs, [sec.key]: e.target.checked })}
              />
              {sec.label}
            </label>
          ))}
        </div>
        <button className="btn primary small" style={{ marginTop: "0.6rem" }} onClick={() => onSaveSettings({ notificationPrefs: notifPrefs })}>
          Guardar avisos
        </button>
      </div>

      <div className="card">
        <p className="card-title" style={{ marginTop: 0 }}>Exportar historial</p>
        <p className="section-sub" style={{ marginBottom: "0.5rem" }}>Descarga todos los movimientos de tu hijo/a en un archivo, por si quieres guardarlo aparte o revisarlo con calma.</p>
        <button
          className="btn ghost small"
          onClick={() => {
            const header = "Fecha,Descripción,Categoría,Importe,Total tras el movimiento\n";
            const rows = (state.ledger || [])
              .map((e) => [e.date, `"${(e.desc || "").replace(/"/g, '""')}"`, e.category || "otros", e.amount, e.balanceAfter ?? ""].join(","))
              .join("\n");
            const blob = new Blob([header + rows], { type: "text/csv;charset=utf-8;" });
            const url = URL.createObjectURL(blob);
            const a = document.createElement("a");
            a.href = url;
            a.download = `historial-${state.childName || "clevank"}.csv`;
            a.click();
            URL.revokeObjectURL(url);
          }}
        >
          Descargar historial (.csv)
        </button>
        <p className="section-sub" style={{ marginTop: "0.8rem", marginBottom: "0.5rem" }}>O descarga una copia completa de toda la familia (todos los hijos/as, con todos sus datos) — pensado como red de seguridad antes de borrar algo importante.</p>
        <button
          className="btn ghost small"
          disabled={backupBusy}
          onClick={async () => {
            setBackupBusy(true);
            try {
              const backup = await window.exportFamilyBackup(familyId);
              const blob = new Blob([JSON.stringify(backup, null, 2)], { type: "application/json;charset=utf-8;" });
              const url = URL.createObjectURL(blob);
              const a = document.createElement("a");
              a.href = url;
              a.download = `copia-familia-${(backup.familyName || "clevank").replace(/\s+/g, "-")}.json`;
              a.click();
              URL.revokeObjectURL(url);
            } catch (e) { /* silencioso, no es una accion critica */ }
            setBackupBusy(false);
          }}
        >
          Descargar copia completa de la familia (.json)
        </button>
      </div>
          </>
        )}

        {screen === "ledger" && (
          <>
      <Section icon={ScrollText} title="Historial completo" accent="#B8B0D8" />
      <div className="card">
        <div className="weekday-row" style={{ marginBottom: "0.6rem" }}>
          {LEDGER_FILTERS.map((f) => (
            <button
              key={f.key}
              className={`term-chip ${ledgerFilter === f.key ? "active" : ""}`}
              onClick={() => setLedgerFilter(f.key)}
            >
              {f.label}
            </button>
          ))}
        </div>
        <LedgerList entries={ledgerFilter === "todo" ? state.ledger : state.ledger.filter((e) => e.category === ledgerFilter)} />
      </div>
          </>
        )}
      </div>
    );
  }

  return (
    <div className="stack">
      {notifBannerParent}
      {balanceCard}

      <div className="icon-grid">
        {PARENT_SCREENS.map((s) => (
          <button key={s.key} className="icon-tile" style={{ "--tile-color": s.color }} onClick={() => setScreen(s.key)}>
            {badges[s.key] > 0 && <span className="tile-badge">{badges[s.key]}</span>}
            <span className="tile-icon"><s.icon size={26} strokeWidth={2.3} /></span>
            <span className="tile-label">{s.label}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

