{"id":36,"date":"2026-04-16T13:45:24","date_gmt":"2026-04-16T18:45:24","guid":{"rendered":"https:\/\/pruebas.misterquin.com\/?page_id=36"},"modified":"2026-04-16T13:47:26","modified_gmt":"2026-04-16T18:47:26","slug":"36-2","status":"publish","type":"page","link":"https:\/\/pruebas.misterquin.com\/?page_id=36","title":{"rendered":"finanzas"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>FinanzApp \u2014 Tu dinero, tu control<\/title>\n<script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js@4.4.4\/dist\/chart.umd.min.js\"><\/script>\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Space+Grotesk:wght@400;500;600;700&#038;family=DM+Sans:wght@300;400;500;600;700&#038;display=swap\" rel=\"stylesheet\">\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.5.1\/css\/all.min.css\">\n<style>\n  :root {\n    --bg: #0B0E14;\n    --bg2: #111520;\n    --card: #161B28;\n    --card-hover: #1C2235;\n    --border: #252D40;\n    --fg: #E4E7EF;\n    --muted: #6C7693;\n    --accent: #00D68F;\n    --accent-dim: rgba(0,214,143,0.12);\n    --danger: #FF6161;\n    --danger-dim: rgba(255,97,97,0.12);\n    --warning: #FFB547;\n    --warning-dim: rgba(255,181,71,0.12);\n    --info: #47B5FF;\n    --info-dim: rgba(71,181,255,0.12);\n  }\n\n  * { margin:0; padding:0; box-sizing:border-box; }\n\n  body {\n    font-family: 'DM Sans', sans-serif;\n    background: var(--bg);\n    color: var(--fg);\n    min-height: 100vh;\n    overflow-x: hidden;\n  }\n\n  h1, h2, h3, h4, h5 { font-family: 'Space Grotesk', sans-serif; }\n\n  \/* Fondo animado *\/\n  .bg-glow {\n    position: fixed;\n    top: -200px; left: -200px;\n    width: 600px; height: 600px;\n    background: radial-gradient(circle, rgba(0,214,143,0.06) 0%, transparent 70%);\n    pointer-events: none;\n    z-index: 0;\n    animation: floatGlow 20s ease-in-out infinite;\n  }\n  .bg-glow-2 {\n    position: fixed;\n    bottom: -300px; right: -200px;\n    width: 700px; height: 700px;\n    background: radial-gradient(circle, rgba(71,181,255,0.04) 0%, transparent 70%);\n    pointer-events: none;\n    z-index: 0;\n    animation: floatGlow2 25s ease-in-out infinite;\n  }\n\n  @keyframes floatGlow {\n    0%, 100% { transform: translate(0,0); }\n    50% { transform: translate(100px, 80px); }\n  }\n  @keyframes floatGlow2 {\n    0%, 100% { transform: translate(0,0); }\n    50% { transform: translate(-80px, -60px); }\n  }\n\n  \/* Scrollbar *\/\n  ::-webkit-scrollbar { width: 6px; }\n  ::-webkit-scrollbar-track { background: var(--bg2); }\n  ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }\n  ::-webkit-scrollbar-thumb:hover { background: var(--muted); }\n\n  \/* Sidebar *\/\n  .sidebar {\n    position: fixed;\n    left: 0; top: 0;\n    width: 260px; height: 100vh;\n    background: var(--bg2);\n    border-right: 1px solid var(--border);\n    z-index: 50;\n    display: flex;\n    flex-direction: column;\n    transition: transform 0.3s ease;\n  }\n\n  .sidebar-link {\n    display: flex; align-items: center; gap: 12px;\n    padding: 12px 20px;\n    color: var(--muted);\n    font-size: 14px; font-weight: 500;\n    border-radius: 10px;\n    margin: 2px 12px;\n    cursor: pointer;\n    transition: all 0.2s;\n    text-decoration: none;\n  }\n  .sidebar-link:hover {\n    background: var(--card);\n    color: var(--fg);\n  }\n  .sidebar-link.active {\n    background: var(--accent-dim);\n    color: var(--accent);\n  }\n  .sidebar-link i { width: 20px; text-align: center; font-size: 16px; }\n\n  \/* Main *\/\n  .main-content {\n    margin-left: 260px;\n    padding: 30px 40px;\n    position: relative;\n    z-index: 1;\n    min-height: 100vh;\n  }\n\n  \/* Cards *\/\n  .card {\n    background: var(--card);\n    border: 1px solid var(--border);\n    border-radius: 16px;\n    padding: 24px;\n    transition: all 0.25s;\n  }\n  .card:hover { border-color: #353D55; }\n\n  \/* Stat cards *\/\n  .stat-card {\n    position: relative;\n    overflow: hidden;\n  }\n  .stat-card::before {\n    content: '';\n    position: absolute;\n    top: -30px; right: -30px;\n    width: 100px; height: 100px;\n    border-radius: 50%;\n    opacity: 0.08;\n    transition: transform 0.4s;\n  }\n  .stat-card:hover::before { transform: scale(1.5); }\n  .stat-card.green::before { background: var(--accent); }\n  .stat-card.red::before { background: var(--danger); }\n  .stat-card.blue::before { background: var(--info); }\n  .stat-card.yellow::before { background: var(--warning); }\n\n  \/* Inputs *\/\n  .input-field {\n    background: var(--bg);\n    border: 1px solid var(--border);\n    border-radius: 10px;\n    padding: 10px 14px;\n    color: var(--fg);\n    font-size: 14px;\n    font-family: 'DM Sans', sans-serif;\n    width: 100%;\n    transition: border-color 0.2s;\n    outline: none;\n  }\n  .input-field:focus { border-color: var(--accent); }\n  .input-field::placeholder { color: var(--muted); }\n\n  select.input-field { cursor: pointer; appearance: none;\n    background-image: url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='12' height='12' fill='%236C7693' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'\/%3E%3C\/svg%3E\");\n    background-repeat: no-repeat;\n    background-position: right 12px center;\n    padding-right: 36px;\n  }\n  select.input-field option { background: var(--card); color: var(--fg); }\n\n  \/* Botones *\/\n  .btn {\n    display: inline-flex; align-items: center; gap: 8px;\n    padding: 10px 20px;\n    border-radius: 10px;\n    font-size: 14px; font-weight: 600;\n    font-family: 'DM Sans', sans-serif;\n    cursor: pointer;\n    border: none;\n    transition: all 0.2s;\n  }\n  .btn-primary {\n    background: var(--accent);\n    color: #0B0E14;\n  }\n  .btn-primary:hover { background: #00F0A0; transform: translateY(-1px); box-shadow: 0 4px 20px rgba(0,214,143,0.3); }\n  .btn-danger {\n    background: var(--danger-dim);\n    color: var(--danger);\n  }\n  .btn-danger:hover { background: rgba(255,97,97,0.2); }\n  .btn-ghost {\n    background: transparent;\n    color: var(--muted);\n    border: 1px solid var(--border);\n  }\n  .btn-ghost:hover { color: var(--fg); border-color: var(--muted); }\n\n  \/* Modal *\/\n  .modal-overlay {\n    position: fixed; inset: 0;\n    background: rgba(0,0,0,0.6);\n    backdrop-filter: blur(4px);\n    z-index: 100;\n    display: flex; align-items: center; justify-content: center;\n    opacity: 0; pointer-events: none;\n    transition: opacity 0.25s;\n  }\n  .modal-overlay.show { opacity: 1; pointer-events: auto; }\n  .modal-box {\n    background: var(--card);\n    border: 1px solid var(--border);\n    border-radius: 20px;\n    padding: 32px;\n    width: 90%; max-width: 500px;\n    transform: translateY(20px) scale(0.97);\n    transition: transform 0.3s;\n  }\n  .modal-overlay.show .modal-box { transform: translateY(0) scale(1); }\n\n  \/* Toast *\/\n  .toast-container {\n    position: fixed; top: 20px; right: 20px;\n    z-index: 200;\n    display: flex; flex-direction: column; gap: 8px;\n  }\n  .toast {\n    padding: 14px 20px;\n    border-radius: 12px;\n    font-size: 14px; font-weight: 500;\n    animation: toastIn 0.3s ease, toastOut 0.3s ease 2.5s forwards;\n    display: flex; align-items: center; gap: 10px;\n    box-shadow: 0 8px 30px rgba(0,0,0,0.3);\n  }\n  .toast.success { background: #0d2818; border: 1px solid #00D68F44; color: var(--accent); }\n  .toast.error { background: #2a0f0f; border: 1px solid #FF616144; color: var(--danger); }\n  .toast.info { background: #0f1a2a; border: 1px solid #47B5FF44; color: var(--info); }\n\n  @keyframes toastIn { from { opacity:0; transform:translateX(40px); } to { opacity:1; transform:translateX(0); } }\n  @keyframes toastOut { from { opacity:1; } to { opacity:0; transform:translateY(-10px); } }\n\n  \/* Transacci\u00f3n row *\/\n  .txn-row {\n    display: grid;\n    grid-template-columns: 40px 1fr 120px 100px 80px;\n    align-items: center;\n    gap: 12px;\n    padding: 14px 16px;\n    border-radius: 12px;\n    transition: background 0.15s;\n  }\n  .txn-row:hover { background: var(--card-hover); }\n\n  .txn-icon {\n    width: 40px; height: 40px;\n    border-radius: 10px;\n    display: flex; align-items: center; justify-content: center;\n    font-size: 16px;\n  }\n\n  \/* Presupuesto barra *\/\n  .budget-bar-bg {\n    height: 10px;\n    background: var(--bg);\n    border-radius: 5px;\n    overflow: hidden;\n  }\n  .budget-bar-fill {\n    height: 100%;\n    border-radius: 5px;\n    transition: width 0.6s ease;\n  }\n\n  \/* Tabs *\/\n  .tab-btn {\n    padding: 8px 18px;\n    border-radius: 8px;\n    font-size: 13px; font-weight: 600;\n    cursor: pointer;\n    border: none;\n    background: transparent;\n    color: var(--muted);\n    font-family: 'DM Sans', sans-serif;\n    transition: all 0.2s;\n  }\n  .tab-btn.active { background: var(--accent-dim); color: var(--accent); }\n  .tab-btn:hover:not(.active) { color: var(--fg); }\n\n  \/* Mobile *\/\n  .mobile-header {\n    display: none;\n    position: fixed; top:0; left:0; right:0;\n    height: 60px;\n    background: var(--bg2);\n    border-bottom: 1px solid var(--border);\n    z-index: 60;\n    align-items: center;\n    padding: 0 16px;\n  }\n\n  @media (max-width: 900px) {\n    .sidebar { transform: translateX(-100%); }\n    .sidebar.open { transform: translateX(0); }\n    .main-content { margin-left: 0; padding: 80px 16px 30px; }\n    .mobile-header { display: flex; }\n    .txn-row { grid-template-columns: 36px 1fr 90px 60px; }\n    .txn-row .txn-date { display: none; }\n  }\n\n  \/* Animaci\u00f3n entrada *\/\n  .fade-in { animation: fadeIn 0.4s ease; }\n  @keyframes fadeIn { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:translateY(0); } }\n\n  \/* N\u00famero animado *\/\n  .counter { transition: all 0.4s ease; }\n\n  @media (prefers-reduced-motion: reduce) {\n    *, *::before, *::after {\n      animation-duration: 0.01ms !important;\n      transition-duration: 0.01ms !important;\n    }\n  }\n<\/style>\n<\/head>\n<body>\n\n<!-- Fondos animados -->\n<div class=\"bg-glow\"><\/div>\n<div class=\"bg-glow-2\"><\/div>\n\n<!-- Toast container -->\n<div class=\"toast-container\" id=\"toastContainer\"><\/div>\n\n<!-- Mobile Header -->\n<header class=\"mobile-header\">\n  <button onclick=\"toggleSidebar()\" style=\"background:none;border:none;color:var(--fg);font-size:20px;cursor:pointer;\" aria-label=\"Men\u00fa\">\n    <i class=\"fas fa-bars\"><\/i>\n  <\/button>\n  <span style=\"margin-left:12px;font-family:'Space Grotesk';font-weight:700;font-size:18px;color:var(--accent);\">FinanzApp<\/span>\n<\/header>\n\n<!-- Sidebar -->\n<aside class=\"sidebar\" id=\"sidebar\" role=\"navigation\" aria-label=\"Navegaci\u00f3n principal\">\n  <div style=\"padding:28px 24px 20px;\">\n    <div style=\"display:flex;align-items:center;gap:10px;\">\n      <div style=\"width:36px;height:36px;background:var(--accent);border-radius:10px;display:flex;align-items:center;justify-content:center;\">\n        <i class=\"fas fa-wallet\" style=\"color:#0B0E14;font-size:16px;\"><\/i>\n      <\/div>\n      <span style=\"font-family:'Space Grotesk';font-weight:700;font-size:20px;color:var(--fg);\">FinanzApp<\/span>\n    <\/div>\n  <\/div>\n\n  <nav style=\"flex:1;padding-top:8px;\">\n    <a class=\"sidebar-link active\" data-page=\"dashboard\" onclick=\"navigate('dashboard')\">\n      <i class=\"fas fa-chart-pie\"><\/i> Dashboard\n    <\/a>\n    <a class=\"sidebar-link\" data-page=\"transacciones\" onclick=\"navigate('transacciones')\">\n      <i class=\"fas fa-exchange-alt\"><\/i> Transacciones\n    <\/a>\n    <a class=\"sidebar-link\" data-page=\"presupuesto\" onclick=\"navigate('presupuesto')\">\n      <i class=\"fas fa-bullseye\"><\/i> Presupuesto\n    <\/a>\n    <a class=\"sidebar-link\" data-page=\"reportes\" onclick=\"navigate('reportes')\">\n      <i class=\"fas fa-chart-bar\"><\/i> Reportes\n    <\/a>\n  <\/nav>\n\n  <div style=\"padding:16px 12px;border-top:1px solid var(--border);\">\n    <button class=\"sidebar-link\" onclick=\"exportData()\" style=\"width:100%;margin:0;\">\n      <i class=\"fas fa-download\"><\/i> Exportar datos\n    <\/button>\n    <button class=\"sidebar-link\" onclick=\"importDataPrompt()\" style=\"width:100%;margin:0;\">\n      <i class=\"fas fa-upload\"><\/i> Importar datos\n    <\/button>\n    <input type=\"file\" id=\"importFile\" accept=\".json\" style=\"display:none\" onchange=\"importData(event)\">\n  <\/div>\n<\/aside>\n\n<!-- Main Content -->\n<main class=\"main-content\" id=\"mainContent\">\n\n  <!-- DASHBOARD -->\n  <section id=\"page-dashboard\" class=\"fade-in\">\n    <div style=\"display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:16px;margin-bottom:28px;\">\n      <div>\n        <h1 style=\"font-size:28px;font-weight:700;\" id=\"greeting\">Buen dia<\/h1>\n        <p style=\"color:var(--muted);font-size:14px;margin-top:4px;\" id=\"dateDisplay\"><\/p>\n      <\/div>\n      <button class=\"btn btn-primary\" onclick=\"openModal()\">\n        <i class=\"fas fa-plus\"><\/i> Nueva transaccion\n      <\/button>\n    <\/div>\n\n    <!-- Stat cards -->\n    <div style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:28px;\">\n      <div class=\"card stat-card green\">\n        <p style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:1px;\">Balance total<\/p>\n        <p style=\"font-size:28px;font-weight:700;margin-top:8px;font-family:'Space Grotesk';\" id=\"statBalance\">$0<\/p>\n        <p style=\"font-size:12px;color:var(--accent);margin-top:4px;\" id=\"statBalanceChange\"><\/p>\n      <\/div>\n      <div class=\"card stat-card blue\">\n        <p style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:1px;\">Ingresos del mes<\/p>\n        <p style=\"font-size:28px;font-weight:700;margin-top:8px;font-family:'Space Grotesk';color:var(--info);\" id=\"statIncome\">$0<\/p>\n      <\/div>\n      <div class=\"card stat-card red\">\n        <p style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:1px;\">Gastos del mes<\/p>\n        <p style=\"font-size:28px;font-weight:700;margin-top:8px;font-family:'Space Grotesk';color:var(--danger);\" id=\"statExpense\">$0<\/p>\n      <\/div>\n      <div class=\"card stat-card yellow\">\n        <p style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:1px;\">Presupuesto restante<\/p>\n        <p style=\"font-size:28px;font-weight:700;margin-top:8px;font-family:'Space Grotesk';color:var(--warning);\" id=\"statBudgetLeft\">$0<\/p>\n      <\/div>\n    <\/div>\n\n    <!-- Gr\u00e1ficos -->\n    <div style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:28px;\">\n      <div class=\"card\" style=\"min-height:300px;\">\n        <h3 style=\"font-size:16px;font-weight:600;margin-bottom:16px;\">Gastos por categoria<\/h3>\n        <div style=\"position:relative;height:250px;\">\n          <canvas id=\"chartCategory\"><\/canvas>\n        <\/div>\n      <\/div>\n      <div class=\"card\" style=\"min-height:300px;\">\n        <h3 style=\"font-size:16px;font-weight:600;margin-bottom:16px;\">Ingresos vs Gastos (6 meses)<\/h3>\n        <div style=\"position:relative;height:250px;\">\n          <canvas id=\"chartMonthly\"><\/canvas>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- \u00daltimas transacciones -->\n    <div class=\"card\">\n      <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;\">\n        <h3 style=\"font-size:16px;font-weight:600;\">Ultimas transacciones<\/h3>\n        <a style=\"color:var(--accent);font-size:13px;cursor:pointer;font-weight:500;\" onclick=\"navigate('transacciones')\">Ver todas <i class=\"fas fa-arrow-right\" style=\"font-size:11px;\"><\/i><\/a>\n      <\/div>\n      <div id=\"recentTransactions\"><\/div>\n    <\/div>\n  <\/section>\n\n  <!-- TRANSACCIONES -->\n  <section id=\"page-transacciones\" class=\"fade-in\" style=\"display:none;\">\n    <div style=\"display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:16px;margin-bottom:28px;\">\n      <h1 style=\"font-size:28px;font-weight:700;\">Transacciones<\/h1>\n      <button class=\"btn btn-primary\" onclick=\"openModal()\">\n        <i class=\"fas fa-plus\"><\/i> Nueva transaccion\n      <\/button>\n    <\/div>\n\n    <!-- Filtros -->\n    <div class=\"card\" style=\"margin-bottom:20px;padding:16px 20px;\">\n      <div style=\"display:flex;gap:12px;flex-wrap:wrap;align-items:center;\">\n        <div style=\"display:flex;gap:4px;\">\n          <button class=\"tab-btn active\" data-filter=\"all\" onclick=\"filterTxn('all',this)\">Todas<\/button>\n          <button class=\"tab-btn\" data-filter=\"ingreso\" onclick=\"filterTxn('ingreso',this)\">Ingresos<\/button>\n          <button class=\"tab-btn\" data-filter=\"gasto\" onclick=\"filterTxn('gasto',this)\">Gastos<\/button>\n        <\/div>\n        <input type=\"text\" class=\"input-field\" placeholder=\"Buscar...\" style=\"max-width:200px;\" id=\"searchInput\" oninput=\"renderTransactions()\">\n        <input type=\"month\" class=\"input-field\" style=\"max-width:180px;\" id=\"filterMonth\" onchange=\"renderTransactions()\">\n        <select class=\"input-field\" style=\"max-width:160px;\" id=\"filterCat\" onchange=\"renderTransactions()\">\n          <option value=\"\">Todas las categorias<\/option>\n        <\/select>\n      <\/div>\n    <\/div>\n\n    <div class=\"card\" style=\"padding:8px;\">\n      <!-- Header -->\n      <div class=\"txn-row\" style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;\">\n        <span><\/span><span>Descripcion<\/span><span>Categoria<\/span><span class=\"txn-date\">Fecha<\/span><span style=\"text-align:right;\">Monto<\/span>\n      <\/div>\n      <div id=\"transactionsList\"><\/div>\n    <\/div>\n  <\/section>\n\n  <!-- PRESUPUESTO -->\n  <section id=\"page-presupuesto\" class=\"fade-in\" style=\"display:none;\">\n    <div style=\"display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:16px;margin-bottom:28px;\">\n      <div>\n        <h1 style=\"font-size:28px;font-weight:700;\">Presupuesto mensual<\/h1>\n        <p style=\"color:var(--muted);font-size:14px;margin-top:4px;\">Define cuanto quieres gastar en cada categoria<\/p>\n      <\/div>\n      <div style=\"display:flex;gap:8px;align-items:center;\">\n        <input type=\"month\" class=\"input-field\" style=\"width:180px;\" id=\"budgetMonth\" onchange=\"renderBudget()\">\n        <button class=\"btn btn-primary\" onclick=\"openBudgetModal()\">\n          <i class=\"fas fa-plus\"><\/i> Asignar\n        <\/button>\n      <\/div>\n    <\/div>\n\n    <!-- Resumen presupuesto -->\n    <div style=\"display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:24px;\">\n      <div class=\"card\" style=\"text-align:center;\">\n        <p style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;\">Presupuesto total<\/p>\n        <p style=\"font-size:24px;font-weight:700;margin-top:6px;font-family:'Space Grotesk';\" id=\"budgetTotal\">$0<\/p>\n      <\/div>\n      <div class=\"card\" style=\"text-align:center;\">\n        <p style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;\">Gastado<\/p>\n        <p style=\"font-size:24px;font-weight:700;margin-top:6px;font-family:'Space Grotesk';color:var(--danger);\" id=\"budgetSpent\">$0<\/p>\n      <\/div>\n      <div class=\"card\" style=\"text-align:center;\">\n        <p style=\"color:var(--muted);font-size:12px;font-weight:600;text-transform:uppercase;\">Disponible<\/p>\n        <p style=\"font-size:24px;font-weight:700;margin-top:6px;font-family:'Space Grotesk';color:var(--accent);\" id=\"budgetAvailable\">$0<\/p>\n      <\/div>\n    <\/div>\n\n    <div class=\"card\">\n      <h3 style=\"font-size:16px;font-weight:600;margin-bottom:20px;\">Categorias<\/h3>\n      <div id=\"budgetList\"><\/div>\n    <\/div>\n\n    <!-- Gr\u00e1fico de presupuesto -->\n    <div class=\"card\" style=\"margin-top:16px;\">\n      <h3 style=\"font-size:16px;font-weight:600;margin-bottom:16px;\">Distribucion del presupuesto<\/h3>\n      <div style=\"position:relative;height:300px;\">\n        <canvas id=\"chartBudget\"><\/canvas>\n      <\/div>\n    <\/div>\n  <\/section>\n\n  <!-- REPORTES -->\n  <section id=\"page-reportes\" class=\"fade-in\" style=\"display:none;\">\n    <h1 style=\"font-size:28px;font-weight:700;margin-bottom:28px;\">Reportes<\/h1>\n\n    <div style=\"display:flex;gap:12px;margin-bottom:20px;flex-wrap:wrap;\">\n      <select class=\"input-field\" style=\"max-width:160px;\" id=\"reportPeriod\" onchange=\"renderReports()\">\n        <option value=\"6\">Ultimos 6 meses<\/option>\n        <option value=\"12\">Ultimo a\u00f1o<\/option>\n      <\/select>\n    <\/div>\n\n    <div style=\"display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:20px;\">\n      <div class=\"card\">\n        <h3 style=\"font-size:16px;font-weight:600;margin-bottom:16px;\">Tendencia de ahorro<\/h3>\n        <div style=\"position:relative;height:280px;\">\n          <canvas id=\"chartSavings\"><\/canvas>\n        <\/div>\n      <\/div>\n      <div class=\"card\">\n        <h3 style=\"font-size:16px;font-weight:600;margin-bottom:16px;\">Top categorias de gasto<\/h3>\n        <div style=\"position:relative;height:280px;\">\n          <canvas id=\"chartTopCats\"><\/canvas>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- Tabla resumen -->\n    <div class=\"card\">\n      <h3 style=\"font-size:16px;font-weight:600;margin-bottom:16px;\">Resumen mensual<\/h3>\n      <div style=\"overflow-x:auto;\">\n        <table style=\"width:100%;border-collapse:collapse;font-size:14px;\">\n          <thead>\n            <tr style=\"border-bottom:1px solid var(--border);color:var(--muted);font-size:12px;text-transform:uppercase;\">\n              <th style=\"padding:10px 12px;text-align:left;\">Mes<\/th>\n              <th style=\"padding:10px 12px;text-align:right;\">Ingresos<\/th>\n              <th style=\"padding:10px 12px;text-align:right;\">Gastos<\/th>\n              <th style=\"padding:10px 12px;text-align:right;\">Balance<\/th>\n              <th style=\"padding:10px 12px;text-align:right;\">Ahorro<\/th>\n            <\/tr>\n          <\/thead>\n          <tbody id=\"reportTable\"><\/tbody>\n        <\/table>\n      <\/div>\n    <\/div>\n  <\/section>\n\n<\/main>\n\n<!-- Modal Nueva Transacci\u00f3n -->\n<div class=\"modal-overlay\" id=\"txnModal\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Nueva transacci\u00f3n\">\n  <div class=\"modal-box\">\n    <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:24px;\">\n      <h2 style=\"font-size:20px;font-weight:700;\" id=\"modalTitle\">Nueva transaccion<\/h2>\n      <button onclick=\"closeModal()\" style=\"background:none;border:none;color:var(--muted);font-size:18px;cursor:pointer;\" aria-label=\"Cerrar\">\n        <i class=\"fas fa-times\"><\/i>\n      <\/button>\n    <\/div>\n\n    <form id=\"txnForm\" onsubmit=\"saveTransaction(event)\">\n      <input type=\"hidden\" id=\"txnId\">\n\n      <!-- Tipo -->\n      <div style=\"display:flex;gap:8px;margin-bottom:16px;\">\n        <button type=\"button\" class=\"tab-btn active\" id=\"typeIngreso\" onclick=\"setTxnType('ingreso')\" style=\"flex:1;\">\n          <i class=\"fas fa-arrow-down\" style=\"color:var(--info);\"><\/i> Ingreso\n        <\/button>\n        <button type=\"button\" class=\"tab-btn\" id=\"typeGasto\" onclick=\"setTxnType('gasto')\" style=\"flex:1;\">\n          <i class=\"fas fa-arrow-up\" style=\"color:var(--danger);\"><\/i> Gasto\n        <\/button>\n      <\/div>\n\n      <!-- Monto -->\n      <div style=\"margin-bottom:16px;\">\n        <label style=\"font-size:12px;font-weight:600;color:var(--muted);display:block;margin-bottom:6px;\">Monto<\/label>\n        <input type=\"number\" step=\"0.01\" min=\"0.01\" class=\"input-field\" id=\"txnAmount\" placeholder=\"0.00\" required style=\"font-size:20px;font-weight:700;font-family:'Space Grotesk';\">\n      <\/div>\n\n      <!-- Descripci\u00f3n -->\n      <div style=\"margin-bottom:16px;\">\n        <label style=\"font-size:12px;font-weight:600;color:var(--muted);display:block;margin-bottom:6px;\">Descripcion<\/label>\n        <input type=\"text\" class=\"input-field\" id=\"txnDesc\" placeholder=\"Ej: Pago de salario\" required>\n      <\/div>\n\n      <!-- Categor\u00eda -->\n      <div style=\"margin-bottom:16px;\">\n        <label style=\"font-size:12px;font-weight:600;color:var(--muted);display:block;margin-bottom:6px;\">Categoria<\/label>\n        <select class=\"input-field\" id=\"txnCategory\" required><\/select>\n      <\/div>\n\n      <!-- Fecha -->\n      <div style=\"margin-bottom:24px;\">\n        <label style=\"font-size:12px;font-weight:600;color:var(--muted);display:block;margin-bottom:6px;\">Fecha<\/label>\n        <input type=\"date\" class=\"input-field\" id=\"txnDate\" required>\n      <\/div>\n\n      <!-- Notas -->\n      <div style=\"margin-bottom:24px;\">\n        <label style=\"font-size:12px;font-weight:600;color:var(--muted);display:block;margin-bottom:6px;\">Notas (opcional)<\/label>\n        <input type=\"text\" class=\"input-field\" id=\"txnNotes\" placeholder=\"Notas adicionales...\">\n      <\/div>\n\n      <button type=\"submit\" class=\"btn btn-primary\" style=\"width:100%;justify-content:center;padding:12px;\">\n        <i class=\"fas fa-check\"><\/i> <span id=\"modalSubmitText\">Guardar transaccion<\/span>\n      <\/button>\n    <\/form>\n  <\/div>\n<\/div>\n\n<!-- Modal Presupuesto -->\n<div class=\"modal-overlay\" id=\"budgetModal\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Asignar presupuesto\">\n  <div class=\"modal-box\">\n    <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:24px;\">\n      <h2 style=\"font-size:20px;font-weight:700;\">Asignar presupuesto<\/h2>\n      <button onclick=\"closeBudgetModal()\" style=\"background:none;border:none;color:var(--muted);font-size:18px;cursor:pointer;\" aria-label=\"Cerrar\">\n        <i class=\"fas fa-times\"><\/i>\n      <\/button>\n    <\/div>\n    <form onsubmit=\"saveBudget(event)\">\n      <div style=\"margin-bottom:16px;\">\n        <label style=\"font-size:12px;font-weight:600;color:var(--muted);display:block;margin-bottom:6px;\">Categoria<\/label>\n        <select class=\"input-field\" id=\"budgetCat\" required><\/select>\n      <\/div>\n      <div style=\"margin-bottom:24px;\">\n        <label style=\"font-size:12px;font-weight:600;color:var(--muted);display:block;margin-bottom:6px;\">Monto maximo<\/label>\n        <input type=\"number\" step=\"0.01\" min=\"1\" class=\"input-field\" id=\"budgetAmount\" placeholder=\"0.00\" required style=\"font-size:18px;font-weight:700;font-family:'Space Grotesk';\">\n      <\/div>\n      <button type=\"submit\" class=\"btn btn-primary\" style=\"width:100%;justify-content:center;padding:12px;\">\n        <i class=\"fas fa-check\"><\/i> Asignar presupuesto\n      <\/button>\n    <\/form>\n  <\/div>\n<\/div>\n\n<script>\n\/\/ ===== DATOS Y ESTADO =====\nconst CATEGORIAS_GASTO = [\n  { id: 'alimentacion', nombre: 'Alimentacion', icon: 'fa-utensils', color: '#FF6161' },\n  { id: 'transporte', nombre: 'Transporte', icon: 'fa-car', color: '#FFB547' },\n  { id: 'vivienda', nombre: 'Vivienda', icon: 'fa-home', color: '#47B5FF' },\n  { id: 'entretenimiento', nombre: 'Entretenimiento', icon: 'fa-gamepad', color: '#A855F7' },\n  { id: 'salud', nombre: 'Salud', icon: 'fa-heartbeat', color: '#F472B6' },\n  { id: 'educacion', nombre: 'Educacion', icon: 'fa-graduation-cap', color: '#34D399' },\n  { id: 'compras', nombre: 'Compras', icon: 'fa-shopping-bag', color: '#FB923C' },\n  { id: 'servicios', nombre: 'Servicios', icon: 'fa-bolt', color: '#60A5FA' },\n  { id: 'otros_gasto', nombre: 'Otros', icon: 'fa-ellipsis-h', color: '#94A3B8' }\n];\n\nconst CATEGORIAS_INGRESO = [\n  { id: 'salario', nombre: 'Salario', icon: 'fa-briefcase', color: '#00D68F' },\n  { id: 'freelance', nombre: 'Freelance', icon: 'fa-laptop-code', color: '#47B5FF' },\n  { id: 'inversiones', nombre: 'Inversiones', icon: 'fa-chart-line', color: '#FFB547' },\n  { id: 'otros_ingreso', nombre: 'Otros', icon: 'fa-ellipsis-h', color: '#94A3B8' }\n];\n\nconst TODAS_CATS = [...CATEGORIAS_GASTO, ...CATEGORIAS_INGRESO];\n\n\/\/ Estado global\nlet state = {\n  transactions: [],\n  budgets: {}, \/\/ { \"2025-01\": { alimentacion: 500, transporte: 200 } }\n  currentFilter: 'all',\n  currentTxnType: 'ingreso'\n};\n\n\/\/ Cargar datos de localStorage\nfunction loadState() {\n  try {\n    const saved = localStorage.getItem('finanzapp_data');\n    if (saved) {\n      const parsed = JSON.parse(saved);\n      state.transactions = parsed.transactions || [];\n      state.budgets = parsed.budgets || {};\n    }\n  } catch (e) {\n    console.error('Error cargando datos:', e);\n  }\n}\n\nfunction saveState() {\n  localStorage.setItem('finanzapp_data', JSON.stringify({\n    transactions: state.transactions,\n    budgets: state.budgets\n  }));\n}\n\n\/\/ ===== UTILIDADES =====\nfunction fmt(n) {\n  return new Intl.NumberFormat('es-MX', { style:'currency', currency:'MXN', minimumFractionDigits:0, maximumFractionDigits:0 }).format(n);\n}\n\nfunction fmtFull(n) {\n  return new Intl.NumberFormat('es-MX', { style:'currency', currency:'MXN' }).format(n);\n}\n\nfunction getCat(id) {\n  return TODAS_CATS.find(c => c.id === id) || { nombre: 'Desconocido', icon: 'fa-question', color: '#94A3B8' };\n}\n\nfunction getMonthKey(dateStr) {\n  return dateStr.substring(0, 7);\n}\n\nfunction getCurrentMonthKey() {\n  const d = new Date();\n  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\n}\n\nfunction monthLabel(key) {\n  const [y, m] = key.split('-');\n  const meses = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic'];\n  return `${meses[parseInt(m)-1]} ${y}`;\n}\n\nfunction generateId() {\n  return Date.now().toString(36) + Math.random().toString(36).substr(2, 5);\n}\n\n\/\/ ===== TOAST =====\nfunction showToast(msg, type = 'success') {\n  const container = document.getElementById('toastContainer');\n  const toast = document.createElement('div');\n  toast.className = `toast ${type}`;\n  const icons = { success: 'fa-check-circle', error: 'fa-exclamation-circle', info: 'fa-info-circle' };\n  toast.innerHTML = `<i class=\"fas ${icons[type]}\"><\/i> ${msg}`;\n  container.appendChild(toast);\n  setTimeout(() => toast.remove(), 3000);\n}\n\n\/\/ ===== NAVEGACI\u00d3N =====\nfunction navigate(page) {\n  document.querySelectorAll('[id^=\"page-\"]').forEach(p => p.style.display = 'none');\n  document.getElementById(`page-${page}`).style.display = 'block';\n  document.getElementById(`page-${page}`).classList.remove('fade-in');\n  void document.getElementById(`page-${page}`).offsetWidth;\n  document.getElementById(`page-${page}`).classList.add('fade-in');\n\n  document.querySelectorAll('.sidebar-link[data-page]').forEach(l => l.classList.remove('active'));\n  document.querySelector(`.sidebar-link[data-page=\"${page}\"]`)?.classList.add('active');\n\n  \/\/ Cerrar sidebar en m\u00f3vil\n  document.getElementById('sidebar').classList.remove('open');\n\n  \/\/ Renderizar la p\u00e1gina correspondiente\n  if (page === 'dashboard') renderDashboard();\n  if (page === 'transacciones') renderTransactions();\n  if (page === 'presupuesto') renderBudget();\n  if (page === 'reportes') renderReports();\n}\n\nfunction toggleSidebar() {\n  document.getElementById('sidebar').classList.toggle('open');\n}\n\n\/\/ ===== DASHBOARD =====\nlet chartCategory = null, chartMonthly = null;\n\nfunction renderDashboard() {\n  \/\/ Saludo\n  const hour = new Date().getHours();\n  const greet = hour < 12 ? 'Buenos dias' : hour < 18 ? 'Buenas tardes' : 'Buenas noches';\n  document.getElementById('greeting').textContent = greet;\n\n  \/\/ Fecha\n  const opts = { weekday:'long', year:'numeric', month:'long', day:'numeric' };\n  document.getElementById('dateDisplay').textContent = new Date().toLocaleDateString('es-MX', opts);\n\n  const now = new Date();\n  const cm = getCurrentMonthKey();\n  const pm = (() => {\n    const d = new Date(now.getFullYear(), now.getMonth()-1, 1);\n    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\n  })();\n\n  \/\/ Calcular estad\u00edsticas\n  const totalIncome = state.transactions.filter(t => t.type === 'ingreso').reduce((s,t) => s + t.amount, 0);\n  const totalExpense = state.transactions.filter(t => t.type === 'gasto').reduce((s,t) => s + t.amount, 0);\n  const monthIncome = state.transactions.filter(t => t.type === 'ingreso' && getMonthKey(t.date) === cm).reduce((s,t) => s + t.amount, 0);\n  const monthExpense = state.transactions.filter(t => t.type === 'gasto' && getMonthKey(t.date) === cm).reduce((s,t) => s + t.amount, 0);\n  const prevExpense = state.transactions.filter(t => t.type === 'gasto' && getMonthKey(t.date) === pm).reduce((s,t) => s + t.amount, 0);\n\n  const balance = totalIncome - totalExpense;\n\n  \/\/ Presupuesto restante\n  const budgetData = state.budgets[cm] || {};\n  const budgetTotal = Object.values(budgetData).reduce((s,v) => s + v, 0);\n  const budgetLeft = budgetTotal - monthExpense;\n\n  document.getElementById('statBalance').textContent = fmt(balance);\n  document.getElementById('statBalance').style.color = balance >= 0 ? 'var(--accent)' : 'var(--danger)';\n  document.getElementById('statIncome').textContent = fmt(monthIncome);\n  document.getElementById('statExpense').textContent = fmt(monthExpense);\n  document.getElementById('statBudgetLeft').textContent = fmt(Math.max(0, budgetLeft));\n\n  \/\/ Cambio respecto al mes anterior\n  if (prevExpense > 0) {\n    const change = ((monthExpense - prevExpense) \/ prevExpense * 100).toFixed(0);\n    const arrow = change > 0 ? '<i class=\"fas fa-arrow-up\"><\/i>' : '<i class=\"fas fa-arrow-down\"><\/i>';\n    const color = change > 0 ? 'var(--danger)' : 'var(--accent)';\n    document.getElementById('statBalanceChange').innerHTML = `${arrow} ${Math.abs(change)}% vs mes anterior`;\n    document.getElementById('statBalanceChange').style.color = color;\n  } else {\n    document.getElementById('statBalanceChange').textContent = 'Sin datos del mes anterior';\n    document.getElementById('statBalanceChange').style.color = 'var(--muted)';\n  }\n\n  \/\/ Gr\u00e1fico de categor\u00edas (gastos del mes)\n  const catTotals = {};\n  state.transactions.filter(t => t.type === 'gasto' && getMonthKey(t.date) === cm).forEach(t => {\n    catTotals[t.category] = (catTotals[t.category] || 0) + t.amount;\n  });\n\n  const catLabels = Object.keys(catTotals).map(k => getCat(k).nombre);\n  const catValues = Object.values(catTotals);\n  const catColors = Object.keys(catTotals).map(k => getCat(k).color);\n\n  if (chartCategory) chartCategory.destroy();\n  const ctxCat = document.getElementById('chartCategory').getContext('2d');\n  chartCategory = new Chart(ctxCat, {\n    type: 'doughnut',\n    data: {\n      labels: catLabels.length ? catLabels : ['Sin datos'],\n      datasets: [{\n        data: catValues.length ? catValues : [1],\n        backgroundColor: catColors.length ? catColors : ['#252D40'],\n        borderColor: 'transparent',\n        borderWidth: 0,\n        hoverOffset: 8\n      }]\n    },\n    options: {\n      responsive: true,\n      maintainAspectRatio: false,\n      cutout: '65%',\n      plugins: {\n        legend: { position: 'right', labels: { color: '#6C7693', font: { size: 12, family: 'DM Sans' }, padding: 12, usePointStyle: true, pointStyleWidth: 10 } },\n        tooltip: { backgroundColor: '#1A1D27', titleColor: '#E4E7EF', bodyColor: '#E4E7EF', borderColor: '#252D40', borderWidth: 1, cornerRadius: 8, padding: 12, callbacks: { label: ctx => ` ${ctx.label}: ${fmt(ctx.raw)}` } }\n      }\n    }\n  });\n\n  \/\/ Gr\u00e1fico mensual (6 meses)\n  const months = [];\n  for (let i = 5; i >= 0; i--) {\n    const d = new Date(now.getFullYear(), now.getMonth() - i, 1);\n    months.push(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\n  }\n  const mIncome = months.map(m => state.transactions.filter(t => t.type === 'ingreso' && getMonthKey(t.date) === m).reduce((s,t) => s + t.amount, 0));\n  const mExpense = months.map(m => state.transactions.filter(t => t.type === 'gasto' && getMonthKey(t.date) === m).reduce((s,t) => s + t.amount, 0));\n\n  if (chartMonthly) chartMonthly.destroy();\n  const ctxM = document.getElementById('chartMonthly').getContext('2d');\n  chartMonthly = new Chart(ctxM, {\n    type: 'bar',\n    data: {\n      labels: months.map(m => monthLabel(m)),\n      datasets: [\n        { label: 'Ingresos', data: mIncome, backgroundColor: 'rgba(71,181,255,0.7)', borderRadius: 6, barPercentage: 0.6 },\n        { label: 'Gastos', data: mExpense, backgroundColor: 'rgba(255,97,97,0.7)', borderRadius: 6, barPercentage: 0.6 }\n      ]\n    },\n    options: {\n      responsive: true,\n      maintainAspectRatio: false,\n      scales: {\n        x: { grid: { display: false }, ticks: { color: '#6C7693', font: { size: 11, family: 'DM Sans' } } },\n        y: { grid: { color: '#1E2330' }, ticks: { color: '#6C7693', font: { size: 11, family: 'DM Sans' }, callback: v => fmt(v) }, beginAtZero: true }\n      },\n      plugins: {\n        legend: { labels: { color: '#6C7693', font: { size: 12, family: 'DM Sans' }, usePointStyle: true, pointStyleWidth: 10 } },\n        tooltip: { backgroundColor: '#1A1D27', titleColor: '#E4E7EF', bodyColor: '#E4E7EF', borderColor: '#252D40', borderWidth: 1, cornerRadius: 8, padding: 12, callbacks: { label: ctx => ` ${ctx.dataset.label}: ${fmt(ctx.raw)}` } }\n      }\n    }\n  });\n\n  \/\/ \u00daltimas transacciones\n  const recent = [...state.transactions].sort((a,b) => new Date(b.date) - new Date(a.date)).slice(0, 6);\n  const container = document.getElementById('recentTransactions');\n  if (recent.length === 0) {\n    container.innerHTML = `<div style=\"text-align:center;padding:40px 0;color:var(--muted);\">\n      <i class=\"fas fa-receipt\" style=\"font-size:32px;margin-bottom:12px;display:block;opacity:0.3;\"><\/i>\n      No tienes transacciones todavia.<br>Agrega tu primera con el boton de arriba.\n    <\/div>`;\n    return;\n  }\n  container.innerHTML = recent.map(t => {\n    const cat = getCat(t.category);\n    const isIncome = t.type === 'ingreso';\n    return `<div class=\"txn-row\" style=\"cursor:pointer;\" onclick=\"editTransaction('${t.id}')\">\n      <div class=\"txn-icon\" style=\"background:${cat.color}18;color:${cat.color};\">\n        <i class=\"fas ${cat.icon}\"><\/i>\n      <\/div>\n      <div>\n        <p style=\"font-size:14px;font-weight:500;\">${t.description}<\/p>\n        <p style=\"font-size:12px;color:var(--muted);\">${cat.nombre}<\/p>\n      <\/div>\n      <span class=\"txn-date\" style=\"font-size:12px;color:var(--muted);\">${new Date(t.date + 'T12:00:00').toLocaleDateString('es-MX',{day:'numeric',month:'short'})}<\/span>\n      <span style=\"text-align:right;font-weight:700;font-size:14px;font-family:'Space Grotesk';color:${isIncome ? 'var(--info)' : 'var(--danger)'};\">\n        ${isIncome ? '+' : '-'}${fmt(t.amount)}\n      <\/span>\n    <\/div>`;\n  }).join('');\n}\n\n\/\/ ===== TRANSACCIONES =====\nfunction populateCategorySelect(selectId, type) {\n  const sel = document.getElementById(selectId);\n  const cats = type === 'ingreso' ? CATEGORIAS_INGRESO : CATEGORIAS_GASTO;\n  sel.innerHTML = cats.map(c => `<option value=\"${c.id}\">${c.nombre}<\/option>`).join('');\n}\n\nfunction populateFilterCategories() {\n  const sel = document.getElementById('filterCat');\n  sel.innerHTML = `<option value=\"\">Todas las categorias<\/option>` +\n    TODAS_CATS.map(c => `<option value=\"${c.id}\">${c.nombre}<\/option>`).join('');\n}\n\nfunction filterTxn(filter, btn) {\n  state.currentFilter = filter;\n  document.querySelectorAll('[data-filter]').forEach(b => b.classList.remove('active'));\n  btn.classList.add('active');\n  renderTransactions();\n}\n\nfunction renderTransactions() {\n  const search = document.getElementById('searchInput').value.toLowerCase();\n  const month = document.getElementById('filterMonth').value;\n  const cat = document.getElementById('filterCat').value;\n\n  let filtered = [...state.transactions];\n\n  if (state.currentFilter !== 'all') {\n    filtered = filtered.filter(t => t.type === state.currentFilter);\n  }\n  if (search) {\n    filtered = filtered.filter(t => t.description.toLowerCase().includes(search) || getCat(t.category).nombre.toLowerCase().includes(search));\n  }\n  if (month) {\n    filtered = filtered.filter(t => getMonthKey(t.date) === month);\n  }\n  if (cat) {\n    filtered = filtered.filter(t => t.category === cat);\n  }\n\n  filtered.sort((a,b) => new Date(b.date) - new Date(a.date));\n\n  const container = document.getElementById('transactionsList');\n  if (filtered.length === 0) {\n    container.innerHTML = `<div style=\"text-align:center;padding:50px 0;color:var(--muted);\">\n      <i class=\"fas fa-search\" style=\"font-size:28px;margin-bottom:12px;display:block;opacity:0.3;\"><\/i>\n      No se encontraron transacciones\n    <\/div>`;\n    return;\n  }\n\n  container.innerHTML = filtered.map(t => {\n    const cat = getCat(t.category);\n    const isIncome = t.type === 'ingreso';\n    return `<div class=\"txn-row\">\n      <div class=\"txn-icon\" style=\"background:${cat.color}18;color:${cat.color};\">\n        <i class=\"fas ${cat.icon}\"><\/i>\n      <\/div>\n      <div style=\"cursor:pointer;\" onclick=\"editTransaction('${t.id}')\">\n        <p style=\"font-size:14px;font-weight:500;\">${t.description}<\/p>\n        <p style=\"font-size:11px;color:var(--muted);\">${cat.nombre}${t.notes ? ' \u2014 ' + t.notes : ''}<\/p>\n      <\/div>\n      <span style=\"font-size:12px;color:var(--muted);\">${cat.nombre}<\/span>\n      <span class=\"txn-date\" style=\"font-size:12px;color:var(--muted);\">${new Date(t.date + 'T12:00:00').toLocaleDateString('es-MX',{day:'numeric',month:'short',year:'numeric'})}<\/span>\n      <span style=\"text-align:right;font-weight:700;font-size:14px;font-family:'Space Grotesk';color:${isIncome ? 'var(--info)' : 'var(--danger)'};\">\n        ${isIncome ? '+' : '-'}${fmt(t.amount)}\n      <\/span>\n    <\/div>`;\n  }).join('');\n}\n\n\/\/ ===== MODAL TRANSACCI\u00d3N =====\nfunction setTxnType(type) {\n  state.currentTxnType = type;\n  document.getElementById('typeIngreso').classList.toggle('active', type === 'ingreso');\n  document.getElementById('typeGasto').classList.toggle('active', type === 'gasto');\n  populateCategorySelect('txnCategory', type);\n}\n\nfunction openModal(id = null) {\n  const modal = document.getElementById('txnModal');\n  const form = document.getElementById('txnForm');\n\n  if (id) {\n    \/\/ Editar\n    const t = state.transactions.find(tx => tx.id === id);\n    if (!t) return;\n    document.getElementById('modalTitle').textContent = 'Editar transaccion';\n    document.getElementById('modalSubmitText').textContent = 'Actualizar transaccion';\n    document.getElementById('txnId').value = t.id;\n    document.getElementById('txnAmount').value = t.amount;\n    document.getElementById('txnDesc').value = t.description;\n    document.getElementById('txnDate').value = t.date;\n    document.getElementById('txnNotes').value = t.notes || '';\n    setTxnType(t.type);\n    setTimeout(() => document.getElementById('txnCategory').value = t.category, 10);\n  } else {\n    \/\/ Nueva\n    document.getElementById('modalTitle').textContent = 'Nueva transaccion';\n    document.getElementById('modalSubmitText').textContent = 'Guardar transaccion';\n    form.reset();\n    document.getElementById('txnId').value = '';\n    document.getElementById('txnDate').value = new Date().toISOString().split('T')[0];\n    setTxnType('gasto');\n  }\n\n  modal.classList.add('show');\n  setTimeout(() => document.getElementById('txnAmount').focus(), 200);\n}\n\nfunction closeModal() {\n  document.getElementById('txnModal').classList.remove('show');\n}\n\nfunction editTransaction(id) {\n  openModal(id);\n}\n\nfunction saveTransaction(e) {\n  e.preventDefault();\n  const id = document.getElementById('txnId').value;\n  const amount = parseFloat(document.getElementById('txnAmount').value);\n  const description = document.getElementById('txnDesc').value.trim();\n  const category = document.getElementById('txnCategory').value;\n  const date = document.getElementById('txnDate').value;\n  const notes = document.getElementById('txnNotes').value.trim();\n\n  if (!amount || amount <= 0 || !description || !date) {\n    showToast('Completa todos los campos requeridos', 'error');\n    return;\n  }\n\n  if (id) {\n    \/\/ Actualizar\n    const idx = state.transactions.findIndex(t => t.id === id);\n    if (idx >= 0) {\n      state.transactions[idx] = { ...state.transactions[idx], amount, description, category, date, notes, type: state.currentTxnType };\n      showToast('Transaccion actualizada', 'success');\n    }\n  } else {\n    \/\/ Crear\n    state.transactions.push({\n      id: generateId(),\n      type: state.currentTxnType,\n      amount, description, category, date, notes\n    });\n    showToast('Transaccion guardada', 'success');\n  }\n\n  saveState();\n  closeModal();\n  navigate(getCurrentPage());\n}\n\nfunction deleteTransaction(id) {\n  state.transactions = state.transactions.filter(t => t.id !== id);\n  saveState();\n  showToast('Transaccion eliminada', 'info');\n  navigate(getCurrentPage());\n}\n\nfunction getCurrentPage() {\n  const active = document.querySelector('.sidebar-link.active[data-page]');\n  return active ? active.dataset.page : 'dashboard';\n}\n\n\/\/ ===== PRESUPUESTO =====\nlet chartBudget = null;\n\nfunction openBudgetModal() {\n  const sel = document.getElementById('budgetCat');\n  sel.innerHTML = CATEGORIAS_GASTO.map(c => `<option value=\"${c.id}\">${c.nombre}<\/option>`).join('');\n  document.getElementById('budgetAmount').value = '';\n  document.getElementById('budgetModal').classList.add('show');\n  setTimeout(() => document.getElementById('budgetAmount').focus(), 200);\n}\n\nfunction closeBudgetModal() {\n  document.getElementById('budgetModal').classList.remove('show');\n}\n\nfunction saveBudget(e) {\n  e.preventDefault();\n  const cat = document.getElementById('budgetCat').value;\n  const amount = parseFloat(document.getElementById('budgetAmount').value);\n  const month = document.getElementById('budgetMonth').value;\n\n  if (!amount || amount <= 0 || !month) {\n    showToast('Completa todos los campos', 'error');\n    return;\n  }\n\n  if (!state.budgets[month]) state.budgets[month] = {};\n  state.budgets[month][cat] = amount;\n\n  saveState();\n  closeBudgetModal();\n  showToast('Presupuesto asignado', 'success');\n  renderBudget();\n}\n\nfunction deleteBudget(cat, month) {\n  if (state.budgets[month]) {\n    delete state.budgets[month][cat];\n    if (Object.keys(state.budgets[month]).length === 0) delete state.budgets[month];\n  }\n  saveState();\n  showToast('Presupuesto eliminado', 'info');\n  renderBudget();\n}\n\nfunction renderBudget() {\n  const month = document.getElementById('budgetMonth').value;\n  if (!month) return;\n\n  const budgetData = state.budgets[month] || {};\n  const monthExpenses = state.transactions.filter(t => t.type === 'gasto' && getMonthKey(t.date) === month);\n\n  let totalBudget = 0, totalSpent = 0;\n  const items = CATEGORIAS_GASTO.map(cat => {\n    const budget = budgetData[cat.id] || 0;\n    const spent = monthExpenses.filter(t => t.category === cat.id).reduce((s,t) => s + t.amount, 0);\n    totalBudget += budget;\n    totalSpent += spent;\n    const pct = budget > 0 ? Math.min((spent \/ budget) * 100, 100) : 0;\n    const overBudget = spent > budget && budget > 0;\n    const barColor = overBudget ? 'var(--danger)' : pct > 75 ? 'var(--warning)' : cat.color;\n    return { ...cat, budget, spent, pct, overBudget, barColor };\n  }).filter(i => i.budget > 0);\n\n  document.getElementById('budgetTotal').textContent = fmt(totalBudget);\n  document.getElementById('budgetSpent').textContent = fmt(totalSpent);\n  const available = totalBudget - totalSpent;\n  const availEl = document.getElementById('budgetAvailable');\n  availEl.textContent = fmt(available);\n  availEl.style.color = available >= 0 ? 'var(--accent)' : 'var(--danger)';\n\n  const list = document.getElementById('budgetList');\n  if (items.length === 0) {\n    list.innerHTML = `<div style=\"text-align:center;padding:40px 0;color:var(--muted);\">\n      <i class=\"fas fa-bullseye\" style=\"font-size:32px;margin-bottom:12px;display:block;opacity:0.3;\"><\/i>\n      No hay presupuestos asignados para este mes.<br>Usa el boton \"Asignar\" para comenzar.\n    <\/div>`;\n  } else {\n    list.innerHTML = items.map(i => `\n      <div style=\"display:flex;align-items:center;gap:16px;padding:14px 0;border-bottom:1px solid var(--border);\">\n        <div class=\"txn-icon\" style=\"background:${i.color}18;color:${i.color};flex-shrink:0;\">\n          <i class=\"fas ${i.icon}\"><\/i>\n        <\/div>\n        <div style=\"flex:1;min-width:0;\">\n          <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;\">\n            <span style=\"font-size:14px;font-weight:500;\">${i.nombre}<\/span>\n            <div style=\"display:flex;align-items:center;gap:10px;\">\n              <span style=\"font-size:13px;color:var(--muted);\">${fmt(i.spent)} \/ ${fmt(i.budget)}<\/span>\n              <button onclick=\"deleteBudget('${i.id}','${month}')\" style=\"background:none;border:none;color:var(--muted);cursor:pointer;font-size:12px;padding:4px;\" aria-label=\"Eliminar presupuesto\">\n                <i class=\"fas fa-trash-alt\"><\/i>\n              <\/button>\n            <\/div>\n          <\/div>\n          <div class=\"budget-bar-bg\">\n            <div class=\"budget-bar-fill\" style=\"width:${i.pct}%;background:${i.barColor};\"><\/div>\n          <\/div>\n          ${i.overBudget ? `<p style=\"font-size:11px;color:var(--danger);margin-top:4px;\"><i class=\"fas fa-exclamation-triangle\"><\/i> Excedido por ${fmt(i.spent - i.budget)}<\/p>` : ''}\n        <\/div>\n        <span style=\"font-size:18px;font-weight:700;font-family:'Space Grotesk';color:${i.overBudget ? 'var(--danger)' : 'var(--fg)'};min-width:60px;text-align:right;\">\n          ${Math.round(i.pct)}%\n        <\/span>\n      <\/div>\n    `).join('');\n  }\n\n  \/\/ Gr\u00e1fico de presupuesto\n  const labels = items.map(i => i.nombre);\n  const budgets = items.map(i => i.budget);\n  const spents = items.map(i => i.spent);\n  const colors = items.map(i => i.color);\n\n  if (chartBudget) chartBudget.destroy();\n  if (items.length === 0) {\n    const ctx = document.getElementById('chartBudget').getContext('2d');\n    ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);\n    return;\n  }\n\n  const ctxB = document.getElementById('chartBudget').getContext('2d');\n  chartBudget = new Chart(ctxB, {\n    type: 'bar',\n    data: {\n      labels,\n      datasets: [\n        { label: 'Presupuesto', data: budgets, backgroundColor: colors.map(c => c + '44'), borderColor: colors, borderWidth: 1.5, borderRadius: 6, barPercentage: 0.5 },\n        { label: 'Gastado', data: spents, backgroundColor: colors.map(c => c + 'BB'), borderColor: colors, borderWidth: 0, borderRadius: 6, barPercentage: 0.5 }\n      ]\n    },\n    options: {\n      responsive: true,\n      maintainAspectRatio: false,\n      indexAxis: 'y',\n      scales: {\n        x: { grid: { color: '#1E2330' }, ticks: { color: '#6C7693', font: { size: 11, family: 'DM Sans' }, callback: v => fmt(v) }, beginAtZero: true },\n        y: { grid: { display: false }, ticks: { color: '#E4E7EF', font: { size: 12, family: 'DM Sans', weight: 500 } } }\n      },\n      plugins: {\n        legend: { labels: { color: '#6C7693', font: { size: 12, family: 'DM Sans' }, usePointStyle: true, pointStyleWidth: 10 } },\n        tooltip: { backgroundColor: '#1A1D27', titleColor: '#E4E7EF', bodyColor: '#E4E7EF', borderColor: '#252D40', borderWidth: 1, cornerRadius: 8, padding: 12, callbacks: { label: ctx => ` ${ctx.dataset.label}: ${fmt(ctx.raw)}` } }\n      }\n    }\n  });\n}\n\n\/\/ ===== REPORTES =====\nlet chartSavings = null, chartTopCats = null;\n\nfunction renderReports() {\n  const period = parseInt(document.getElementById('reportPeriod').value) || 6;\n  const now = new Date();\n  const months = [];\n  for (let i = period - 1; i >= 0; i--) {\n    const d = new Date(now.getFullYear(), now.getMonth() - i, 1);\n    months.push(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\n  }\n\n  const incomes = months.map(m => state.transactions.filter(t => t.type === 'ingreso' && getMonthKey(t.date) === m).reduce((s,t) => s + t.amount, 0));\n  const expenses = months.map(m => state.transactions.filter(t => t.type === 'gasto' && getMonthKey(t.date) === m).reduce((s,t) => s + t.amount, 0));\n  const savings = incomes.map((inc, i) => inc - expenses[i]);\n\n  \/\/ Gr\u00e1fico de tendencia de ahorro\n  if (chartSavings) chartSavings.destroy();\n  const ctxS = document.getElementById('chartSavings').getContext('2d');\n  chartSavings = new Chart(ctxS, {\n    type: 'line',\n    data: {\n      labels: months.map(m => monthLabel(m)),\n      datasets: [{\n        label: 'Ahorro mensual',\n        data: savings,\n        borderColor: '#00D68F',\n        backgroundColor: 'rgba(0,214,143,0.1)',\n        fill: true,\n        tension: 0.4,\n        pointBackgroundColor: '#00D68F',\n        pointRadius: 5,\n        pointHoverRadius: 7\n      }]\n    },\n    options: {\n      responsive: true,\n      maintainAspectRatio: false,\n      scales: {\n        x: { grid: { display: false }, ticks: { color: '#6C7693', font: { size: 11, family: 'DM Sans' } } },\n        y: { grid: { color: '#1E2330' }, ticks: { color: '#6C7693', font: { size: 11, family: 'DM Sans' }, callback: v => fmt(v) } }\n      },\n      plugins: {\n        legend: { display: false },\n        tooltip: { backgroundColor: '#1A1D27', titleColor: '#E4E7EF', bodyColor: '#E4E7EF', borderColor: '#252D40', borderWidth: 1, cornerRadius: 8, padding: 12, callbacks: { label: ctx => ` Ahorro: ${fmt(ctx.raw)}` } }\n      }\n    }\n  });\n\n  \/\/ Top categor\u00edas (per\u00edodo completo)\n  const catTotals = {};\n  state.transactions.filter(t => t.type === 'gasto' && months.includes(getMonthKey(t.date))).forEach(t => {\n    catTotals[t.category] = (catTotals[t.category] || 0) + t.amount;\n  });\n  const sorted = Object.entries(catTotals).sort((a,b) => b[1] - a[1]).slice(0, 7);\n\n  if (chartTopCats) chartTopCats.destroy();\n  if (sorted.length > 0) {\n    const ctxT = document.getElementById('chartTopCats').getContext('2d');\n    chartTopCats = new Chart(ctxT, {\n      type: 'bar',\n      data: {\n        labels: sorted.map(([k]) => getCat(k).nombre),\n        datasets: [{\n          data: sorted.map(([,v]) => v),\n          backgroundColor: sorted.map(([k]) => getCat(k).color + 'AA'),\n          borderColor: sorted.map(([k]) => getCat(k).color),\n          borderWidth: 1,\n          borderRadius: 8,\n          barPercentage: 0.6\n        }]\n      },\n      options: {\n        responsive: true,\n        maintainAspectRatio: false,\n        indexAxis: 'y',\n        scales: {\n          x: { grid: { color: '#1E2330' }, ticks: { color: '#6C7693', font: { size: 11, family: 'DM Sans' }, callback: v => fmt(v) }, beginAtZero: true },\n          y: { grid: { display: false }, ticks: { color: '#E4E7EF', font: { size: 12, family: 'DM Sans', weight: 500 } } }\n        },\n        plugins: {\n          legend: { display: false },\n          tooltip: { backgroundColor: '#1A1D27', titleColor: '#E4E7EF', bodyColor: '#E4E7EF', borderColor: '#252D40', borderWidth: 1, cornerRadius: 8, padding: 12, callbacks: { label: ctx => ` ${fmt(ctx.raw)}` } }\n        }\n      }\n    });\n  }\n\n  \/\/ Tabla resumen\n  const table = document.getElementById('reportTable');\n  table.innerHTML = months.map((m, i) => {\n    const sav = incomes[i] - expenses[i];\n    const savPct = incomes[i] > 0 ? ((sav \/ incomes[i]) * 100).toFixed(1) : '0.0';\n    return `<tr style=\"border-bottom:1px solid var(--border);\">\n      <td style=\"padding:12px;font-weight:500;\">${monthLabel(m)}<\/td>\n      <td style=\"padding:12px;text-align:right;color:var(--info);\">${fmt(incomes[i])}<\/td>\n      <td style=\"padding:12px;text-align:right;color:var(--danger);\">${fmt(expenses[i])}<\/td>\n      <td style=\"padding:12px;text-align:right;font-weight:600;color:${sav >= 0 ? 'var(--accent)' : 'var(--danger)'};\">${fmt(sav)}<\/td>\n      <td style=\"padding:12px;text-align:right;\">\n        <span style=\"background:${parseFloat(savPct) >= 20 ? 'var(--accent-dim)' : parseFloat(savPct) >= 0 ? 'var(--warning-dim)' : 'var(--danger-dim)'};color:${parseFloat(savPct) >= 20 ? 'var(--accent)' : parseFloat(savPct) >= 0 ? 'var(--warning)' : 'var(--danger)'};padding:4px 10px;border-radius:6px;font-size:12px;font-weight:600;\">\n          ${savPct}%\n        <\/span>\n      <\/td>\n    <\/tr>`;\n  }).join('');\n}\n\n\/\/ ===== EXPORTAR \/ IMPORTAR =====\nfunction exportData() {\n  const data = JSON.stringify({ transactions: state.transactions, budgets: state.budgets }, null, 2);\n  const blob = new Blob([data], { type: 'application\/json' });\n  const url = URL.createObjectURL(blob);\n  const a = document.createElement('a');\n  a.href = url;\n  a.download = `finanzapp_backup_${new Date().toISOString().split('T')[0]}.json`;\n  a.click();\n  URL.revokeObjectURL(url);\n  showToast('Datos exportados correctamente', 'success');\n}\n\nfunction importDataPrompt() {\n  document.getElementById('importFile').click();\n}\n\nfunction importData(e) {\n  const file = e.target.files[0];\n  if (!file) return;\n  const reader = new FileReader();\n  reader.onload = function(ev) {\n    try {\n      const data = JSON.parse(ev.target.result);\n      if (data.transactions && Array.isArray(data.transactions)) {\n        state.transactions = data.transactions;\n        state.budgets = data.budgets || {};\n        saveState();\n        showToast(`Importados ${data.transactions.length} transacciones`, 'success');\n        navigate(getCurrentPage());\n      } else {\n        showToast('Archivo invalido', 'error');\n      }\n    } catch (err) {\n      showToast('Error al leer el archivo', 'error');\n    }\n  };\n  reader.readAsText(file);\n  e.target.value = '';\n}\n\n\/\/ ===== CERRAR MODALES CON ESCAPE =====\ndocument.addEventListener('keydown', e => {\n  if (e.key === 'Escape') {\n    closeModal();\n    closeBudgetModal();\n  }\n});\n\n\/\/ Cerrar modales al hacer clic fuera\ndocument.getElementById('txnModal').addEventListener('click', e => {\n  if (e.target === document.getElementById('txnModal')) closeModal();\n});\ndocument.getElementById('budgetModal').addEventListener('click', e => {\n  if (e.target === document.getElementById('budgetModal')) closeBudgetModal();\n});\n\n\/\/ ===== INICIALIZACI\u00d3N =====\nfunction init() {\n  loadState();\n\n  \/\/ Si no hay datos, crear datos de ejemplo\n  if (state.transactions.length === 0) {\n    seedData();\n  }\n\n  \/\/ Establecer mes actual en los selectores\n  document.getElementById('budgetMonth').value = getCurrentMonthKey();\n\n  \/\/ Poblar filtros de categor\u00edas\n  populateFilterCategories();\n\n  \/\/ Renderizar dashboard\n  renderDashboard();\n}\n\nfunction seedData() {\n  const now = new Date();\n  const cm = getCurrentMonthKey();\n\n  \/\/ Datos de ejemplo para los \u00faltimos 3 meses\n  const sampleData = [\n    \/\/ Mes actual\n    { type:'ingreso', amount:25000, description:'Salario mensual', category:'salario', date:`${cm}-01`, notes:'' },\n    { type:'ingreso', amount:5000, description:'Proyecto freelance web', category:'freelance', date:`${cm}-05`, notes:'' },\n    { type:'gasto', amount:4500, description:'Renta del departamento', category:'vivienda', date:`${cm}-01`, notes:'' },\n    { type:'gasto', amount:2800, description:'Supermercado semanal', category:'alimentacion', date:`${cm}-03`, notes:'Compras en Walmart' },\n    { type:'gasto', amount:1200, description:'Gasolina', category:'transporte', date:`${cm}-04`, notes:'' },\n    { type:'gasto', amount:699, description:'Netflix + Spotify', category:'entretenimiento', date:`${cm}-05`, notes:'Servicios mensuales' },\n    { type:'gasto', amount:800, description:'Consulta dental', category:'salud', date:`${cm}-08`, notes:'' },\n    { type:'gasto', amount:1500, description:'Curso de programacion', category:'educacion', date:`${cm}-10`, notes:'Udemy' },\n    { type:'gasto', amount:350, description:'Uber viajes', category:'transporte', date:`${cm}-12`, notes:'' },\n    { type:'gasto', amount:2100, description:'Restaurantes y cafes', category:'alimentacion', date:`${cm}-15`, notes:'' },\n    { type:'gasto', amount:580, description:'Internet + telefono', category:'servicios', date:`${cm}-10`, notes:'' },\n    { type:'gasto', amount:1200, description:'Zapatillas nuevas', category:'compras', date:`${cm}-18`, notes:'Nike' },\n\n    \/\/ Mes anterior\n    ...(() => {\n      const d = new Date(now.getFullYear(), now.getMonth()-1, 1);\n      const pm = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\n      return [\n        { type:'ingreso', amount:25000, description:'Salario mensual', category:'salario', date:`${pm}-01`, notes:'' },\n        { type:'ingreso', amount:3000, description:'Dividendos inversiones', category:'inversiones', date:`${pm}-15`, notes:'' },\n        { type:'gasto', amount:4500, description:'Renta del departamento', category:'vivienda', date:`${pm}-01`, notes:'' },\n        { type:'gasto', amount:3200, description:'Supermercado', category:'alimentacion', date:`${pm}-05`, notes:'' },\n        { type:'gasto', amount:1500, description:'Gasolina', category:'transporte', date:`${pm}-07`, notes:'' },\n        { type:'gasto', amount:1800, description:'Cena de cumpleanos', category:'entretenimiento', date:`${pm}-12`, notes:'' },\n        { type:'gasto', amount:600, description:'Farmacia', category:'salud', date:`${pm}-09`, notes:'' },\n        { type:'gasto', amount:580, description:'Internet + telefono', category:'servicios', date:`${pm}-10`, notes:'' },\n        { type:'gasto', amount:900, description:'Camisa y pantalon', category:'compras', date:`${pm}-20`, notes:'' },\n      ];\n    })(),\n\n    \/\/ Hace 2 meses\n    ...(() => {\n      const d = new Date(now.getFullYear(), now.getMonth()-2, 1);\n      const pm = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\n      return [\n        { type:'ingreso', amount:25000, description:'Salario mensual', category:'salario', date:`${pm}-01`, notes:'' },\n        { type:'ingreso', amount:8000, description:'Proyecto de diseno', category:'freelance', date:`${pm}-10`, notes:'' },\n        { type:'gasto', amount:4500, description:'Renta del departamento', category:'vivienda', date:`${pm}-01`, notes:'' },\n        { type:'gasto', amount:2600, description:'Supermercado', category:'alimentacion', date:`${pm}-04`, notes:'' },\n        { type:'gasto', amount:1100, description:'Gasolina', category:'transporte', date:`${pm}-06`, notes:'' },\n        { type:'gasto', amount:450, description:'Cine y palomitas', category:'entretenimiento', date:`${pm}-14`, notes:'' },\n        { type:'gasto', amount:2000, description:'Seguro medico', category:'salud', date:`${pm}-15`, notes:'' },\n        { type:'gasto', amount:580, description:'Internet + telefono', category:'servicios', date:`${pm}-10`, notes:'' },\n      ];\n    })(),\n\n    \/\/ Hace 3-5 meses (datos m\u00e1s ligeros)\n    ...(() => {\n      const result = [];\n      for (let i = 3; i <= 5; i++) {\n        const d = new Date(now.getFullYear(), now.getMonth()-i, 1);\n        const mk = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\n        result.push(\n          { type:'ingreso', amount:25000, description:'Salario mensual', category:'salario', date:`${mk}-01`, notes:'' },\n          { type:'gasto', amount:4500, description:'Renta', category:'vivienda', date:`${mk}-01`, notes:'' },\n          { type:'gasto', amount:2500 + Math.random()*1000, description:'Supermercado', category:'alimentacion', date:`${mk}-06`, notes:'' },\n          { type:'gasto', amount:1000 + Math.random()*500, description:'Transporte', category:'transporte', date:`${mk}-08`, notes:'' },\n          { type:'gasto', amount:500 + Math.random()*500, description:'Entretenimiento', category:'entretenimiento', date:`${mk}-12`, notes:'' },\n          { type:'gasto', amount:580, description:'Servicios', category:'servicios', date:`${mk}-10`, notes:'' }\n        );\n      }\n      return result;\n    })()\n  ];\n\n  \/\/ Redondear montos aleatorios\n  state.transactions = sampleData.map(t => ({\n    ...t,\n    amount: Math.round(t.amount),\n    id: generateId()\n  }));\n\n  \/\/ Presupuesto de ejemplo para el mes actual\n  state.budgets[cm] = {\n    alimentacion: 6000,\n    transporte: 2000,\n    vivienda: 4500,\n    entretenimiento: 1500,\n    salud: 1000,\n    educacion: 2000,\n    compras: 2000,\n    servicios: 600,\n    otros_gasto: 500\n  };\n\n  saveState();\n}\n\n\/\/ Arrancar la app\ninit();\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>FinanzApp \u2014 Tu dinero, tu control FinanzApp FinanzApp Dashboard Transacciones Presupuesto Reportes Exportar datos Importar datos Buen dia Nueva transaccion Balance total $0 Ingresos del mes $0 Gastos del mes $0 Presupuesto restante $0 Gastos por categoria Ingresos vs Gastos (6 meses) Ultimas transacciones Ver todas Transacciones Nueva transaccion Todas Ingresos Gastos Todas las categorias [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-36","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=\/wp\/v2\/pages\/36","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=36"}],"version-history":[{"count":2,"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=\/wp\/v2\/pages\/36\/revisions"}],"predecessor-version":[{"id":38,"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=\/wp\/v2\/pages\/36\/revisions\/38"}],"wp:attachment":[{"href":"https:\/\/pruebas.misterquin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=36"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}