⬅ חזרה לתיקייה · הורד קובץ · ערוך

📄 20260704_205726_index.html

C:\TripServer\backups\uploaded_files\MoneyHome_main\20260704_205726_index.html

<!doctype html>
<html lang="he" dir="rtl">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
  <title>ניהול תקציב משפחתי</title>
  <style>
    :root{
      --bg:#f5f7fb; --card:#ffffff; --ink:#172033; --muted:#667085; --line:#e5e7ef;
      --good:#12805c; --bad:#c62828; --warn:#b26a00; --brand:#2947d3; --soft:#eef2ff;
      --radius:18px; --shadow:0 10px 30px rgba(20,30,60,.08);
    }
    *{box-sizing:border-box}
    body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;background:var(--bg);color:var(--ink)}
    header{position:sticky;top:0;z-index:20;background:rgba(245,247,251,.94);backdrop-filter:blur(14px);border-bottom:1px solid var(--line)}
    .top{max-width:1160px;margin:auto;padding:14px 16px;display:flex;gap:10px;align-items:center;justify-content:space-between}
    .brand{font-weight:900;font-size:20px}
    .small{font-size:12px;color:var(--muted)}
    main{max-width:1160px;margin:auto;padding:16px 14px 92px}
    .grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
    .grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
    .card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:16px}
    .stat .label{font-size:13px;color:var(--muted);margin-bottom:6px}
    .stat .value{font-size:24px;font-weight:900;direction:ltr;text-align:right}
    .good{color:var(--good)} .bad{color:var(--bad)} .warn{color:var(--warn)}
    .toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:12px 0}
    button,select,input{font:inherit;border-radius:12px;border:1px solid var(--line);background:#fff;color:var(--ink)}
    button{padding:10px 13px;font-weight:800;cursor:pointer}
    button.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
    button.ghost{background:var(--soft);color:var(--brand);border-color:#dbe3ff}
    button.danger{background:#fff2f2;color:var(--bad);border-color:#ffd0d0}
    button.goodbtn{background:#ecfdf3;color:var(--good);border-color:#b7ebc7}
    input,select{padding:10px 11px;min-width:0}
    .form{display:grid;grid-template-columns:2fr 1fr 1fr 1fr 1fr auto;gap:8px;align-items:end}
    .form.compact{grid-template-columns:2fr 1fr 1fr auto}
    .field label{display:block;font-size:12px;color:var(--muted);margin:0 0 5px}
    .tabs{display:flex;gap:8px;overflow:auto;padding-bottom:2px}
    .tab{white-space:nowrap}
    .tab.active{background:var(--brand);color:white;border-color:var(--brand)}
    table{width:100%;border-collapse:separate;border-spacing:0 8px}
    th{font-size:12px;color:var(--muted);text-align:right;font-weight:800;padding:0 8px}
    td{background:#fff;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:10px 8px;vertical-align:middle}
    tr td:first-child{border-right:1px solid var(--line);border-radius:0 12px 12px 0}
    tr td:last-child{border-left:1px solid var(--line);border-radius:12px 0 0 12px}
    .amount{direction:ltr;text-align:right;font-weight:900}
    .pill{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:999px;padding:5px 9px;background:#fafafa;font-size:12px;color:var(--muted)}
    .pill.good{background:#ecfdf3;border-color:#b7ebc7;color:var(--good)}
    .pill.bad{background:#fff2f2;border-color:#ffd0d0;color:var(--bad)}
    .pill.warn{background:#fff8e1;border-color:#ffe2a8;color:var(--warn)}
    .section-title{display:flex;justify-content:space-between;gap:10px;align-items:center;margin-bottom:8px}
    h2{font-size:18px;margin:0}
    .muted{color:var(--muted)}
    .row-actions{display:flex;gap:6px;justify-content:flex-end}
    .notice{border-radius:14px;padding:12px 14px;background:#fff8e1;border:1px solid #ffe2a8;color:#6b4300;margin:10px 0}
    .hidden{display:none!important}
    .bottom-nav{position:fixed;bottom:0;right:0;left:0;background:rgba(255,255,255,.94);backdrop-filter:blur(12px);border-top:1px solid var(--line);z-index:30}
    .bottom-nav .inner{max-width:1160px;margin:auto;display:grid;grid-template-columns:repeat(5,1fr);gap:6px;padding:8px}
    .bottom-nav button{font-size:12px;padding:9px 4px}
    .progress{height:10px;background:#edf0f7;border-radius:999px;overflow:hidden;margin-top:8px}
    .bar{height:100%;background:var(--good);width:0%}
    .bar.warnbg{background:var(--warn)}
    .bar.badbg{background:var(--bad)}
    .two{display:grid;grid-template-columns:1.05fr .95fr;gap:12px}
    .login{max-width:520px;margin:40px auto}
    canvas{max-width:100%;height:auto}
    .chartWrap{height:280px;display:flex;align-items:center;justify-content:center}
    .print-only{display:none}
    .archive-card{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:center;border:1px solid var(--line);border-radius:14px;padding:12px;margin:8px 0;background:#fff}
    .income-amount-edit,.income-name-edit,
    .limit-amount-edit,.limit-name-edit,.limit-group-edit,
    .temp-name-edit,.temp-card-edit,.temp-amount-edit,.temp-progress-edit input{
      width:100%;max-width:260px;background:#f8fafc;border:1px solid var(--line);border-radius:10px;padding:9px
    }
    .income-amount-edit,.limit-amount-edit,.temp-amount-edit,.temp-progress-edit input{direction:ltr;text-align:right;font-weight:900}
    .income-amount-edit{color:var(--good)}
    .limit-amount-edit,.temp-amount-edit{color:var(--brand)}
    .temp-card-edit{color:#172033}
    .temp-progress-edit{display:flex;align-items:center;gap:6px}
    .temp-progress-edit input{max-width:72px}
    @media(max-width:860px){
      .grid,.grid3{grid-template-columns:repeat(2,1fr)}
      .two{grid-template-columns:1fr}
      .form,.form.compact{grid-template-columns:1fr 1fr}
      .form .wide{grid-column:1/-1}
      table,thead,tbody,tr,td,th{display:block}
      thead{display:none}
      tr{background:#fff;border:1px solid var(--line);border-radius:14px;margin:10px 0;padding:8px}
      td{border:0!important;border-radius:0!important;padding:7px 4px;display:flex;justify-content:space-between;gap:12px}
      td:before{content:attr(data-label);font-size:12px;color:var(--muted);font-weight:800}
      .row-actions{justify-content:stretch}
      .row-actions button{flex:1}
      .stat .value{font-size:21px}
    }
    @media(max-width:520px){
      .grid,.grid3{grid-template-columns:1fr}
      .top{align-items:flex-start;flex-direction:column}
      .bottom-nav .inner{grid-template-columns:repeat(5,1fr)}
    }
    @media print{
      header,.bottom-nav,.tabs,.toolbar button,.no-print,#loginView{display:none!important}
      body{background:white}
      main{padding:0;max-width:none}
      .card{box-shadow:none;border:1px solid #ddd;break-inside:avoid;margin-bottom:10px}
      .hidden{display:none!important}
      .print-only{display:block}
      #appView{display:block!important}
      #tab-dashboard,#tab-limits,#tab-items,#tab-archive{display:block!important}
      #tab-settings{display:none!important}
    }
  
    .mini-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}
    #tab-items .card,#tab-limits .card{font-size:inherit}
    #tab-items input,#tab-items select,#tab-items button,#tab-limits input,#tab-limits select,#tab-limits button{min-height:44px}
    @media(max-width:860px){.mini-grid{grid-template-columns:1fr}.form,.form.compact{grid-template-columns:1fr!important}.field{width:100%}}
</style>
<!-- EITAN_QA: dashboardTempDetails container exists and renderDashboardTempDetails is called from render() -->
</head>
<body>
<header>
  <div class="top">
    <div>
      <div class="brand">ניהול תקציב משפחתי</div>
      <div class="small" id="syncStatus">מצב סנכרון: לא מחובר</div>
    </div>
    <div class="toolbar">
      <select id="periodPicker"></select>
      <button class="ghost" id="pdfBtn">דוח PDF</button>
      <button class="ghost" id="htmlReportBtn">דוח HTML</button>
      <button class="ghost" id="exportBtn">גיבוי JSON</button>
      <button id="signOutBtn" class="hidden">יציאה</button>
    </div>
  </div>
</header>

<main>
  <section id="loginView" class="login card">
    <h2>כניסה</h2>

    <div class="form" style="grid-template-columns:1fr;gap:10px">
      <div class="field">
        <label>מייל</label>
        <input id="emailLoginInput" type="email" autocomplete="username" placeholder="name@gmail.com">
      </div>

      <div class="field">
        <label>סיסמה</label>
        <input id="passwordLoginInput" type="password" autocomplete="current-password" placeholder="סיסמה">
      </div>

      <button class="primary" id="emailLoginBtn">כניסה עם מייל וסיסמה</button>
    </div>

    <div class="toolbar">
      <button class="ghost" id="googleLoginBtn">כניסה עם Google</button>
      <button class="ghost" id="localModeBtn">מצב מקומי לבדיקה</button>
<button class="secondary" id="cleanTestBtn">מצב בדיקה נקי</button>
    </div>

    <p class="small">מומלץ להשתמש בכניסה עם מייל וסיסמה. Google נשאר כאפשרות גיבוי.</p>
  </section>

  <section id="appView" class="hidden">
    <div class="print-only">
      <h1>דוח תקציב חודשי</h1>
      <p id="printPeriod"></p>
    </div>

    <div class="grid">
      <div class="card stat">
        <div class="label">מסגרת הכנסות חודשית</div>
        <div class="value" id="incomeTotal">₪0</div>
      </div>
      <div class="card stat">
        <div class="label">תקרת תקציב</div>
        <div class="value" id="limitTotal">₪0</div>
      </div>
      <div class="card stat">
        <div class="label">הוצאות בפועל</div>
        <div class="value" id="spentTotal">₪0</div>
      </div>
      <div class="card stat">
        <div class="label">יתרה מול הכנסות</div>
        <div class="value" id="balanceTotal">₪0</div>
      </div>
    </div>


    <div class="card" style="margin-top:12px">
      <div class="section-title"><h2>פירוט הכנסות החודש</h2></div>
      <div id="dashboardIncomeBreakdown" class="mini-grid"></div>
    </div>
    <div class="card" style="margin-top:12px">
      <div class="section-title">
        <h2>מצב החודש</h2>
        <span class="pill" id="monthHealth">—</span>
      </div>
      <div class="progress"><div class="bar" id="spendBar"></div></div>
      <p class="small" id="monthHint"></p>
    </div>

    <div class="tabs toolbar no-print">
      <button class="tab active" data-tab="dashboard">דשבורד</button>
      <button class="tab" data-tab="items">הזנת הוצאות / הכנסות</button>
      <button class="tab" data-tab="limits">מסגרות תקציב</button>
      <button class="tab" data-tab="archive">ארכיון</button>
      <button class="tab" data-tab="settings">הגדרות</button>
    </div>

    <section id="tab-dashboard">
      <div class="two">
        <div class="card">
          <div class="section-title"><h2>מצב לפי קטגוריה</h2></div>
          <div id="categorySummary"></div>
        </div>
        <div class="card">
          <div class="section-title"><h2>תרשים עוגה לפי קטגוריות</h2></div>
          <div class="chartWrap"><canvas id="pieChart" width="300" height="260"></canvas></div>
        </div>
      </div>
      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>גרף הוצאות מול מסגרת</h2></div>
        <div class="chartWrap"><canvas id="barChart" width="760" height="260"></canvas></div>
      </div>

      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>פירוט הוצאות זמניות / הלוואות</h2></div>
        <p class="small">פירוט נפרד לכל התחייבות זמנית: שם, מקור חיוב, סכום חודשי, כמה מתוך כמה ויתרה עתידית.</p>
        <div id="dashboardTempDetails" class="grid3" style="margin-top:10px"></div>
      </div>

      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>סיכום לפי מקור חיוב</h2></div>
        <div id="paymentSourceSummary" class="grid3" style="margin-top:10px"></div>
      </div>
    </section>

    <section id="tab-items" class="hidden">
      <div class="card">
        <div class="section-title"><h2>הוספת הוצאה בפועל</h2></div>
        <div class="form">
          <div class="field wide"><label>פירוט</label><input id="nameInput" placeholder="לדוגמה: שופרסל / חשמל / מסעדה"></div>
          <div class="field"><label>סכום ₪</label><input id="amountInput" type="number" inputmode="decimal" placeholder="0"></div>
          <div class="field"><label>קטגוריה</label><select id="categoryInput"></select></div>
          <div class="field"><label>תאריך</label><input id="dateInput" type="date"></div>
          <div class="field"><label>אמצעי</label><select id="methodInput">
<option>בנק</option>
<option>אשראי</option>
<option>ויזה בנק הפועלים — איתן</option>
<option>ויזה חוץ בנקאית — קרין</option>
<option>מזומן</option>
<option>אחר</option>
</select></div>
          <button class="primary" id="addBtn">שמירה</button>
        </div>
      </div>


      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>עדכון הכנסות החודש</h2></div>
        <p class="small">כאן מעדכנים את המשכורות בפועל לאותו חודש ומוסיפים הכנסה חד־פעמית לחודש בלבד.</p>
        <div id="monthlyIncomeQuickEdit" class="mini-grid"></div>
        <div class="form compact" style="margin-top:12px">
          <div class="field wide"><label>שם הכנסה חד־פעמית</label><input id="quickExtraName" placeholder="לדוגמה: בונוס / עבודה נוספת"></div>
          <div class="field"><label>סכום ₪</label><input id="quickExtraAmount" type="number" inputmode="decimal" placeholder="0"></div>
          <div class="field"><label>שייך ל</label><select id="quickExtraOwner"><option>איתן</option><option>קרין</option><option>משפחה</option></select></div>
          <button class="primary" id="quickAddExtraBtn">הוסף הכנסה לחודש</button>
        </div>
      </div>

      <div class="card" style="margin-top:12px">
        <div class="section-title">
          <h2>הוצאות החודש בפועל</h2>
          <span class="pill" id="itemsCount">0 הוצאות</span>
        </div>
        <p class="small">הוצאות קבועות ותשלומים זמניים נכנסים אוטומטית בכל חודש. הוצאות ידניות מתווספות בנוסף.</p>
        <table>
          <thead><tr><th>פירוט</th><th>קטגוריה</th><th>תאריך</th><th>אמצעי</th><th>סכום</th><th></th></tr></thead>
          <tbody id="itemsTable"></tbody>
        </table>
      </div>
    </section>

    <section id="tab-limits" class="hidden">
      <div class="card">
        <div class="section-title"><h2>הכנסות חודשיות בסיס</h2></div>
        <p class="small">אפשר לערוך ישירות את שם ההכנסה או הסכום. שינוי כאן משפיע על בסיס התקציב, עד שתשנה שוב.</p>
        <div class="form compact">
          <div class="field wide"><label>שם הכנסה</label><input id="incomeName" placeholder="לדוגמה: משכורת"></div>
          <div class="field"><label>סכום ₪</label><input id="incomeAmount" type="number" inputmode="decimal" placeholder="0"></div>
          <div class="field"><label>סוג</label><select id="incomeType"><option>משכורת</option><option>הכנסה נוספת</option></select></div>
          <button class="primary" id="addIncomeBtn">הוספה</button>
        </div>
        <table>
          <thead><tr><th>שם</th><th>סוג</th><th>סכום</th><th></th></tr></thead>
          <tbody id="incomeTable"></tbody>
        </table>
      </div>

      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>הכנסה נוספת לחודש הנוכחי בלבד</h2></div>
        <p class="small">כאן מוסיפים בונוס/עבודה נוספת/השלמה חד־פעמית. זה משפיע רק על החודש הנוכחי ולא משנה את הכנסות הבסיס.</p>
        <div class="form compact">
          <div class="field wide"><label>שם הכנסה</label><input id="extraIncomeName" placeholder="לדוגמה: עבודה נוספת איתן / בונוס קרין"></div>
          <div class="field"><label>סכום ₪</label><input id="extraIncomeAmount" type="number" inputmode="decimal" placeholder="0"></div>
          <div class="field"><label>שייך ל</label><select id="extraIncomeOwner"><option>איתן</option><option>קרין</option><option>משפחה</option></select></div>
          <button class="primary" id="addExtraIncomeBtn">הוספה לחודש</button>
        </div>
        <table>
          <thead><tr><th>שם</th><th>שייך ל</th><th>סכום</th><th></th></tr></thead>
          <tbody id="extraIncomeTable"></tbody>
        </table>
      </div>

      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>מסגרות תקציב לפי קטגוריה</h2></div>
        <p class="small">אפשר לערוך ישירות שם, קטגוריה ותקרה. שורת “הוצאות זמניות בכ.א” מחושבת אוטומטית מתוך התשלומים הזמניים.</p>
        <div class="form compact">
          <div class="field wide"><label>שם מסגרת</label><input id="limitName" placeholder="לדוגמה: קניות סופר"></div>
          <div class="field"><label>סכום ₪</label><input id="limitAmount" type="number" inputmode="decimal" placeholder="0"></div>
          <div class="field"><label>קטגוריית על</label><select id="limitGroup"></select></div>
          <button class="primary" id="addמסגרתBtn">הוספה</button>
        </div>
        <table>
          <thead><tr><th>מסגרת</th><th>קטגוריה</th><th>מקור חיוב</th><th>תקרה</th><th>בפועל</th><th>מצב</th><th></th></tr></thead>
          <tbody id="limitsTable"></tbody>
        </table>
      </div>

      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>הוצאות זמניות בכרטיס אשראי</h2></div>
        <p class="small">אפשר לערוך ישירות שם, כרטיס/מקור חיוב, סכום חודשי, תשלום נוכחי וסך תשלומים.</p>
        <div id="tempCards" class="grid3" style="margin:10px 0"></div>
        <div class="form compact">
          <div class="field wide"><label>שם</label><input id="tempName" placeholder="לדוגמה: רובוט שואב"></div>
          <div class="field"><label>תשלום חודשי ₪</label><input id="tempAmount" type="number" inputmode="decimal"></div>
          <div class="field"><label>תשלום נוכחי / סה״כ</label><input id="tempProgress" placeholder="7/10"></div>
          <button class="primary" id="addTempBtn">הוספה</button>
        </div>
        <table>
          <thead><tr><th>שם</th><th>כרטיס / מקור חיוב</th><th>תשלום חודשי</th><th>כמה מתוך כמה</th><th>יתרה עתידית</th><th></th></tr></thead>
          <tbody id="tempTable"></tbody>
        </table>
      </div>
    </section>

    <section id="tab-archive" class="hidden">
      <div class="card">
        <div class="section-title"><h2>סגירת חודש והתחלה חדשה</h2></div>
        <p class="muted">סגירת חודש שומרת Snapshot בארכיון ואז פותחת חודש תקציבי חדש וריק להזנת הוצאות בפועל. ה־מסגרתs וההכנסות נשארים כבסיס לחודש הבא.</p>
        <div class="toolbar">
          <button class="danger" id="closeMonthBtn">סגור חודש והתחל חדש</button>
          <button class="ghost" id="manualResetBtn">איפוס הוצאות החודש בלי ארכיון</button>
        </div>
      </div>
      <div class="card" style="margin-top:12px">
        <div class="section-title"><h2>חודשים קודמים</h2></div>
        <div id="archiveList"></div>
      </div>
    </section>

    <section id="tab-settings" class="hidden">
      <div class="card">
        <div class="section-title"><h2>הגדרות מחזור תקציב</h2></div>
        <div class="form compact">
          <div class="field wide">
            <label>מחזור חודש</label>
            <select id="cycleMode">
              <option value="2">2 לחודש עד 2 לחודש הבא</option>
              <option value="10">10 לחודש עד 10 לחודש הבא</option>
              <option value="15">15 לחודש עד 15 לחודש הבא</option>
              <option value="custom">מותאם אישית</option>
            </select>
          </div>
          <div class="field"><label>יום התחלה מותאם</label><input id="customCycleDay" type="number" min="1" max="28" value="1"></div>
          <div class="field"><label>איפוס אוטומטי</label><select id="autoReset"><option value="false">כבוי</option><option value="true">פעיל</option></select></div>
          <button class="primary" id="saveSettingsBtn">שמירה</button>
        </div>
        <p class="small">איפוס אוטומטי יסגור חודש לפי יום המחזור שבחרת. איפוס ידני תמיד זמין.</p>
      </div>
    </section>
  </section>
</main>

<nav class="bottom-nav hidden no-print" id="bottomNav">
  <div class="inner">
    <button onclick="BudgetApp.showTab('dashboard')">דשבורד</button>
    <button onclick="BudgetApp.showTab('items')">הוצאות / הכנסות</button>
    <button onclick="BudgetApp.showTab('limits')">מסגרות תקציב</button>
    <button onclick="BudgetApp.showTab('archive')">ארכיון</button>
    <button onclick="BudgetApp.showTab('settings')">הגדרות</button>
  </div>
</nav>

<script type="module">
  const firebaseConfig = {
    apiKey: "AIzaSyDJVmdnQw6ZV3sFOc04EMkYob735XATKBM",
    authDomain: "vaction-budget.firebaseapp.com",
    projectId: "vaction-budget",
    storageBucket: "vaction-budget.firebasestorage.app",
    messagingSenderId: "1039853800467",
    appId: "1:1039853800467:web:6ab326be5e04fe8bbc43e6",
    measurementId: "G-35QMXX5D01"
  };

  const allowedEmails = [
    "eitan.fridman@gmail.com",
    "eitan.friedman@gmail.com",
    "Karin.enoshi@gmail.com",
    "karin.enoshi@gmail.com"
  ];

  const APP_ID = "householdBudgetClean2026";
  const HOUSEHOLD_ID = "fridmanCleanFinal";

  const FIXED_LOCKED_LIMITS = new Set([
    "ארנונה",
    "ועד בית",
    "נטפליקס",
    "מנוי פיס",
    "דמי כיס ילדים",
    "איתוראן",
    "iCloud+ 2TB",
    "Apple Music",
    "ChatGPT Plus",
    "סים מצלמת רכב",
    "019 סלולר",
    "סלקום טריפל",
    "חוג עידו",
    "חסכונות חודשיים"
  ,"משכנתא","ביטוח חיים מנורה","ביטוח רפואי פרטי משפחתי — כלל","ביטוח רפואי פרטי מיכאל — מנורה","ביטוח מחלות קשות ילדים — הראל",
    "קו סלולר קרין — סלקום"]);

  let firebaseReady = false, db = null, auth = null, currentUser = null, unsubscribe = null;
  let localMode = false;
  let cleanTestMode = false;

  const defaultState = () => ({
    schemaVersion: 200,
    activePeriod: periodKeyForDate(new Date(), 10),
    expenses: [],
    monthlyExtraIncomes: [],
    incomes: [
      {id: uid(), name:"משכורת איתן", type:"משכורת", amount:14500},
      {id: uid(), name:"משכורת קרין — עיריית תל אביב", type:"משכורת", amount:13500},
      {id: uid(), name:"משכורת קרין — קרן מחקרים", type:"משכורת", amount:8500},
      {id: uid(), name:"ביטוח לאומי ילדים", type:"הכנסה נוספת", amount:276}
    ],
    groups: ["בית ודיור","ביטוחים ובריאות","רכב ותחבורה","ילדים וחינוך","תקשורת ומנויים","פנאי ושונות","מזון וסופר","רזרבה / בלתי צפוי","חסכונות חודשיים","הוצאות זמניות בכ.א"],
    limits: [
      ["משכנתא","בית ודיור",8256,"בנק"],
      ["ועד בית","בית ודיור",800,"בנק"],
      ["ארנונה","בית ודיור",1111,"בנק"],
      ["גז","בית ודיור",300,"אשראי"],
      ["חשמל","בית ודיור",550,"אשראי"],
      ["מים","בית ודיור",400,"אשראי"],
      ["ביטוח מחלות קשות ילדים — הראל","ביטוחים ובריאות",40,"אשראי"],
      ["ביטוח רפואי פרטי משפחתי — כלל","ביטוחים ובריאות",422,"בנק"],
      ["ביטוח רפואי פרטי מיכאל — מנורה","ביטוחים ובריאות",98,"אשראי"],
      ["ביטוח חיים מנורה","ביטוחים ובריאות",320,"אשראי"],
      ["ביטוח מבנה מנורה","ביטוחים ובריאות",122,"אשראי"],
      ["קופת חולים","ביטוחים ובריאות",500,"בנק"],
      ["תרופות","ביטוחים ובריאות",700,"אשראי"],
      ["דלק","רכב ותחבורה",650,"אשראי"],
      ["איתוראן","רכב ותחבורה",52,"אשראי"],
      ["רב־קו ילדים","רכב ותחבורה",100,"אשראי"],
      ["ביטוח רכב חובה — הראל","רכב ותחבורה",126,"אשראי"],
      ["ביטוח מקיף — הראל","רכב ותחבורה",706,"אשראי"],
      ["טיפול שנתי לרכב","רכב ותחבורה",200,"אשראי"],
      ["טסט שנתי","רכב ותחבורה",460,"אשראי"],
      ["חוג עידו","ילדים וחינוך",550,"אשראי"],
      ["הוצאות בית ספר","ילדים וחינוך",200,"אשראי"],
      ["דמי כיס ילדים","ילדים וחינוך",200,"אשראי"],
      ["סלקום טריפל","תקשורת ומנויים",200,"אשראי"],
      ["קו סלולר קרין — סלקום","תקשורת ומנויים",10,"אשראי"],
      ["נטפליקס","תקשורת ומנויים",70,"אשראי"],
      ["019 סלולר","תקשורת ומנויים",71,"אשראי"],
      ["סים מצלמת רכב","תקשורת ומנויים",27,"אשראי"],
      ["Apple Music","תקשורת ומנויים",35,"אשראי"],
      ["iCloud+ 2TB","תקשורת ומנויים",40,"אשראי"],
      ["ChatGPT Plus","תקשורת ומנויים",70,"אשראי"],
      ["מנוי פיס","פנאי ושונות",60,"אשראי"],
      ["תספורות","פנאי ושונות",350,"אשראי"],
      ["קניות סופר","מזון וסופר",4500,"אשראי"],
      ["בלת״מים","רזרבה / בלתי צפוי",1000,"אשראי"],
      ["חסכונות חודשיים","חסכונות חודשיים",8900,"בנק"],
      ["הוצאות זמניות בכ.א","הוצאות זמניות בכ.א",2211.5,"אשראי"]
    ].map(x => ({id: uid(), name:x[0], group:x[1], amount:x[2], source:x[3] || "אשראי"})),
    savingsBreakdown: [
      {name:"רכב", amount:3000},{name:"עידו חתונה", amount:1200},{name:"מיכאל חתונה", amount:200},{name:"מיכאל בר מצווה", amount:1000},{name:"חופשה", amount:3000},{name:"עידו חשבון בנק", amount:100},{name:"מפתח", amount:400}
    ],
    tempPayments: [
      {id: uid(), name:"הלוואת רכב", card:"הוראת קבע / בנק", amount:1694, paid:8, total:36},
      {id: uid(), name:"שואב רובוטי", card:"ויזה בנק הפועלים — איתן", amount:442.5, paid:7, total:10},
      {id: uid(), name:"ביטוח למסך סלון", card:"ויזה חוץ בנקאית — קרין", amount:75, paid:31, total:36}
    ],
    settings: { cycleMode:"10", customCycleDay:1, autoReset:false },
    archive: []
  });

  let state = defaultState();

  const $ = id => document.getElementById(id);
  const money = n => new Intl.NumberFormat("he-IL",{style:"currency",currency:"ILS",maximumFractionDigits: n % 1 ? 1 : 0}).format(Number(n||0));
  function uid(){ return crypto.randomUUID ? crypto.randomUUID() : String(Date.now()) + Math.random().toString(16).slice(2); }
  const today = () => new Date().toISOString().slice(0,10);
  function cycleDay(){ return state.settings.cycleMode === "custom" ? Number(state.settings.customCycleDay||1) : Number(state.settings.cycleMode||10); }
  function periodKeyForDate(date, startDay){
    const d = new Date(date);
    let y = d.getFullYear(), m = d.getMonth();
    if(d.getDate() < startDay) m -= 1;
    const s = new Date(y, m, startDay);
    return s.toISOString().slice(0,10);
  }
  function periodLabel(key){
    const s = new Date(key+"T00:00:00");
    const e = new Date(s.getFullYear(), s.getMonth()+1, s.getDate());
    return `${s.toLocaleDateString("he-IL")} עד ${e.toLocaleDateString("he-IL")}`;
  }
  function setStatus(txt){ $("syncStatus").textContent = "מצב סנכרון: " + txt; }

  async function initFirebase(){
    const appMod = await import("https://www.gstatic.com/firebasejs/10.12.5/firebase-app.js");
    const authMod = await import("https://www.gstatic.com/firebasejs/10.12.5/firebase-auth.js");
    const fsMod = await import("https://www.gstatic.com/firebasejs/10.12.5/firebase-firestore.js");
    window.FirebaseBudget = {appMod, authMod, fsMod};
    const app = appMod.initializeApp(firebaseConfig);
    auth = authMod.getAuth(app);
    db = fsMod.getFirestore(app);
    firebaseReady = true;
    authMod.onAuthStateChanged(auth, user => {
      const allowed = allowedEmails.map(e => e.toLowerCase());

      if(user && allowed.includes((user.email || "").toLowerCase())){
        currentUser = user;
        enterApp(false);
        return;
      }

      if(user){
        const userEmail = user.email || "לא זוהה";
        authMod.signOut(auth);
        alert("המייל שנכנס אינו מורשה לאפליקציה: " + userEmail);
      }
    });
  }

  async function emailLogin(){
    if(!firebaseReady){
      alert("Firebase לא מוכן.");
      return;
    }

    const email = $("emailLoginInput").value.trim();
    const password = $("passwordLoginInput").value;

    if(!email || !password){
      alert("צריך להזין מייל וסיסמה.");
      return;
    }

    const btn = $("emailLoginBtn");
    if(btn){
      btn.disabled = true;
      btn.textContent = "מתחבר...";
    }

    const {authMod} = window.FirebaseBudget;

    try {
      await authMod.signInWithEmailAndPassword(auth, email, password);
    } catch (err) {
      console.error("Email/password login error:", err);
      alert("שגיאת כניסה עם מייל וסיסמה: " + (err.message || err.code || err));
    } finally {
      if(btn){
        btn.disabled = false;
        btn.textContent = "כניסה עם מייל וסיסמה";
      }
    }
  }

  let googleLoginRunning = false;

  async function googleLogin(){
    if(!firebaseReady){
      alert("Firebase לא מוכן.");
      return;
    }

    if(googleLoginRunning){
      return;
    }

    googleLoginRunning = true;
    const btn = $("googleLoginBtn");
    if(btn){
      btn.disabled = true;
      btn.textContent = "מתחבר...";
    }

    const {authMod} = window.FirebaseBudget;
    const provider = new authMod.GoogleAuthProvider();

    const loginTimeout = setTimeout(() => {
      if(googleLoginRunning && btn){
        btn.disabled = false;
        btn.textContent = "כניסה עם Google";
      }
    }, 12000);

    try {
      await authMod.signInWithPopup(auth, provider);
      clearTimeout(loginTimeout);
    } catch (err) {
      clearTimeout(loginTimeout);
      console.error("Google popup login error:", err);
      alert("שגיאת התחברות Google: " + (err.message || err.code || err));
    } finally {
      googleLoginRunning = false;
      if(btn){
        btn.disabled = false;
        btn.textContent = "כניסה עם Google";
      }
    }
  }

  function enterApp(isLocal){
    localMode = isLocal;
    $("loginView").classList.add("hidden");
    $("appView").classList.remove("hidden");
    $("bottomNav").classList.remove("hidden");
    $("signOutBtn").classList.toggle("hidden", isLocal);
    $("dateInput").value = today();
    if(isLocal){ setStatus("מקומי במכשיר"); loadLocal(); afterLoad(); render(); }
    else { setStatus("מחובר לענן"); listenCloud(); }
  }

  
  function enterCleanTestMode(){
    cleanTestMode = true;
    localMode = true;
    state = defaultState();
    state.expenses = [];
    state.monthlyExtraIncomes = [];
    state.archive = [];
    $("loginView").classList.add("hidden");
    $("appView").classList.remove("hidden");
    $("bottomNav").classList.remove("hidden");
    $("signOutBtn").classList.add("hidden");
    setStatus("מצב בדיקה נקי");
    afterLoad();
    render();
  }

function docPath(){ return ["apps", APP_ID, "households", HOUSEHOLD_ID, "main", "state"]; }

  async function listenCloud(){
    const {fsMod} = window.FirebaseBudget;
    const ref = fsMod.doc(db, ...docPath());
    if(unsubscribe) unsubscribe();
    unsubscribe = fsMod.onSnapshot(ref, snap => {
      try {
        if(snap.exists()){
          state = mergeState(defaultState(), snap.data());
        }else{
          save();
        }
        afterLoad();
        render();
      } catch (err) {
        console.error("שגיאת טעינת נתונים:", err);
        alert("שגיאת טעינת נתונים אחרי התחברות: " + (err.message || err));
      }
    }, err => {
      console.error("Firestore error:", err);
      alert("שגיאת Firestore: " + (err.message || err.code || err));
    });
  }


  function normalizeBaseIncomes(){
    state.incomes = asArray(state.incomes, []);

    const hasKarinCity = state.incomes.some(i => String(i.name || "").includes("קרין") && String(i.name || "").includes("עיריית"));
    const hasKarinResearch = state.incomes.some(i => String(i.name || "").includes("קרין") && String(i.name || "").includes("קרן מחקרים"));

    if(!hasKarinCity || !hasKarinResearch){
      state.incomes = state.incomes.filter(i => {
        const name = String(i.name || "");
        return !(name === "משכורת קרין" || (name.includes("משכורת קרין") && !name.includes("עיריית") && !name.includes("קרן מחקרים")));
      });

      if(!hasKarinCity){
        state.incomes.push({id: uid(), name:"משכורת קרין — עיריית תל אביב", type:"משכורת", amount:13500});
      }

      if(!hasKarinResearch){
        state.incomes.push({id: uid(), name:"משכורת קרין — קרן מחקרים", type:"משכורת", amount:8500});
      }
    }

    if(!state.incomes.some(i => String(i.name || "") === "משכורת איתן")){
      state.incomes.unshift({id: uid(), name:"משכורת איתן", type:"משכורת", amount:14500});
    }

    if(!state.incomes.some(i => String(i.name || "").includes("ביטוח לאומי"))){
      state.incomes.push({id: uid(), name:"ביטוח לאומי ילדים", type:"הכנסה נוספת", amount:276});
    }

    const order = [
      "משכורת איתן",
      "משכורת קרין — עיריית תל אביב",
      "משכורת קרין — קרן מחקרים",
      "ביטוח לאומי ילדים"
    ];

    state.incomes.sort((a,b) => {
      const ia = order.indexOf(a.name);
      const ib = order.indexOf(b.name);
      return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib);
    });
  }

  function syncTemporaryמסגרתFromPayments(){
    state.limits = asArray(state.limits, []);
    state.tempPayments = asArray(state.tempPayments, []);

    const total = state.tempPayments.reduce((sum, payment) => sum + Number(payment.amount || 0), 0);
    let limit = state.limits.find(l => l.name === "הוצאות זמניות בכ.א");

    if(!limit){
      state.limits.push({
        id: uid(),
        name: "הוצאות זמניות בכ.א",
        group: "הוצאות זמניות בכ.א",
        amount: total
      });
      return;
    }

    limit.amount = total;
    limit.group = "הוצאות זמניות בכ.א";
  }

  function asArray(value, fallback=[]){
    return Array.isArray(value) ? value : fallback;
  }

  function asObject(value, fallback={}){
    return value && typeof value === "object" && !Array.isArray(value) ? value : fallback;
  }

  function mergeState(base, data){
    data = asObject(data, {});
    const out = {...base, ...data};

    out.incomes = asArray(data.incomes, base.incomes);
    out.groups = asArray(data.groups, base.groups);
    out.limits = asArray(data.limits, base.limits);
    out.expenses = asArray(data.expenses, []);
    out.monthlyExtraIncomes = asArray(data.monthlyExtraIncomes, []);
    out.tempPayments = asArray(data.tempPayments, base.tempPayments);
    out.savingsBreakdown = asArray(data.savingsBreakdown, base.savingsBreakdown);
    out.settings = {...base.settings, ...asObject(data.settings, {})};
    out.archive = asArray(data.archive, []);

    if(!out.activePeriod || typeof out.activePeriod !== "string"){
      out.activePeriod = periodKeyForDate(new Date(), Number(out.settings.cycleMode || 10));
    }

    return out;
  }



  function normalizeMasterBudgetData(){
    state.limits = asArray(state.limits, []);

    const requiredמסגרתs = [
      {name:"משכנתא", group:"בית ודיור", amount:8256, source:"בנק"},
      {name:"ביטוח חיים מנורה", group:"ביטוחים ובריאות", amount:320, source:"אשראי"},
      {name:"ביטוח רפואי פרטי משפחתי — כלל", group:"ביטוחים ובריאות", amount:422, source:"בנק"},
      {name:"ביטוח רפואי פרטי מיכאל — מנורה", group:"ביטוחים ובריאות", amount:98, source:"אשראי"},
      {name:"ביטוח מחלות קשות ילדים — הראל", group:"ביטוחים ובריאות", amount:40, source:"אשראי"},
      {name:"קו סלולר קרין — סלקום", group:"תקשורת ומנויים", amount:10, source:"אשראי"}
    ];

    requiredמסגרתs.forEach(required => {
      let existing = state.limits.find(l => l.name === required.name);
      if(existing){
        existing.group = required.group;
        existing.amount = required.amount;
        existing.source = required.source;
      }else{
        state.limits.push({id: uid(), ...required});
      }
    });
  }


  function normalizePaymentSources(){
    const bankNames = new Set(["משכנתא","ארנונה","ועד בית","קופת חולים","ביטוח רפואי פרטי משפחתי — כלל","חסכונות חודשיים"]);
    state.limits = (state.limits || []).map(limit => ({
      ...limit,
      source: bankNames.has(limit.name) ? "בנק" : (limit.source || "אשראי")
    }));
    state.tempPayments = (state.tempPayments || []).map(payment => {
      if(payment.name === "הלוואת רכב") return {...payment, card:"הוראת קבע / בנק"};
      return payment;
    });
  }

  function afterLoad(){
    normalizeMasterBudgetData();
    normalizePaymentSources();
    normalizeBaseIncomes();
    syncTemporaryמסגרתFromPayments();
    const currentPeriod = periodKeyForDate(new Date(), cycleDay());
    if(!state.activePeriod) state.activePeriod = currentPeriod;
    if(state.settings.autoReset && currentPeriod !== state.activePeriod){
      closeCurrentMonth(true);
      state.activePeriod = currentPeriod;
      save();
    }
    renderPeriodOptions();
  }

  function loadLocal(){
    const data = localStorage.getItem("householdBudget:v2");
    if(data){ try{ state = mergeState(defaultState(), JSON.parse(data)); }catch(e){} }
  }

  async function save(){
    if(localMode || !firebaseReady){
      localStorage.setItem("householdBudget:v2", JSON.stringify(state));
      render();
      return;
    }
    const {fsMod} = window.FirebaseBudget;
    await fsMod.setDoc(fsMod.doc(db, ...docPath()), {
      ...state,
      updatedAt: fsMod.serverTimestamp(),
      updatedBy: currentUser?.email || null
    }, {merge:false});
  }

  function renderPeriodOptions(){
    const sel = $("periodPicker");
    const old = sel.value || state.activePeriod;
    sel.innerHTML = "";
    const periods = new Set([state.activePeriod, periodKeyForDate(new Date(), cycleDay()), ...state.archive.map(a=>a.periodKey)]);
    [...periods].sort().reverse().forEach(p=>{
      const o = document.createElement("option");
      o.value=p; o.textContent = periodLabel(p);
      sel.appendChild(o);
    });
    sel.value = old || state.activePeriod;
  }

  function currentExpenses(){
    return state.expenses.filter(e => (e.periodKey || state.activePeriod) === state.activePeriod);
  }
  function currentExtraIncomes(){
    return (state.monthlyExtraIncomes || []).filter(i => (i.periodKey || state.activePeriod) === state.activePeriod);
  }

  function automaticMonthlyExpenses(){
    const auto = [];

    state.limits.forEach(limit => {
      if(FIXED_LOCKED_LIMITS.has(limit.name)){
        auto.push({
          id: "auto-fixed-" + limit.id,
          periodKey: state.activePeriod,
          name: limit.name,
          amount: Number(limit.amount || 0),
          limitName: limit.name,
          date: state.activePeriod,
          method: "קבוע אוטומטי",
          auto: true
        });
      }
    });

    state.tempPayments.forEach(payment => {
      auto.push({
        id: "auto-temp-" + payment.id,
        periodKey: state.activePeriod,
        name: payment.name,
        amount: Number(payment.amount || 0),
        limitName: "הוצאות זמניות בכ.א",
        date: state.activePeriod,
        method: `תשלום זמני אוטומטי — ${payment.card || "לא הוגדר"}`,
        auto: true
      });
    });

    return auto;
  }

  function allCurrentExpenses(){
    return [...automaticMonthlyExpenses(), ...currentExpenses()];
  }

  function baseIncomeTotal(){ return state.incomes.reduce((s,i)=>s+Number(i.amount||0),0); }
  function extraIncomeTotal(){ return currentExtraIncomes().reduce((s,i)=>s+Number(i.amount||0),0); }
  function incomeTotal(){ return baseIncomeTotal() + extraIncomeTotal(); }
  function limitTotal(){ return state.limits.reduce((s,l)=>s+Number(l.amount||0),0); }
  function spentByמסגרת(limitName){
    return allCurrentExpenses().filter(e=>e.limitName===limitName).reduce((s,e)=>s+Number(e.amount||0),0);
  }
  function spentByGroup(group){
    const names = state.limits.filter(l=>l.group===group).map(l=>l.name);
    return allCurrentExpenses().filter(e=>names.includes(e.limitName)).reduce((s,e)=>s+Number(e.amount||0),0);
  }
  function spentTotal(){ return allCurrentExpenses().reduce((s,e)=>s+Number(e.amount||0),0); }


  function autoSelectPaymentMethod(){
    const selected = $("categoryInput") ? $("categoryInput").value : "";
    const limit = state.limits.find(l=>l.name===selected);
    if(!limit || !$("methodInput")) return;
    $("methodInput").value = limit.source === "בנק" ? "בנק" : "אשראי";
  }


  function addExpense(){
    const name = $("nameInput").value.trim();
    const amount = Number($("amountInput").value);
    if(!name || !amount){ alert("צריך פירוט וסכום."); return; }
    const limitName = $("categoryInput").value;
    state.expenses.push({id:uid(), periodKey: state.activePeriod, name, amount, limitName, date:$("dateInput").value || today(), method:$("methodInput").value});
    $("nameInput").value=""; $("amountInput").value=""; $("dateInput").value=today();
    save();
  }
  function deleteExpense(id){ state.expenses = state.expenses.filter(e=>e.id!==id); save(); }

  function addIncome(){
    const name=$("incomeName").value.trim(), amount=Number($("incomeAmount").value);
    if(!name || !amount){ alert("צריך שם וסכום."); return; }
    state.incomes.push({id:uid(), name, amount, type:$("incomeType").value});
    $("incomeName").value=""; $("incomeAmount").value="";
    save();
  }
  function deleteIncome(id){ state.incomes=state.incomes.filter(i=>i.id!==id); save(); }

  function updateIncomeAmount(id, value){
    const income = state.incomes.find(i => i.id === id);
    if(!income) return;
    const amount = Number(value);
    income.amount = Number.isFinite(amount) ? amount : 0;
    save();
  }

  function updateIncomeName(id, value){
    const income = state.incomes.find(i => i.id === id);
    if(!income) return;
    income.name = String(value || "").trim() || income.name;
    save();
  }

  function addExtraIncome(){
    const name=$("extraIncomeName").value.trim(), amount=Number($("extraIncomeAmount").value);
    if(!name || !amount){ alert("צריך שם וסכום."); return; }
    state.monthlyExtraIncomes = state.monthlyExtraIncomes || [];
    state.monthlyExtraIncomes.push({
      id:uid(),
      periodKey:state.activePeriod,
      name,
      owner:$("extraIncomeOwner").value,
      amount
    });
    $("extraIncomeName").value="";
    $("extraIncomeAmount").value="";
    save();
  }

  function deleteExtraIncome(id){
    state.monthlyExtraIncomes = (state.monthlyExtraIncomes || []).filter(i=>i.id!==id);
    save();
  }

  function addמסגרת(){
    const name=$("limitName").value.trim(), amount=Number($("limitAmount").value);
    if(!name || !amount){ alert("צריך שם וסכום."); return; }
    const group=$("limitGroup").value;
    state.limits.push({id:uid(), name, group, amount});
    $("limitName").value=""; $("limitAmount").value="";
    save();
  }
  function deleteמסגרת(id){
    const l = state.limits.find(x=>x.id===id);
    if(l && state.expenses.some(e=>e.limitName===l.name)){ alert("אי אפשר למחוק מסגרת שכבר יש עליה הוצאות."); return; }
    state.limits=state.limits.filter(l=>l.id!==id); save();
  }

  function updateמסגרתName(id, value){
    const limit = state.limits.find(l => l.id === id);
    if(!limit) return;
    const oldName = limit.name;
    const newName = String(value || "").trim();
    if(!newName) return;
    limit.name = newName;

    state.expenses.forEach(expense => {
      if(expense.limitName === oldName){
        expense.limitName = newName;
      }
    });

    save();
  }

  function updateמסגרתAmount(id, value){
    const limit = state.limits.find(l => l.id === id);
    if(!limit) return;
    const amount = Number(value);
    limit.amount = Number.isFinite(amount) ? amount : 0;
    save();
  }

  function updateמסגרתGroup(id, value){
    const limit = state.limits.find(l => l.id === id);
    if(!limit) return;
    limit.group = String(value || "").trim() || limit.group;
    save();
  }

  function addTemp(){
    const name=$("tempName").value.trim(), amount=Number($("tempAmount").value), prog=$("tempProgress").value.trim();
    const m = prog.match(/(\d+)\s*\/\s*(\d+)/);
    if(!name || !amount || !m){ alert("צריך שם, סכום והתקדמות בפורמט 7/10."); return; }
    state.tempPayments.push({id:uid(), name, amount, paid:Number(m[1]), total:Number(m[2])});
    syncTemporaryמסגרתFromPayments();
    $("tempName").value=""; $("tempAmount").value=""; $("tempProgress").value="";
    save();
  }
  function deleteTemp(id){
    state.tempPayments = state.tempPayments.filter(t=>t.id!==id);
    syncTemporaryמסגרתFromPayments();
    save();
  }

  function updateTempPayment(id, field, value){
    const payment = state.tempPayments.find(t => t.id === id);
    if(!payment) return;

    if(field === "name"){
      payment.name = String(value || "").trim() || payment.name;
    }

    if(field === "card"){
      payment.card = String(value || "").trim() || "לא הוגדר";
    }

    if(field === "amount"){
      const amount = Number(value);
      payment.amount = Number.isFinite(amount) ? amount : 0;
    }

    if(field === "paid"){
      const paid = Number(value);
      payment.paid = Number.isFinite(paid) ? paid : 0;
    }

    if(field === "total"){
      const total = Number(value);
      payment.total = Number.isFinite(total) ? total : 0;
    }

    syncTemporaryמסגרתFromPayments();
    save();
  }

  function saveSettings(){
    state.settings.cycleMode = $("cycleMode").value;
    state.settings.customCycleDay = Math.min(28, Math.max(1, Number($("customCycleDay").value || 1)));
    state.settings.autoReset = $("autoReset").value === "true";
    const newPeriod = periodKeyForDate(new Date(), cycleDay());
    if(confirm("לעדכן את מחזור התקציב הנוכחי לפי ההגדרה החדשה?")){
      state.activePeriod = newPeriod;
    }
    save();
  }

  function closeCurrentMonth(auto=false){
    const snapshot = buildSnapshot();
    state.archive = state.archive.filter(a=>a.periodKey !== snapshot.periodKey);
    state.archive.push(snapshot);
    state.archive.sort((a,b)=>String(b.periodKey).localeCompare(String(a.periodKey)));
    state.expenses = state.expenses.filter(e => e.periodKey !== state.activePeriod);
    state.monthlyExtraIncomes = (state.monthlyExtraIncomes || []).filter(i => i.periodKey !== state.activePeriod);
    state.activePeriod = periodKeyForDate(new Date(new Date(state.activePeriod+"T00:00:00").setMonth(new Date(state.activePeriod+"T00:00:00").getMonth()+1)), cycleDay());
    if(!auto) save();
  }

  function manualReset(){
    if(!confirm("לאפס את הוצאות החודש בלי לשמור ארכיון?")) return;
    state.expenses = state.expenses.filter(e => e.periodKey !== state.activePeriod);
    state.monthlyExtraIncomes = (state.monthlyExtraIncomes || []).filter(i => i.periodKey !== state.activePeriod);
    save();
  }

  function buildSnapshot(){
    const inc=incomeTotal(), lim=limitTotal(), spent=spentTotal();
    const byמסגרת = state.limits.map(l=>({name:l.name, group:l.group, limit:l.amount, spent:spentByמסגרת(l.name), diff:Number(l.amount)-spentByמסגרת(l.name)}));
    return {
      id:uid(), periodKey:state.activePeriod, label:periodLabel(state.activePeriod),
      closedAt:new Date().toISOString(), income:inc, limits:lim, spent, balance:inc-spent, vsמסגרתs:lim-spent,
      byמסגרת, expenses: allCurrentExpenses(), manualExpenses: currentExpenses(), automaticExpenses: automaticMonthlyExpenses(), extraIncomes: currentExtraIncomes()
    };
  }

  function render(){
    renderDashboardIncomeBreakdown();
    renderMonthlyIncomeQuickEdit();
    renderDashboardTempDetails();
    renderPaymentSourceSummary();
    renderPeriodOptions();
    renderOptions();
    autoSelectPaymentMethod();
    const inc=incomeTotal(), lim=limitTotal(), spent=spentTotal(), balance=inc-spent;
    $("printPeriod").textContent = periodLabel(state.activePeriod);
    $("incomeTotal").textContent=money(inc);
    $("limitTotal").textContent=money(lim);
    $("spentTotal").textContent=money(spent);
    $("balanceTotal").textContent=money(balance);
    $("balanceTotal").className="value "+(balance>=0?"good":"bad");
    const pct = lim ? Math.min(140, spent/lim*100) : 0;
    $("spendBar").style.width=Math.min(100,pct)+"%";
    $("spendBar").className="bar "+(pct>100?"badbg":pct>85?"warnbg":"");
    $("monthHealth").textContent = pct>100 ? "חריגה מהתקציב" : pct>85 ? "קרוב לתקרה" : "תקין";
    $("monthHealth").className = "pill "+(pct>100?"bad":pct>85?"warn":"good");
    $("monthHint").textContent = `הכנסות בסיס: ${money(baseIncomeTotal())}. הכנסה נוספת החודש: ${money(extraIncomeTotal())}. נוצלו ${Math.round(pct)}% מתקרת התקציב. יתרה מול מסגרות: ${money(lim-spent)}.`;

    renderSummary(); renderExpenses(); renderמסגרתs(); renderIncomes(); renderExtraIncomes(); renderTemps(); renderArchive(); renderSettings(); drawCharts();
  }

  function renderOptions(){
    const cat=$("categoryInput"); const old=cat.value;
    cat.innerHTML="";
    state.limits.forEach(l=>{ const o=document.createElement("option"); o.value=l.name; o.textContent=`${l.name} — ${money(l.amount)}`; cat.appendChild(o); });
    if(old) cat.value=old;
    const lg=$("limitGroup"); const oldg=lg.value;
    lg.innerHTML="";
    state.groups.forEach(g=>{ const o=document.createElement("option"); o.value=g; o.textContent=g; lg.appendChild(o); });
    if(oldg) lg.value=oldg;
  }

  
  
  function renderPaymentSourceSummary(){
    const box = $("paymentSourceSummary");
    if(!box) return;

    const bankמסגרתNames = new Set(state.limits.filter(l=>l.source==="בנק").map(l=>l.name));
    const creditמסגרתNames = new Set(state.limits.filter(l=>(l.source||"אשראי")==="אשראי").map(l=>l.name));

    const bankTotal = allCurrentExpenses().filter(e => bankמסגרתNames.has(e.limitName) || (e.method||"").includes("בנק")).reduce((s,e)=>s+Number(e.amount||0),0);
    const creditTotal = allCurrentExpenses().filter(e => creditמסגרתNames.has(e.limitName) || (e.method||"").includes("אשראי") || (e.method||"").includes("ויזה")).reduce((s,e)=>s+Number(e.amount||0),0);

    box.innerHTML = `
      <div class="card stat"><div class="label">חיובי בנק</div><div class="value">${money(bankTotal)}</div></div>
      <div class="card stat"><div class="label">חיובי אשראי</div><div class="value">${money(creditTotal)}</div></div>
    `;
  }



  function renderDashboardIncomeBreakdown(){
    const box = $("dashboardIncomeBreakdown");
    if(!box) return;
    box.innerHTML = "";

    state.incomes.forEach(i=>{
      const d=document.createElement("div");
      d.className="card stat";
      d.innerHTML=`<div class="label">${esc(i.name)}</div><div class="value good">${money(i.amount)}</div><div class="small">${esc(i.type || "")}</div>`;
      box.appendChild(d);
    });

    currentExtraIncomes().forEach(i=>{
      const d=document.createElement("div");
      d.className="card stat";
      d.innerHTML=`<div class="label">${esc(i.name)} — ${esc(i.owner || "")}</div><div class="value good">${money(i.amount)}</div><div class="small">הכנסה חד־פעמית לחודש</div>`;
      box.appendChild(d);
    });
  }

  function renderMonthlyIncomeQuickEdit(){
    const box = $("monthlyIncomeQuickEdit");
    if(!box) return;
    box.innerHTML = "";

    state.incomes.forEach(i=>{
      const d=document.createElement("div");
      d.className="card";
      d.innerHTML=`
        <div class="field">
          <label>${esc(i.name)}</label>
          <input class="income-amount-edit" data-quick-income="${i.id}" type="number" inputmode="decimal" value="${Number(i.amount||0)}">
        </div>
        <div class="small">${esc(i.type || "")}</div>`;
      box.appendChild(d);
    });

    box.querySelectorAll("[data-quick-income]").forEach(inp=>{
      inp.onchange=()=>{
        const income = state.incomes.find(i=>i.id===inp.dataset.quickIncome);
        if(income){
          income.amount = Number(inp.value||0);
          save();
        }
      };
    });
  }

  function quickAddExtraIncome(){
    const name = $("quickExtraName").value.trim();
    const amount = Number($("quickExtraAmount").value||0);
    if(!name || !amount){ alert("צריך שם וסכום."); return; }
    state.monthlyExtraIncomes = state.monthlyExtraIncomes || [];
    state.monthlyExtraIncomes.push({
      id: uid(),
      periodKey: state.activePeriod,
      name,
      owner: $("quickExtraOwner").value,
      amount
    });
    $("quickExtraName").value="";
    $("quickExtraAmount").value="";
    save();
  }


function renderDashboardTempDetails(){
    const box = $("dashboardTempDetails");
    if(!box) return;
    box.innerHTML = "";

    state.tempPayments.forEach(t=>{
      const leftCount = Math.max(0, Number(t.total||0)-Number(t.paid||0));
      const leftAmount = leftCount * Number(t.amount||0);

      const row = document.createElement("div");
      row.className = "card";
      row.style.margin = "10px 0";
      row.innerHTML = `
        <div style="display:flex;justify-content:space-between;gap:10px;align-items:center;flex-wrap:wrap">
          <div>
            <div style="font-weight:900;font-size:17px">${esc(t.name)}</div>
            <div class="small">${esc(t.card || "לא הוגדר")}</div>
          </div>

          <div class="amount" style="font-size:20px">${money(t.amount)}</div>
        </div>

        <div style="margin-top:8px">
          <span class="pill good">תשלום ${Number(t.paid||0)} מתוך ${Number(t.total||0)}</span>
          <span class="pill">נותרו ${leftCount} תשלומים</span>
          <span class="pill">יתרה עתידית ${money(leftAmount)}</span>
        </div>
      `;
      box.appendChild(row);
    });
  }


function renderSummary(){
    const box=$("categorySummary"); box.innerHTML="";
    state.limits.forEach(l=>{
      const spent=spentByמסגרת(l.name), diff=Number(l.amount)-spent, pct=l.amount?spent/l.amount*100:0;
      const cls = pct>100?"bad":pct>85?"warn":"good";
      const div=document.createElement("div");
      div.style.margin="0 0 14px";
      div.innerHTML=`
        <div style="display:flex;justify-content:space-between;gap:10px;align-items:center">
          <div><b>${esc(l.name)}</b><div class="small">${esc(l.group)}</div></div>
          <span class="amount ${cls}">${money(spent)} / ${money(l.amount)}</span>
        </div>
        <div class="progress"><div class="bar ${pct>100?'badbg':pct>85?'warnbg':''}" style="width:${Math.min(100,pct)}%"></div></div>
        <div class="small ${cls}">${diff>=0 ? "יתרה: "+money(diff) : "חריגה: "+money(Math.abs(diff))}</div>`;
      box.appendChild(div);
    });
  }

  function renderExpenses(){
    const tbody=$("itemsTable"); tbody.innerHTML="";
    const arr=[...allCurrentExpenses()].sort((a,b)=>String(b.date).localeCompare(String(a.date)));
    const manualCount = currentExpenses().length;
    const autoCount = automaticMonthlyExpenses().length;
    $("itemsCount").textContent=`${arr.length} הוצאות (${autoCount} אוטומטיות, ${manualCount} ידניות)`;

    arr.forEach(e=>{
      const tr=document.createElement("tr");
      const actionCell = e.auto
        ? `<span class="pill good">אוטומטי</span>`
        : `<div class="row-actions"><button class="danger" data-del="${e.id}">מחיקה</button></div>`;

      tr.innerHTML=`
        <td data-label="פירוט">${esc(e.name)}</td>
        <td data-label="קטגוריה">${esc(e.limitName)}</td>
        <td data-label="תאריך">${esc(e.date)}</td>
        <td data-label="אמצעי">${esc(e.method||"")}</td>
        <td data-label="סכום" class="amount bad">${money(e.amount)}</td>
        <td data-label="פעולות">${actionCell}</td>`;
      tbody.appendChild(tr);
    });

    tbody.querySelectorAll("[data-del]").forEach(b=>b.onclick=()=>deleteExpense(b.dataset.del));
  }

  function renderמסגרתs(){
    const tbody=$("limitsTable"); tbody.innerHTML="";
    state.limits.forEach(l=>{
      const spent=spentByמסגרת(l.name), diff=l.amount-spent, pct=l.amount?spent/l.amount*100:0;
      const cls=pct>100?"bad":pct>85?"warn":"good";
      const isTempמסגרת = l.name === "הוצאות זמניות בכ.א";
      const isFixedLocked = FIXED_LOCKED_LIMITS.has(l.name);
      const isLockedמסגרת = isTempמסגרת || isFixedLocked;
      const amountInput = isLockedמסגרת
        ? `<span class="amount">${money(l.amount)}</span><div class="small">${isTempמסגרת ? "מחושב אוטומטית מהתשלומים הזמניים" : "סכום קבוע"}</div>`
        : `<input class="limit-amount-edit" data-limit-amount="${l.id}" type="number" inputmode="decimal" value="${Number(l.amount||0)}">`;

      const tr=document.createElement("tr");
      tr.innerHTML=`
        <td data-label="מסגרת"><input class="limit-name-edit" data-limit-name="${l.id}" value="${esc(l.name)}" ${isLockedמסגרת ? "readonly" : ""}></td>
        <td data-label="קטגוריה"><select class="limit-group-edit" data-limit-group="${l.id}" ${isLockedמסגרת ? "disabled" : ""}>${state.groups.map(g=>`<option value="${esc(g)}" ${g===l.group?"selected":""}>${esc(g)}</option>`).join("")}</select></td>
        <td data-label="מקור חיוב"><span class="pill">${esc(l.source || "אשראי")}</span></td>
        <td data-label="תקרה">${amountInput}</td>
        <td data-label="בפועל" class="amount">${money(spent)}</td>
        <td data-label="מצב" class="${cls}">${diff>=0 ? "יתרה "+money(diff) : "חריגה "+money(Math.abs(diff))}</td>
        <td data-label="פעולות"><div class="row-actions"><button class="danger" data-dellimit="${l.id}" ${isLockedמסגרת ? "disabled" : ""}>מחיקה</button></div></td>`;
      tbody.appendChild(tr);
    });

    tbody.querySelectorAll("[data-dellimit]").forEach(b=>{
      if(!b.disabled) b.onclick=()=>deleteמסגרת(b.dataset.dellimit);
    });

    tbody.querySelectorAll("[data-limit-name]").forEach(inp=>{
      if(!inp.readOnly) inp.onchange=()=>updateמסגרתName(inp.dataset.limitName, inp.value);
    });

    tbody.querySelectorAll("[data-limit-amount]").forEach(inp=>{
      inp.onchange=()=>updateמסגרתAmount(inp.dataset.limitAmount, inp.value);
    });

    tbody.querySelectorAll("[data-limit-group]").forEach(sel=>{
      if(!sel.disabled) sel.onchange=()=>updateמסגרתGroup(sel.dataset.limitGroup, sel.value);
    });
  }

  function renderIncomes(){
    const tbody=$("incomeTable"); tbody.innerHTML="";
    state.incomes.forEach(i=>{
      const tr=document.createElement("tr");
      tr.innerHTML=`
        <td data-label="שם"><input class="income-name-edit" data-income-name="${i.id}" value="${esc(i.name)}"></td>
        <td data-label="סוג">${esc(i.type)}</td>
        <td data-label="סכום"><input class="income-amount-edit" data-income-amount="${i.id}" type="number" inputmode="decimal" value="${Number(i.amount||0)}"></td>
        <td data-label="פעולות"><div class="row-actions"><button class="danger" data-delincome="${i.id}">מחיקה</button></div></td>`;
      tbody.appendChild(tr);
    });
    tbody.querySelectorAll("[data-delincome]").forEach(b=>b.onclick=()=>deleteIncome(b.dataset.delincome));
    tbody.querySelectorAll("[data-income-amount]").forEach(inp=>{
      inp.onchange=()=>updateIncomeAmount(inp.dataset.incomeAmount, inp.value);
    });
    tbody.querySelectorAll("[data-income-name]").forEach(inp=>{
      inp.onchange=()=>updateIncomeName(inp.dataset.incomeName, inp.value);
    });
  }

  function renderExtraIncomes(){
    const tbody=$("extraIncomeTable");
    if(!tbody) return;
    tbody.innerHTML="";
    currentExtraIncomes().forEach(i=>{
      const tr=document.createElement("tr");
      tr.innerHTML=`
        <td data-label="שם">${esc(i.name)}</td>
        <td data-label="שייך ל">${esc(i.owner || "")}</td>
        <td data-label="סכום" class="amount good">${money(i.amount)}</td>
        <td data-label="פעולות"><div class="row-actions"><button class="danger" data-delextraincome="${i.id}">מחיקה</button></div></td>`;
      tbody.appendChild(tr);
    });
    tbody.querySelectorAll("[data-delextraincome]").forEach(b=>b.onclick=()=>deleteExtraIncome(b.dataset.delextraincome));
  }

  function renderTempCards(){
    const box = $("tempCards");
    if(!box) return;
    box.innerHTML = "";
    state.tempPayments.forEach(t=>{
      const leftCount = Math.max(0, Number(t.total||0)-Number(t.paid||0));
      const d = document.createElement("div");
      d.className = "card stat";
      d.innerHTML = `
        <div class="label">${esc(t.name)}</div>
        <div class="value">${money(t.amount)}</div>
        <div class="small">${esc(t.card || "לא הוגדר")}</div>
        <div class="small">תשלום ${Number(t.paid||0)} מתוך ${Number(t.total||0)} · נותר ${leftCount} תשלומים · יתרה ${money(leftCount*Number(t.amount||0))}</div>`;
      box.appendChild(d);
    });
  }

  function renderTemps(){
    renderTempCards();
    const tbody=$("tempTable"); tbody.innerHTML="";
    state.tempPayments.forEach(t=>{
      const left=Math.max(0, Number(t.total)-Number(t.paid));
      const card = t.card || "לא הוגדר";
      const tr=document.createElement("tr");
      tr.innerHTML=`
        <td data-label="שם"><input class="temp-name-edit" data-temp-name="${t.id}" value="${esc(t.name)}"></td>
        <td data-label="כרטיס / מקור חיוב"><input class="temp-card-edit" data-temp-card="${t.id}" value="${esc(card)}"></td>
        <td data-label="תשלום חודשי"><input class="temp-amount-edit" data-temp-amount="${t.id}" type="number" inputmode="decimal" value="${Number(t.amount||0)}"></td>
        <td data-label="כמה מתוך כמה">
          <div class="temp-progress-edit">
            <input data-temp-paid="${t.id}" type="number" inputmode="numeric" value="${Number(t.paid||0)}">
            <span>/</span>
            <input data-temp-total="${t.id}" type="number" inputmode="numeric" value="${Number(t.total||0)}">
          </div>
        </td>
        <td data-label="יתרה עתידית" class="amount">${money(left*Number(t.amount||0))}</td>
        <td data-label="פעולות"><div class="row-actions"><button class="danger" data-deltemp="${t.id}">מחיקה</button></div></td>`;
      tbody.appendChild(tr);
    });

    tbody.querySelectorAll("[data-deltemp]").forEach(b=>b.onclick=()=>deleteTemp(b.dataset.deltemp));
    tbody.querySelectorAll("[data-temp-name]").forEach(inp=>inp.onchange=()=>updateTempPayment(inp.dataset.tempName, "name", inp.value));
    tbody.querySelectorAll("[data-temp-card]").forEach(inp=>inp.onchange=()=>updateTempPayment(inp.dataset.tempCard, "card", inp.value));
    tbody.querySelectorAll("[data-temp-amount]").forEach(inp=>inp.onchange=()=>updateTempPayment(inp.dataset.tempAmount, "amount", inp.value));
    tbody.querySelectorAll("[data-temp-paid]").forEach(inp=>inp.onchange=()=>updateTempPayment(inp.dataset.tempPaid, "paid", inp.value));
    tbody.querySelectorAll("[data-temp-total]").forEach(inp=>inp.onchange=()=>updateTempPayment(inp.dataset.tempTotal, "total", inp.value));
  }

  function renderArchive(){
    const box=$("archiveList"); box.innerHTML="";
    if(!state.archive.length){ box.innerHTML='<p class="muted">אין עדיין חודשים סגורים.</p>'; return; }
    state.archive.forEach(a=>{
      const div=document.createElement("div");
      div.className="archive-card";
      div.innerHTML=`
        <div>
          <b>${esc(a.label)}</b>
          <div class="small">הוצאות: ${money(a.spent)} | יתרה מול הכנסות: ${money(a.balance)} | מול מסגרות: ${money(a.vsמסגרתs)}</div>
        </div>
        <button class="ghost" data-loadarchive="${a.id}">הצג</button>`;
      box.appendChild(div);
    });
    box.querySelectorAll("[data-loadarchive]").forEach(b=>b.onclick=()=>alert("החודש שמור בארכיון. בגרסה הבאה אוסיף מסך צפייה מלא בחודש סגור."));
  }

  function renderSettings(){
    $("cycleMode").value=state.settings.cycleMode;
    $("customCycleDay").value=state.settings.customCycleDay;
    $("autoReset").value=String(!!state.settings.autoReset);
  }

  function drawCharts(){
    drawPie(); drawBar();
  }

  function drawPie(){
    const c=$("pieChart"), ctx=c.getContext("2d"); ctx.clearRect(0,0,c.width,c.height);
    const data=state.groups.map(g=>({g, v:spentByGroup(g)})).filter(x=>x.v>0);
    if(!data.length){ ctx.fillStyle="#667085"; ctx.font="16px Arial"; ctx.fillText("אין עדיין הוצאות",100,130); return; }
    const total=data.reduce((s,x)=>s+x.v,0); let start=-Math.PI/2;
    const colors=["#2947d3","#12805c","#b26a00","#c62828","#6f42c1","#00838f","#ad1457","#455a64","#7cb342","#ef6c00"];
    data.forEach((x,i)=>{
      const ang=x.v/total*Math.PI*2; ctx.beginPath(); ctx.moveTo(130,120); ctx.arc(130,120,100,start,start+ang); ctx.closePath(); ctx.fillStyle=colors[i%colors.length]; ctx.fill(); start+=ang;
    });
    let y=18; ctx.font="12px Arial"; ctx.textAlign="right";
    data.slice(0,8).forEach((x,i)=>{ctx.fillStyle=colors[i%colors.length];ctx.fillRect(285,y-10,10,10);ctx.fillStyle="#172033";ctx.fillText(`${x.g}: ${money(x.v)}`,275,y);y+=18;});
  }

  function drawBar(){
    const c=$("barChart"), ctx=c.getContext("2d"); ctx.clearRect(0,0,c.width,c.height);
    const data=state.limits.map(l=>({name:l.name, limit:l.amount, spent:spentByמסגרת(l.name)})).filter(x=>x.limit>0).slice(0,14);
    if(!data.length){ return; }
    const max=Math.max(...data.map(x=>Math.max(x.limit,x.spent)),1);
    const left=120, top=20, h=14, gap=4;
    ctx.font="11px Arial"; ctx.textAlign="right";
    data.forEach((x,i)=>{
      const y=top+i*(h*2+gap);
      ctx.fillStyle="#172033"; ctx.fillText(x.name,left-8,y+h);
      ctx.fillStyle="#edf0f7"; ctx.fillRect(left,y,560,h);
      ctx.fillStyle=x.spent>x.limit?"#c62828":"#12805c"; ctx.fillRect(left,y,560*(x.spent/max),h);
      ctx.strokeStyle="#2947d3"; const lx=left+560*(x.limit/max); ctx.beginPath(); ctx.moveTo(lx,y-2); ctx.lineTo(lx,y+h+2); ctx.stroke();
      ctx.fillStyle="#667085"; ctx.textAlign="left"; ctx.fillText(`${money(x.spent)} / ${money(x.limit)}`, left+570, y+h); ctx.textAlign="right";
    });
  }

  function downloadTextFile(filename, content, mime){
    const blob = new Blob([content], {type:mime});
    const a=document.createElement("a");
    a.href=URL.createObjectURL(blob);
    a.download=filename;
    document.body.appendChild(a);
    a.click();
    setTimeout(()=>{
      URL.revokeObjectURL(a.href);
      a.remove();
    }, 1000);
  }

  function buildReportHtml(){
    const inc=incomeTotal(), lim=limitTotal(), spent=spentTotal(), balance=inc-spent;
    const now = new Date().toLocaleString("he-IL");
    const limitRows = state.limits.map(l=>{
      const spentמסגרת = spentByמסגרת(l.name);
      const diff = Number(l.amount||0) - spentמסגרת;
      const status = diff >= 0 ? `יתרה ${money(diff)}` : `חריגה ${money(Math.abs(diff))}`;
      const cls = diff >= 0 ? "good" : "bad";
      return `<tr><td>${esc(l.name)}</td><td>${esc(l.group)}</td><td>${esc(l.source || "אשראי")}</td><td>${money(l.amount)}</td><td>${money(spentמסגרת)}</td><td class="${cls}">${status}</td></tr>`;
    }).join("");

    const incomeRows = state.incomes.map(i=>`<tr><td>${esc(i.name)}</td><td>${esc(i.type)}</td><td>${money(i.amount)}</td></tr>`).join("");
    const extraRows = currentExtraIncomes().map(i=>`<tr><td>${esc(i.name)}</td><td>${esc(i.owner || "")}</td><td>${money(i.amount)}</td></tr>`).join("") || `<tr><td colspan="3">אין הכנסות נוספות החודש</td></tr>`;
    const expenseRows = allCurrentExpenses().map(e=>`<tr><td>${esc(e.date)}</td><td>${esc(e.name)}</td><td>${esc(e.limitName)}</td><td>${esc(e.method || "")}${e.auto ? " — אוטומטי" : ""}</td><td>${money(e.amount)}</td></tr>`).join("") || `<tr><td colspan="5">אין הוצאות בפועל</td></tr>`;
    const tempRows = state.tempPayments.map(t=>{
      const left = Math.max(0, Number(t.total||0)-Number(t.paid||0));
      return `<tr><td>${esc(t.name)}</td><td>${esc(t.card || "לא הוגדר")}</td><td>${money(t.amount)}</td><td>${Number(t.paid||0)}/${Number(t.total||0)}</td><td>${money(left*Number(t.amount||0))}</td></tr>`;
    }).join("");

    return `<!doctype html>
<html lang="he" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>דוח תקציב משפחתי</title>
<style>
body{font-family:Arial,sans-serif;color:#172033;background:#fff;margin:24px;line-height:1.5}
h1,h2{margin:0 0 12px}
h2{margin-top:28px}
.summary{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:18px 0}
.card{border:1px solid #e5e7ef;border-radius:14px;padding:14px;background:#f8fafc}
.label{color:#667085;font-size:13px}
.value{font-size:22px;font-weight:900;direction:ltr;text-align:right}
.good{color:#12805c;font-weight:800}.bad{color:#c62828;font-weight:800}
table{width:100%;border-collapse:collapse;margin-top:10px}
th,td{border:1px solid #e5e7ef;padding:8px;text-align:right}
th{background:#eef2ff}
.note{color:#667085;font-size:13px}
@media print{button{display:none}.summary{grid-template-columns:repeat(4,1fr)}body{margin:12mm}}
@media(max-width:700px){.summary{grid-template-columns:1fr 1fr}body{margin:14px;font-size:14px}}
</style>
</head>
<body>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px">
<button onclick="window.print()" style="padding:13px 20px;border-radius:14px;border:0;background:#2947d3;color:white;font-weight:900;box-shadow:0 6px 18px rgba(41,71,211,.25)">שמירה / הדפסה ל־PDF</button>
<button onclick="if(history.length>1){history.back()}else{window.close()}" style="padding:13px 20px;border-radius:14px;border:1px solid #d0d5dd;background:#fff;color:#172033;font-weight:900">חזרה לאפליקציה</button>
</div>
<h1>דוח תקציב משפחתי</h1>
<div class="note">תקופה: ${periodLabel(state.activePeriod)} | הופק: ${now}</div>
<div class="summary">
<div class="card"><div class="label">הכנסות</div><div class="value">${money(inc)}</div></div>
<div class="card"><div class="label">תקרת תקציב</div><div class="value">${money(lim)}</div></div>
<div class="card"><div class="label">הוצאות בפועל</div><div class="value">${money(spent)}</div></div>
<div class="card"><div class="label">יתרה מול הכנסות</div><div class="value ${balance>=0?'good':'bad'}">${money(balance)}</div></div>
</div>
<h2>הכנסות בסיס</h2>
<table><thead><tr><th>שם</th><th>סוג</th><th>סכום</th></tr></thead><tbody>${incomeRows}</tbody></table>
<h2>הכנסות נוספות לחודש</h2>
<table><thead><tr><th>שם</th><th>שייך ל</th><th>סכום</th></tr></thead><tbody>${extraRows}</tbody></table>
<h2>מסגרות תקציב</h2>
<table><thead><tr><th>שם</th><th>קבוצה</th><th>מקור חיוב</th><th>תקרה</th><th>בפועל</th><th>מצב</th></tr></thead><tbody>${limitRows}</tbody></table>
<h2>הוצאות בפועל</h2>
<table><thead><tr><th>תאריך</th><th>פירוט</th><th>מסגרת</th><th>אמצעי</th><th>סכום</th></tr></thead><tbody>${expenseRows}</tbody></table>
<h2>הוצאות זמניות בכרטיס אשראי</h2>
<table><thead><tr><th>שם</th><th>כרטיס / מקור חיוב</th><th>תשלום חודשי</th><th>כמה מתוך כמה</th><th>יתרה עתידית</th></tr></thead><tbody>${tempRows}</tbody></table>
</body>
</html>`;
  }

  function exportData(){
    const data = JSON.stringify(state, null, 2);
    downloadTextFile(`household-budget-${state.activePeriod}.json`, "\ufeff" + data, "application/json;charset=utf-8");
  }

  function exportHtmlReport(){
    const report = buildReportHtml();
    downloadTextFile(`household-budget-report-${state.activePeriod}.html`, "\ufeff" + report, "text/html;charset=utf-8");
  }

  function printPdf(){
    const report = buildReportHtml();
    const win = window.open("", "_blank");
    if(!win){
      exportHtmlReport();
      alert("הדפדפן חסם פתיחת חלון. הורדתי במקום זה דוח HTML; פתח אותו ובחר שיתוף/הדפסה ל־PDF.");
      return;
    }
    win.document.open();
    win.document.write(report);
    win.document.close();
    setTimeout(()=>{ try{ win.print(); }catch(e){} }, 600);
  }
  function esc(s){ return String(s??"").replace(/[&<>"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m])); }
  function showTab(tab){
    document.querySelectorAll(".tab").forEach(b=>b.classList.toggle("active", b.dataset.tab===tab));
    ["dashboard","items","limits","archive","settings"].forEach(t=>$("tab-"+t).classList.toggle("hidden", t!==tab));
  }

  $("emailLoginBtn").onclick=emailLogin;
  $("googleLoginBtn").onclick=googleLogin;
  $("localModeBtn").onclick=()=>enterApp(true);
  $("cleanTestBtn").onclick=enterCleanTestMode;
  $("addBtn").onclick=addExpense;
  $("categoryInput").onchange=autoSelectPaymentMethod;
  $("addIncomeBtn").onclick=addIncome;
  $("quickAddExtraBtn").onclick=quickAddExtraIncome;
  $("addExtraIncomeBtn").onclick=addExtraIncome;
  $("addמסגרתBtn").onclick=addמסגרת;
  $("addTempBtn").onclick=addTemp;
  $("saveSettingsBtn").onclick=saveSettings;
  $("exportBtn").onclick=exportData;
  $("pdfBtn").onclick=printPdf;
  $("htmlReportBtn").onclick=exportHtmlReport;
  $("closeMonthBtn").onclick=()=>{ if(confirm("לסגור את החודש, לשמור ארכיון ולהתחיל חודש חדש?")) closeCurrentMonth(false); };
  $("manualResetBtn").onclick=manualReset;
  $("periodPicker").onchange=()=>{ state.activePeriod=$("periodPicker").value; render(); };
  $("signOutBtn").onclick=async()=>{ if(unsubscribe) unsubscribe(); const {authMod}=window.FirebaseBudget; await authMod.signOut(auth); location.reload(); };
  document.querySelectorAll(".tab").forEach(b=>b.onclick=()=>showTab(b.dataset.tab));
  window.BudgetApp={showTab};

  initFirebase();
</script>
</body>
</html>