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

📄 index.html

C:\TripServer\Moneyapp\index.html

<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ניהול תקציב חופשה</title>

<meta name="eitan-tripserver-artifact-contract" content="eitan-tripserver-artifact/v1">
<meta name="eitan-artifact-id" content="Moneyapp">
<meta name="eitan-artifact-type" content="vacation-budget-app">
<meta name="eitan-artifact-version" content="2026.07.17-GOLD-AUDIT-PDF-COMPAT-v11">
<script id="eitan-platform-artifact" type="application/json">
{
  "schema": "eitan-tripserver-artifact/v1",
  "artifact_id": "Moneyapp",
  "artifact_type": "vacation-budget-app",
  "version": "2026.07.17-GOLD-AUDIT-PDF-COMPAT-v11",
  "features": [
    "foreign_budget_sources",
    "ido_private_budget",
    "michael_private_budget",
    "expense_budget_owner",
    "visible_version_badge"
  ]
}
</script>

<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-app.js";
import { getAuth, onAuthStateChanged, signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-auth.js";
import { initializeFirestore, persistentLocalCache, persistentMultipleTabManager, collection, addDoc, setDoc, doc, getDocs, getDocFromServer, getDocsFromServer, deleteDoc, onSnapshot, waitForPendingWrites } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-firestore.js";

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 app = initializeApp(firebaseConfig);
const db = initializeFirestore(app, {
  localCache: persistentLocalCache({
    tabManager: persistentMultipleTabManager()
  })
});
const auth = getAuth(app);

const budgetILS = 93063;
const clothingBudgetILS = 20000;
const foodDailyBudgetILS = 750;

const allowedEmails = [
  "eitan.fridman@gmail.com",
  "eitan.friedman@gmail.com",
  "karin.enoshi@gmail.com"
].map(e => e.toLowerCase());

let pendingOfflineWrites = Number(localStorage.getItem("pendingOfflineWrites") || "0");
let appDataStarted = false;
let dataUnsubscribers = [];
let cloudRefreshPromise = null;
let pendingWriteSequence = pendingOfflineWrites;
let acknowledgedWriteSequence = 0;
let pendingWriteAckPromise = null;
let legacyFreezeCompleted = false;
let tripNameSaveTimer = null;
let tripSettingsDirty = false;
let dataSessionId = 0;
let renderFramePending = false;
const BUDGET_APP_VERSION = "2026.07.17-GOLD-AUDIT-PDF-COMPAT-v11";

function persistPendingWriteCount(){
  pendingOfflineWrites = Math.max(0, pendingWriteSequence - acknowledgedWriteSequence);
  localStorage.setItem("pendingOfflineWrites", String(pendingOfflineWrites));
}

function clearDataListeners(){
  dataUnsubscribers.forEach(unsubscribe => {
    try{ unsubscribe(); }catch(err){ console.warn("Listener cleanup skipped", err); }
  });
  dataUnsubscribers = [];
}

function stopBudgetAppData(){
  dataSessionId += 1;
  clearDataListeners();
  appDataStarted = false;
}

function schedulePendingWriteConfirmation(){
  if(!navigator.onLine || pendingWriteAckPromise || !auth.currentUser || !isAllowedUser(auth.currentUser)) return pendingWriteAckPromise;
  const targetSequence = pendingWriteSequence;
  let confirmed = false;
  pendingWriteAckPromise = waitForPendingWrites(db)
    .then(() => {
      confirmed = true;
      acknowledgedWriteSequence = Math.max(acknowledgedWriteSequence, targetSequence);
      persistPendingWriteCount();
      updateConnectionStatus();
    })
    .catch(err => {
      console.warn("Pending writes confirmation postponed", err);
      updateConnectionStatus();
    })
    .finally(() => {
      pendingWriteAckPromise = null;
      if(confirmed && navigator.onLine && pendingWriteSequence > acknowledgedWriteSequence){
        setTimeout(schedulePendingWriteConfirmation, 250);
      }
    });
  return pendingWriteAckPromise;
}

function authMsg(text){
  const el = document.getElementById("authMessage");
  if(el) el.innerText = text || "";
}

function isAllowedUser(user){
  return !!(user && user.email && allowedEmails.includes(user.email.toLowerCase()));
}

window.loginBudgetApp = async function(){
  const email = document.getElementById("authEmail")?.value.trim();
  const password = document.getElementById("authPassword")?.value;
  if(!email || !password){ authMsg("יש להזין אימייל וסיסמה."); return; }
  try{
    authMsg("מתחבר...");
    await signInWithEmailAndPassword(auth, email, password);
  }catch(err){
    authMsg("הכניסה נכשלה. בדוק את כתובת האימייל והסיסמה.");
  }
};

window.createBudgetUser = async function(){
  const email = document.getElementById("authEmail")?.value.trim();
  const password = document.getElementById("authPassword")?.value;
  if(!email || !password){ authMsg("יש להזין אימייל וסיסמה ליצירת משתמש."); return; }
  if(!allowedEmails.includes(email.toLowerCase())){
    authMsg("כתובת האימייל הזו אינה מורשית לשימוש באפליקציה.");
    return;
  }
  try{
    authMsg("יוצר משתמש...");
    await createUserWithEmailAndPassword(auth, email, password);
  }catch(err){
    if(String(err.code || "").includes("email-already-in-use")) authMsg("המשתמש כבר קיים. אפשר ללחוץ כניסה.");
    else if(String(err.code || "").includes("weak-password")) authMsg("הסיסמה חלשה מדי. צריך לפחות 6 תווים.");
    else authMsg("לא ניתן היה ליצור את המשתמש. נסה שוב.");
  }
};

window.logoutBudgetApp = async function(){
  await signOut(auth);
};

function showAppForUser(user){
  document.body.classList.remove("authLocked");
  const overlay = document.getElementById("authOverlay");
  if(overlay) overlay.style.display = "none";
  const topBar = document.getElementById("authTopBar");
  if(topBar) topBar.style.display = "flex";
  const label = document.getElementById("authUserLabel");
  if(label) label.innerText = "מחובר: " + (user.email || "");
  updateConnectionStatus();
  if(typeof initAppTabs === 'function') setTimeout(initAppTabs, 80);
}

function showAuthScreen(message){
  document.body.classList.add("authLocked");
  const overlay = document.getElementById("authOverlay");
  if(overlay) overlay.style.display = "flex";
  const topBar = document.getElementById("authTopBar");
  if(topBar) topBar.style.display = "none";
  authMsg(message || "");
}

function setSyncStatus(mode, text, sub){
  const bar = document.getElementById("syncStatusBar");
  const txt = document.getElementById("syncStatusText");
  const subEl = document.getElementById("syncStatusSub");
  if(!bar || !txt || !subEl) return;
  bar.classList.remove("online","offline","pending");
  bar.classList.add(mode);
  txt.innerText = text;
  subEl.innerText = sub || "";
}

function updateConnectionStatus(){
  if(!navigator.onLine){
    setSyncStatus("offline", "מצב אופליין", "אפשר להזין הוצאות. הן יישמרו במכשיר ויסונכרנו כשהחיבור יחזור.");
    return;
  }
  if(pendingOfflineWrites > 0){
    setSyncStatus("pending", "מסנכרן נתונים...", "יש שינויים שממתינים לאישור מהענן.");
    return;
  }
  setSyncStatus("online", "מחובר ומסונכרן", "כל שינוי נשמר בענן ומסתנכרן בין המכשירים.");
}

window.addEventListener("online", () => {
  updateConnectionStatus();
  schedulePendingWriteConfirmation();
  if(typeof renderAll === "function") setTimeout(renderAll, 100);
});

window.addEventListener("offline", updateConnectionStatus);

let lastCloudRefreshAt = 0;
document.addEventListener("visibilitychange", () => {
  if(!document.hidden && auth.currentUser && isAllowedUser(auth.currentUser)){
    const now = Date.now();
    if(now - lastCloudRefreshAt > 10000){
      lastCloudRefreshAt = now;
      forceFirebaseServerRefresh("חזרה לאפליקציה");
    }
  }
});
window.addEventListener("focus", () => {
  if(auth.currentUser && isAllowedUser(auth.currentUser)){
    const now = Date.now();
    if(now - lastCloudRefreshAt > 10000){
      lastCloudRefreshAt = now;
      forceFirebaseServerRefresh("חזרה לחלון");
    }
  }
});


function markLocalWriteAttempt(){
  pendingWriteSequence += 1;
  persistPendingWriteCount();
  updateConnectionStatus();
}

async function trackedWrite(operation){
  markLocalWriteAttempt();
  try{
    const result = await operation();
    schedulePendingWriteConfirmation();
    return result;
  }catch(err){
    acknowledgedWriteSequence = Math.min(pendingWriteSequence, acknowledgedWriteSequence + 1);
    persistPendingWriteCount();
    updateConnectionStatus();
    throw err;
  }
}

function trackedSetDoc(ref, data, options){
  return trackedWrite(() => setDoc(ref, data, options));
}

function trackedAddDoc(ref, data){
  return trackedWrite(() => addDoc(ref, data));
}

function trackedDeleteDoc(ref){
  return trackedWrite(() => deleteDoc(ref));
}

onAuthStateChanged(auth, async (user) => {
  if(!user){
    stopBudgetAppData();
    showAuthScreen("");
    return;
  }
  if(!isAllowedUser(user)){
    stopBudgetAppData();
    await signOut(auth);
    showAuthScreen("המשתמש הזה אינו מורשה להיכנס לאפליקציה.");
    return;
  }
  showAppForUser(user);
  if(!appDataStarted){
    appDataStarted = true;
    const sessionId = ++dataSessionId;
    startBudgetAppData(sessionId).catch(err => {
      console.error("Budget data startup failed", err);
      if(sessionId === dataSessionId) appDataStarted = false;
      setSyncStatus("pending", "טעינת הנתונים נכשלה", "בדוק חיבור אינטרנט ורענן את האפליקציה.");
    });
  }
});


const tripBudgetDefaults = {
  japan_2026: {
    aliases: ["japan_2026", "יפן 2026", "Japan 2026"],
    dailyFoodBudget: 750,
    totalFoodBudget: 0,
    shoppingBudget: 20000,
    totalBudget: 93063
  },
  usa_2027: {
    aliases: [
      "usa_2027",
      "ארה״ב 2027",
      "ארהב 2027",
      "ארצות הברית 2027",
      "טיול ארה״ב 2027",
      "טיול ארהב 2027",
      "USA 2027",
      "US 2027",
      "United States 2027"
    ],
    dailyFoodBudget: 900,
    totalFoodBudget: 27000,
    shoppingBudget: 30000,
    totalBudget: 246000
  },
  custom: {
    aliases: [],
    dailyFoodBudget: foodDailyBudgetILS,
    totalFoodBudget: 0,
    shoppingBudget: clothingBudgetILS,
    totalBudget: budgetILS
  }
};

function normalizeTripText(v){
  return String(v || "")
    .trim()
    .replace(/"/g, "״")
    .replace(/\s+/g, " ")
    .toLowerCase();
}

function getTripDefaultBudgetProfile(trip = {}){
  const rawKeys = [trip.id, trip.name, trip.tripName, activeTripId].filter(Boolean);
  const keysToCheck = rawKeys.map(normalizeTripText);
  const joined = keysToCheck.join(" ");

  if(joined.includes("usa") || joined.includes("us 2027") || joined.includes("ארהב") || joined.includes("ארה״ב") || joined.includes("ארצות הברית")){
    return tripBudgetDefaults.usa_2027;
  }

  if(joined.includes("japan") || joined.includes("יפן")){
    return tripBudgetDefaults.japan_2026;
  }

  for(const key of Object.keys(tripBudgetDefaults)){
    const cfg = tripBudgetDefaults[key];
    const aliases = [key, ...(cfg.aliases || [])].map(normalizeTripText);
    if(keysToCheck.some(k => aliases.includes(k))){
      return cfg;
    }
  }

  return tripBudgetDefaults.custom;
}

function cleanBudgetNumber(value, fallback = 0){
  const n = Number(value);
  return Number.isFinite(n) && n >= 0 ? n : Number(fallback || 0);
}

function normalizeBudgetSources(trip = getActiveTrip()){
  const defaults = getTripDefaultBudgetProfile(trip);
  const raw = Array.isArray(trip.budgetSources) ? trip.budgetSources : [];
  const sources = raw
    .map((src, index) => ({
      id: String(src?.id || `source_${index}`),
      amount: cleanBudgetNumber(src?.amount, 0),
      currency: ["ILS","USD","JPY","EUR","GBP","CAD"].includes(src?.currency) ? src.currency : "ILS",
      holdingType: src?.holdingType === "cash" ? "cash" : "bank"
    }))
    .filter(src => src.amount > 0);

  if(!sources.length){
    sources.push({id:"legacy_ils", amount:cleanBudgetNumber(trip.budgetILS, defaults.totalBudget), currency:"ILS", holdingType:"bank"});
  }
  return sources;
}

function getTripBudgetSourcesILS(trip = getActiveTrip()){
  return roundILS(normalizeBudgetSources(trip).reduce((sum, src) => sum + convertToILS(src.amount, src.currency, rates), 0));
}

function getTripBudgetConfig(trip = getActiveTrip()){
  const defaults = getTripDefaultBudgetProfile(trip);
  return {
    totalBudget: getTripBudgetSourcesILS(trip),
    budgetSources: normalizeBudgetSources(trip),
    idoPrivateBudget: cleanBudgetNumber(trip.idoPrivateBudgetILS, 0),
    michaelPrivateBudget: cleanBudgetNumber(trip.michaelPrivateBudgetILS, 0),
    shoppingBudget: cleanBudgetNumber(trip.clothingBudgetILS, defaults.shoppingBudget),
    dailyFoodBudget: cleanBudgetNumber(trip.foodDailyBudgetILS, defaults.dailyFoodBudget),
    totalFoodBudget: cleanBudgetNumber(trip.totalFoodBudgetILS, defaults.totalFoodBudget || 0)
  };
}

function budgetOwnerKey(e){
  return ["home","ido","michael"].includes(e?.budgetOwner) ? e.budgetOwner : "home";
}

function budgetOwnerLabel(owner){
  return owner === "ido" ? "תקציב עידו פרטי" : owner === "michael" ? "תקציב מיכאל פרטי" : "תקציב הבית";
}


function holdingTypeLabel(type){
  return type === "cash" ? "מזומן" : "חשבון בנק";
}

function expenseFundingType(e){
  return e?.paymentMethod === "cash" ? "cash" : "bank";
}

function expenseRateAtEntry(e){
  if(e.currency === "ILS") return 1;
  const map = {USD:"rateUSDAtEntry",JPY:"rateJPYAtEntry",EUR:"rateEURAtEntry",GBP:"rateGBPAtEntry",CAD:"rateCADAtEntry"};
  return Number(e[map[e.currency]] || rates[e.currency] || 1);
}

let fundingLedgerCache = null;

function buildFundingLedger(trip = getActiveTrip(), tripId = activeTripId, extraExpense = null, excludeExpenseId = null){
  const sources = normalizeBudgetSources(trip).map(src => ({...src, remaining: Number(src.amount || 0)}));
  const sourceGroups = {};
  sources.forEach(src => {
    const key = `${src.currency}::${src.holdingType}`;
    (sourceGroups[key] ||= []).push(src);
  });
  const items = getTripExpenses(tripId)
    .filter(e => !excludeExpenseId || e.id !== excludeExpenseId)
    .map(e => ({...e}));
  if(extraExpense) items.push({...extraExpense, id: extraExpense.id || "__preview__"});
  items.sort((a,b)=>(a.created||0)-(b.created||0) || (a.updated||0)-(b.updated||0));

  const allocations = {};
  let homeSpentILS = 0;
  let uncoveredForeignILS = 0;
  let totalFeeILS = 0;

  function consume(groupKey, amount){
    let left = Math.max(0, Number(amount || 0));
    let consumed = 0;
    for(const src of (sourceGroups[groupKey] || [])){
      if(left <= 0) break;
      const take = Math.min(src.remaining, left);
      src.remaining -= take;
      left -= take;
      consumed += take;
    }
    return {consumed, uncovered:left};
  }

  for(const e of items){
    const owner = budgetOwnerKey(e);
    const amount = Math.max(0, Number(e.amount || 0));
    const currency = e.currency || "ILS";
    const baseRate = expenseRateAtEntry(e);
    const isPreview = e.id === "__preview__";
    const storedBaseILS = roundILS(Number.isFinite(Number(e.baseIlsAtEntry)) ? Number(e.baseIlsAtEntry) : amount * baseRate);
    const storedFeeILS = roundILS(Number.isFinite(Number(e.foreignCreditFeeILS)) ? Number(e.foreignCreditFeeILS) : 0);
    const storedTotalILS = roundILS(Number.isFinite(Number(e.ilsAtEntry)) ? Number(e.ilsAtEntry) : storedBaseILS + storedFeeILS);
    let coveredNative = 0, uncoveredNative = 0, feeILS = 0, effectiveILS = 0;

    if(owner !== "home"){
      feeILS = isPreview && currency !== "ILS" && e.paymentMethod === "credit"
        ? roundILS(storedBaseILS * FOREIGN_CREDIT_FEE_RATE)
        : storedFeeILS;
      effectiveILS = isPreview ? roundILS(storedBaseILS + feeILS) : storedTotalILS;
    }else if(currency === "ILS"){
      const preferredType = ["bank","cash"].includes(e.fundingTypeAtEntry) ? e.fundingTypeAtEntry : expenseFundingType(e);
      const fallbackType = preferredType === "cash" ? "bank" : "cash";
      const fromPreferred = consume(`ILS::${preferredType}`, amount);
      const fromFallback = fromPreferred.uncovered > 0 ? consume(`ILS::${fallbackType}`, fromPreferred.uncovered) : {consumed:0, uncovered:0};
      coveredNative = fromPreferred.consumed + fromFallback.consumed;
      uncoveredNative = Math.max(0, amount - coveredNative);
      effectiveILS = isPreview ? roundILS(amount) : storedTotalILS;
    }else{
      const type = ["bank","cash"].includes(e.fundingTypeAtEntry) ? e.fundingTypeAtEntry : expenseFundingType(e);
      const result = consume(`${currency}::${type}`, amount);
      coveredNative = result.consumed;
      uncoveredNative = result.uncovered;
      const uncoveredBaseILS = roundILS(uncoveredNative * baseRate);
      feeILS = isPreview ? roundILS(uncoveredBaseILS * FOREIGN_CREDIT_FEE_RATE) : storedFeeILS;
      effectiveILS = isPreview ? roundILS(storedBaseILS + feeILS) : storedTotalILS;
      if(uncoveredBaseILS + feeILS > 0){
        const ilsCharge = uncoveredBaseILS + feeILS;
        const fromBank = consume("ILS::bank", ilsCharge);
        if(fromBank.uncovered > 0) consume("ILS::cash", fromBank.uncovered);
      }
      uncoveredForeignILS += uncoveredBaseILS;
    }
    if(owner === "home") homeSpentILS = roundILS(homeSpentILS + effectiveILS);
    totalFeeILS = roundILS(totalFeeILS + feeILS);
    allocations[e.id || "__preview__"] = {coveredNative, uncoveredNative, feeILS, effectiveILS, fundingType:expenseFundingType(e)};
  }

  const balances = sources.map(src => ({...src, used: Math.max(0, src.amount-src.remaining), valueILS: roundILS(convertToILS(src.remaining, src.currency, rates))}));
  const walletRemainingILS = roundILS(balances.reduce((sum,src)=>sum+src.valueILS,0));
  const totalBudgetILS = getTripBudgetSourcesILS(trip);
  const remainingTotalILS = roundILS(totalBudgetILS - homeSpentILS);
  return {sources:balances, allocations, homeSpentILS:roundILS(homeSpentILS), remainingTotalILS, walletRemainingILS, uncoveredForeignILS:roundILS(uncoveredForeignILS), totalFeeILS:roundILS(totalFeeILS)};
}

function refreshFundingLedger(){
  fundingLedgerCache = buildFundingLedger();
  return fundingLedgerCache;
}

function getFundingAllocation(e){
  return fundingLedgerCache?.allocations?.[e.id] || null;
}

function walletBalancesHtml(ledger = fundingLedgerCache){
  if(!ledger) ledger = buildFundingLedger();
  const rows = ledger.sources.filter(src => src.currency !== "ILS" || src.amount > 0).map(src => {
    const cls = src.remaining < 0 ? "red" : "";
    return `<div class="walletBalanceLine ${cls}"><b>${currencyLabel(src.currency)} · ${holdingTypeLabel(src.holdingType)}:</b> ${fmtCurrencyByCode(src.remaining,src.currency)} <span class="small">מתוך ${fmtCurrencyByCode(src.amount,src.currency)} · שווי נוכחי ${fmtILS(src.valueILS)}</span></div>`;
  });
  return rows.length ? rows.join("") : '<div class="small">לא הוגדרו קופות תקציב.</div>';
}

function getOwnerExpenses(owner, tripId = activeTripId){
  return getTripExpenses(tripId).filter(e => budgetOwnerKey(e) === owner);
}

function getOwnerSpentILS(owner, tripId = activeTripId){
  return getOwnerExpenses(owner, tripId).reduce((sum,e) => sum + expenseILS(e), 0);
}

function getOwnerBudgetILS(owner, trip = getActiveTrip()){
  const cfg = getTripBudgetConfig(trip);
  if(owner === "ido") return cfg.idoPrivateBudget;
  if(owner === "michael") return cfg.michaelPrivateBudget;
  return cfg.totalBudget;
}

function getCurrentTripBudgetConfig(){
  return getTripBudgetConfig(getActiveTrip());
}

function getTripExpenses(tripId){
  return expenses.filter(e => (e.tripId || defaultTrip.id) === tripId);
}


function isShoppingCategory(category){
  const cat = String(category || "").trim();
  return cat.includes("קניות") || cat.includes("ביגוד") || cat.includes("הנעלה") || cat.includes("אלקטרוניקה");
}

function getTripShoppingILS(tripId){
  return getTripExpenses(tripId)
    .filter(e => budgetOwnerKey(e) === "home")
    .filter(e => isShoppingCategory(e.category))
    .reduce((s,e)=>s + expenseILS(e), 0);
}


window.showAppTab = function(tabId, shouldScroll = true){
  const map = {
    tabMain: "tab-main",
    tabSummary: "tab-summary",
    tabCharts: "tab-charts",
    tabCompare: "tab-compare",
    tabSettings: "tab-settings"
  };
  const scrollMap = {
    tabMain: "tabMain",
    tabSummary: "summaryAnchor",
    tabCharts: "chartsInfoAnchor",
    tabCompare: "compareAnchor",
    tabSettings: "settingsExportAnchor"
  };
  const cls = map[tabId] || "tab-main";

  document.body.classList.remove("tab-main","tab-summary","tab-charts","tab-compare","tab-settings");
  document.body.classList.add(cls);

  document.querySelectorAll(".tabPanel").forEach(p => p.classList.remove("active"));
  const panel = document.getElementById(tabId);
  if(panel) panel.classList.add("active");

  document.querySelectorAll(".appTabs button").forEach(b => b.classList.remove("active"));
  const btn = document.querySelector(`[data-tab-btn="${tabId}"]`);
  if(btn) btn.classList.add("active");

  localStorage.setItem("activeBudgetTab", tabId);

  if(typeof renderExpenses === "function") setTimeout(renderExpenses, 60);
  if(tabId === "tabCharts" && typeof renderDashboard === "function") setTimeout(renderDashboard, 120);
  if(tabId === "tabCompare" && typeof renderTripComparison === "function") setTimeout(() => renderTripComparison(true), 100);
  if(typeof applyMobileTableLabels === "function") setTimeout(applyMobileTableLabels, 90);

  if(shouldScroll){
    const target = document.getElementById(scrollMap[tabId] || tabId) || panel;
    if(target){
      setTimeout(() => {
        try{
          target.scrollIntoView({behavior:"smooth", block:"start"});
        }catch(err){
          target.scrollIntoView(true);
        }
      }, 60);
    }
  }
};

function initAppTabs(){
  const saved = localStorage.getItem("activeBudgetTab") || "tabMain";
  showAppTab(saved, false);
}

const defaultRates = {
  USD: 2.9090,
  JPY: 0.018511,
  EUR: 3.4225,
  GBP: 3.9572,
  CAD: 2.1282,
  source: "בנק ישראל",
  updatedAtText: "11/05/2026",
  note: "USD 2.9090, JPY 0.018511, EUR 3.4225, GBP 3.9572, CAD 2.1282"
};

let rates = {...defaultRates};
let expenses = [];
let tripName = "";
let legacyFreezeRunning = false;

function finitePositive(value, fallback){
  const number = Number(value);
  return Number.isFinite(number) && number > 0 ? number : Number(fallback);
}

function normalizeRatesData(input = {}, base = rates){
  return {
    ...base,
    USD: finitePositive(input.USD, base.USD || defaultRates.USD),
    JPY: finitePositive(input.JPY, base.JPY || defaultRates.JPY),
    EUR: finitePositive(input.EUR, base.EUR || defaultRates.EUR),
    GBP: finitePositive(input.GBP, base.GBP || defaultRates.GBP),
    CAD: finitePositive(input.CAD, base.CAD || defaultRates.CAD),
    source: String(input.source || base.source || defaultRates.source),
    updatedAtText: String(input.updatedAtText || base.updatedAtText || defaultRates.updatedAtText),
    note: String(input.note || base.note || "")
  };
}

function withTimeout(promise, milliseconds, message){
  let timeoutId;
  const timeout = new Promise((_, reject) => {
    timeoutId = setTimeout(() => reject(new Error(message || "הפעולה נמשכה זמן רב מדי")), milliseconds);
  });
  return Promise.race([promise, timeout]).finally(() => clearTimeout(timeoutId));
}

function showActionError(userMessage, err){
  console.error(userMessage, err);
  alert(userMessage + " הנתונים הקיימים לא שונו.");
}

const FOREIGN_CREDIT_FEE_RATE = 0.01;

function paymentMethodLabel(method){
  return method === "credit" ? "אשראי" : "מזומן";
}

function resolvedExpensePaymentMethod(e){
  if(e?.paymentMethod === "cash" || e?.paymentMethod === "credit") return e.paymentMethod;
  return Number(e?.foreignCreditFeeILS || 0) > 0 ? "credit" : "cash";
}

function baseExpenseILSValue(e){
  if(typeof e.baseIlsAtEntry === "number") return e.baseIlsAtEntry;
  if(typeof e.ilsAtEntry === "number" && typeof e.foreignCreditFeeILS === "number") return Math.max(0, e.ilsAtEntry - e.foreignCreditFeeILS);
  return convertToILS(e.amount, e.currency, rates);
}

function foreignCreditFeeILSValue(e){
  if(typeof e.foreignCreditFeeILS === "number") return e.foreignCreditFeeILS;
  return 0;
}

function formatExpenseTotalCell(e){
  const total = expenseILS(e);
  const fee = foreignCreditFeeILSValue(e);
  const base = baseExpenseILSValue(e);
  const currentAllocation = getFundingAllocation(e);
  const hasCoveredSnapshot = Number.isFinite(Number(e?.coveredForeignAmountAtEntry));
  const hasUncoveredSnapshot = Number.isFinite(Number(e?.uncoveredForeignAmountAtEntry));
  const coveredNative = hasCoveredSnapshot ? Number(e.coveredForeignAmountAtEntry) : Number(currentAllocation?.coveredNative || 0);
  const uncoveredNative = hasUncoveredSnapshot ? Number(e.uncoveredForeignAmountAtEntry) : Number(currentAllocation?.uncoveredNative || 0);
  const hasFundingData = currentAllocation || hasCoveredSnapshot || hasUncoveredSnapshot;
  const fundingLine = hasFundingData && e.currency !== "ILS" && budgetOwnerKey(e)==="home"
    ? `<br><span class="expenseSubLine">כוסה מהקופה בעת ההזנה: ${fmtCurrencyByCode(coveredNative,e.currency)}${uncoveredNative>0?` · הומר לשקלים: ${fmtCurrencyByCode(uncoveredNative,e.currency)}`:""}</span>`
    : "";
  if(fee > 0){
    return `<b>${fmtILS(total)}</b><br><span class="expenseSubLine">בסיס: ${fmtILS(base)}<br>עמלת המרה 1%: ${fmtILS(fee)}</span>${fundingLine}`;
  }
  return `<b>${fmtILS(total)}</b>${fundingLine}`;
}

function renderExpenseFeePreview(){
  const box = document.getElementById("fxFeePreview");
  if(!box) return;
  const amount = Number(document.getElementById("amount")?.value || 0);
  const currency = document.getElementById("currency")?.value || "ILS";
  const paymentMethod = document.getElementById("paymentMethod")?.value || "credit";
  const budgetOwner = document.getElementById("budgetOwner")?.value || "home";
  if(!amount || amount <= 0){
    box.innerHTML = "הזן סכום כדי לראות מאיזו קופה תחויב ההוצאה והאם תחול עמלת המרה.";
    box.classList.remove("feeActive"); return;
  }
  const rateSnapshot = {USD:Number(rates.USD),JPY:Number(rates.JPY),EUR:Number(rates.EUR),GBP:Number(rates.GBP),CAD:Number(rates.CAD)};
  const baseILS = convertToILS(amount,currency,rateSnapshot);
  if(budgetOwner !== "home"){
    const feeILS = currency !== "ILS" && paymentMethod === "credit" ? baseILS*FOREIGN_CREDIT_FEE_RATE : 0;
    box.innerHTML = `<b>${budgetOwnerLabel(budgetOwner)}:</b> ${fmtILS(baseILS+feeILS)}${feeILS?`<br>עמלת המרה 1%: ${fmtILS(feeILS)}`:""}`;
    box.classList.toggle("feeActive",feeILS>0); return;
  }
  const previewExpense = {id:"__preview__",tripId:activeTripId,amount,currency,paymentMethod,budgetOwner:"home",created:Date.now(),baseIlsAtEntry:baseILS,
    rateUSDAtEntry:rateSnapshot.USD,rateJPYAtEntry:rateSnapshot.JPY,rateEURAtEntry:rateSnapshot.EUR,rateGBPAtEntry:rateSnapshot.GBP,rateCADAtEntry:rateSnapshot.CAD};
  const ledger = buildFundingLedger(getActiveTrip(),activeTripId,previewExpense,editingExpenseId);
  const a = ledger.allocations.__preview__;
  if(currency === "ILS"){
    const sourceLabel = paymentMethod === "cash" ? "קופת השקלים במזומן" : "קופת השקלים בחשבון הבנק";
    box.innerHTML = `<b>${sourceLabel}:</b> ${fmtILS(a.effectiveILS)}.`;
    box.classList.remove("feeActive"); return;
  }
  const type = paymentMethod === "cash" ? "מזומן" : "חשבון בנק";
  const covered = fmtCurrencyByCode(a.coveredNative,currency);
  const uncovered = fmtCurrencyByCode(a.uncoveredNative,currency);
  box.innerHTML = `<b>${currencyLabel(currency)} · ${type}</b><br>מכוסה מהקופה: ${covered}<br>${a.uncoveredNative>0?`לא מכוסה בקופה: ${uncovered}<br>עמלת המרה 1% על החלק הלא מכוסה: ${fmtILS(a.feeILS)}<br>`:"אין עמלת המרה — קיימת יתרה מספקת בקופה.<br>"}<b>עלות ההוצאה לצורכי מעקב: ${fmtILS(a.effectiveILS)}</b>`;
  box.classList.toggle("feeActive",a.feeILS>0);
}

const fixedCategories = [
  "כרטיסי סים",
  "אוכל",
  "קניות עידו",
  "קניות מיכאל",
  "ביגוד והנעלה",
  "תחבורה ציבורית",
  "אטרקציות",
  "תשלומים במזומן",
  "מלונות",
  "ביטוח נסיעות",
  "טיסות",
  "השכרת רכב",
  "דלק",
  "טיפים",
  "כביסות",
  "קרוז",
  "שונות"
];

let currentFilterType = "category";
let currentCategoryFilter = "ALL";
let currentDateFilter = "ALL";
let summaryCurrencyCode = "ILS";
let currentCompareMetric = "TOTAL";
let currentCompareDate = "ALL";

let trips = [];
let activeTripId = localStorage.getItem("activeTripId") || "japan_2026";
const defaultTrip = {
  id: "japan_2026",
  name: "יפן 2026",
  budgetILS: 93063,
  clothingBudgetILS: 20000,
  foodDailyBudgetILS: 750,
  budgetSources: [{id:"legacy_ils", amount:93063, currency:"ILS", holdingType:"bank"}],
  idoPrivateBudgetILS: 0,
  michaelPrivateBudgetILS: 0,
  created: 0
};


function safeTripId(name){
  const base = String(name || "trip").trim()
    .replace(/[\s]+/g, "_")
    .replace(/[^֐-׿a-zA-Z0-9_\-]/g, "")
    .slice(0, 40);
  return (base || "trip") + "_" + Date.now();
}

function getActiveTrip(){
  return trips.find(t => t.id === activeTripId) || defaultTrip;
}

function getActiveBudgetILS(){
  return Number(getCurrentTripBudgetConfig().totalBudget || 0);
}

function getActiveClothingBudgetILS(){
  return Number(getCurrentTripBudgetConfig().shoppingBudget || 0);
}

function getActiveFoodDailyBudgetILS(){
  return Number(getCurrentTripBudgetConfig().dailyFoodBudget || 0);
}


function activeExpenses(){
  return expenses.filter(e => (e.tripId || "japan_2026") === activeTripId);
}

function visibleExpenses(){
  let list = activeExpenses();

  if(currentFilterType === "category" && currentCategoryFilter !== "ALL"){
    list = list.filter(e => e.category === currentCategoryFilter);
  }

  if(currentFilterType === "date" && currentDateFilter !== "ALL"){
    list = list.filter(e => e.date === currentDateFilter);
  }

  return list;
}

function activeFilterDescription(){
  if(currentFilterType === "category" && currentCategoryFilter !== "ALL"){
    return "קטגוריה: " + currentCategoryFilter;
  }
  if(currentFilterType === "date" && currentDateFilter !== "ALL"){
    return "תאריך: " + currentDateFilter;
  }
  return "כל ההוצאות בטיול הפעיל";
}

function roundILS(value){
  const number = Number(value || 0);
  if(!Number.isFinite(number)) return 0;
  return Math.round((number + Number.EPSILON) * 100) / 100;
}

function fmtILS(n){ return "₪" + roundILS(n).toLocaleString("he-IL", {minimumFractionDigits:2, maximumFractionDigits:2}); }

function fmtCurrencyByCode(n, code){
  n = Number(n || 0);
  if(code === "ILS") return fmtILS(n);
  if(code === "USD") return "$" + n.toLocaleString("en-US", {minimumFractionDigits:2, maximumFractionDigits:2});
  if(code === "JPY") return "¥" + n.toLocaleString("ja-JP", {maximumFractionDigits:0});
  if(code === "EUR") return "€" + n.toLocaleString("de-DE", {minimumFractionDigits:2, maximumFractionDigits:2});
  if(code === "GBP") return "£" + n.toLocaleString("en-GB", {minimumFractionDigits:2, maximumFractionDigits:2});
  if(code === "CAD") return "C$" + n.toLocaleString("en-CA", {minimumFractionDigits:2, maximumFractionDigits:2});
  return fmtILS(n);
}

function convertFromILS(amountILS, code){
  amountILS = Number(amountILS || 0);
  if(code === "ILS") return amountILS;
  return amountILS / Number(rates[code] || 1);
}

function currencyLabel(code){
  const labels = {
    ILS: "שקל ישראלי",
    USD: "דולר אמריקאי",
    JPY: "ין יפני",
    EUR: "יורו",
    GBP: "לירה סטרלינג",
    CAD: "דולר קנדי"
  };
  return labels[code] || "שקל ישראלי";
}

function todayText(){ return new Date().toLocaleDateString("he-IL"); }

function isoToday(){
  const d = new Date();
  const offset = d.getTimezoneOffset();
  const local = new Date(d.getTime() - offset * 60000);
  return local.toISOString().slice(0,10);
}

function heDateToISO(dateText){
  const s = String(dateText || "").trim();
  const m = s.match(/^(\d{1,2})[./-](\d{1,2})[./-](\d{2,4})$/);
  if(!m) return "";
  const day = m[1].padStart(2,"0");
  const month = m[2].padStart(2,"0");
  const year = m[3].length === 2 ? "20" + m[3] : m[3];
  return `${year}-${month}-${day}`;
}

function isoToHeDate(iso){
  const s = String(iso || "").trim();
  const m = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if(!m) return todayText();
  return `${Number(m[3])}.${Number(m[2])}.${m[1]}`;
}

function dateSortKey(dateText){
  return heDateToISO(dateText) || String(dateText || "");
}

function expenseDateTextFromInput(){
  const input = document.getElementById("expenseDate");
  return input?.value ? isoToHeDate(input.value) : todayText();
}

function ensureExpenseDateDefault(){
  const input = document.getElementById("expenseDate");
  if(input && !input.value) input.value = isoToday();
}

function setExpenseDateInput(dateText){
  const input = document.getElementById("expenseDate");
  if(input) input.value = heDateToISO(dateText) || isoToday();
}

function escapeHtml(text){
  return String(text || "")
    .replaceAll("&","&amp;")
    .replaceAll("<","&lt;")
    .replaceAll(">","&gt;")
    .replaceAll('"',"&quot;")
    .replaceAll("'","&#039;");
}

function convertToILS(amount, currency, rateSnapshot = rates){
  amount = Number(amount || 0);
  if(currency === "ILS") return amount;
  if(currency === "USD") return amount * Number(rateSnapshot.USD);
  if(currency === "JPY") return amount * Number(rateSnapshot.JPY);
  if(currency === "EUR") return amount * Number(rateSnapshot.EUR);
  if(currency === "GBP") return amount * Number(rateSnapshot.GBP);
  if(currency === "CAD") return amount * Number(rateSnapshot.CAD);
  return amount;
}

function expenseILS(e){
  if(Number.isFinite(Number(e?.ilsAtEntry))) return roundILS(e.ilsAtEntry);
  const allocation = getFundingAllocation(e);
  if(allocation && budgetOwnerKey(e) === "home") return roundILS(allocation.effectiveILS);
  return roundILS(convertToILS(e.amount, e.currency, rates));
}

async function ensureDefaultSettings(){
  const ratesRef = doc(db, "settings", "rates");
  const defaultTripRef = doc(db, "trips", "japan_2026");
  try{
    const [ratesSnap, tripSnap] = await Promise.all([
      getDocFromServer(ratesRef),
      getDocFromServer(defaultTripRef)
    ]);

    if(!ratesSnap.exists()){
      await trackedSetDoc(ratesRef, defaultRates, {merge:true});
    }

    if(!tripSnap.exists()){
      await trackedSetDoc(defaultTripRef, {...defaultTrip, id:"japan_2026", name:"יפן 2026"}, {merge:true});
    }
  }catch(err){
    console.warn("Default settings check skipped", err);
  }
}

window.scheduleTripNameSave = function(){
  clearTimeout(tripNameSaveTimer);
  tripNameSaveTimer = setTimeout(() => window.saveTripName(), 700);
};

window.saveTripName = async function(){
  clearTimeout(tripNameSaveTimer);
  tripNameSaveTimer = null;
  const input = document.getElementById("tripName");
  const name = input?.value.trim() || "";
  const trip = getActiveTrip();
  const finalName = name || trip.name || "טיול ללא שם";
  tripName = finalName;
  if(finalName === (trip.name || "")) return;
  const updatedTrip = {...trip, id:activeTripId, name:finalName};
  try{
    await trackedSetDoc(doc(db, "trips", activeTripId), updatedTrip, {merge:true});
    trips = trips.some(t => t.id === activeTripId)
      ? trips.map(t => t.id === activeTripId ? {...t, ...updatedTrip} : t)
      : [...trips, updatedTrip];
    renderTripSelector();
    renderHomeTripSelector();
    renderHomeSummary();
  }catch(err){
    console.error("trip name save failed", err);
    setSyncStatus("pending", "שם הטיול טרם נשמר", "המשך לעבוד כרגיל ונסה שוב כשהחיבור יציב.");
  }
};

window.saveManualRates = async function(){
  const usd = Number(document.getElementById("usdRateInput").value);
  const jpy = Number(document.getElementById("jpyRateInput").value);
  const eur = Number(document.getElementById("eurRateInput").value);
  const gbp = Number(document.getElementById("gbpRateInput").value);
  const cad = Number(document.getElementById("cadRateInput").value);
  if(![usd,jpy,eur,gbp,cad].every(value => Number.isFinite(value) && value > 0)){
    alert("יש להזין שער חיובי ותקין לכל מטבע.");
    return;
  }

  const manualRates = {
    USD: usd,
    JPY: jpy,
    EUR: eur,
    GBP: gbp,
    CAD: cad,
    source: "עדכון ידני",
    updatedAtText: new Date().toLocaleString("he-IL"),
    note: "שערים ידניים"
  };

  try{
    await trackedSetDoc(doc(db, "settings", "rates"), manualRates, {merge:true});
    rates = normalizeRatesData(manualRates, rates);
    renderRates();
    renderAll();
    alert("השערים נשמרו בהצלחה. הוצאות שכבר נוספו לא ישתנו.");
  }catch(err){
    showActionError("לא ניתן היה לשמור את שערי המטבע.", err);
  }
};



window.updateRatesLive = async function(){
  try{
    document.getElementById("ratesStatus").innerText = "מעדכן שערים בזמן אמת...";
    const controller = new AbortController();
    const abortTimer = setTimeout(() => controller.abort(), 10000);
    let res;
    try{
      res = await fetch("https://open.er-api.com/v6/latest/ILS", {cache:"no-store", signal:controller.signal});
    }finally{
      clearTimeout(abortTimer);
    }
    if(!res.ok) throw new Error("Rates request failed: " + res.status);
    const data = await res.json();

    if(data.result !== "success" || !data.rates || !data.rates.USD || !data.rates.JPY || !data.rates.EUR || !data.rates.GBP || !data.rates.CAD){
      throw new Error("Invalid rates response");
    }

    const usdIls = 1 / Number(data.rates.USD);
    const jpyIls = 1 / Number(data.rates.JPY);
    const eurIls = 1 / Number(data.rates.EUR);
    const gbpIls = 1 / Number(data.rates.GBP);
    const cadIls = 1 / Number(data.rates.CAD);

    const liveRates = {
      USD: usdIls,
      JPY: jpyIls,
      EUR: eurIls,
      GBP: gbpIls,
      CAD: cadIls,
      source: "שער חי מהאינטרנט",
      updatedAtText: new Date().toLocaleString("he-IL"),
      note: "שער עדכני ממקור מקוון"
    };

    await trackedSetDoc(doc(db, "settings", "rates"), liveRates, {merge:true});
    rates = normalizeRatesData(liveRates, rates);
    renderRates();
    renderAll();

    document.getElementById("ratesStatus").innerText = "השערים נשמרו. הוצאות קיימות נשארות לפי השער שבו הוזנו.";
    alert("השערים נשמרו בהצלחה. הוצאות שכבר נוספו לא השתנו.");
  }catch(err){
    console.error(err);
    document.getElementById("ratesStatus").innerText = "עדכון חי נכשל. נשארים עם השערים האחרונים.";
    alert("לא הצלחתי לעדכן שערים בזמן אמת. בדוק חיבור אינטרנט או נסה שוב.");
  }
};

let editingExpenseId = null;

function buildExpensePayload(existing = {}){
  const category = document.getElementById("category")?.value || "";
  const note = document.getElementById("note")?.value.trim() || "";
  const amount = Number(document.getElementById("amount")?.value);
  const currency = document.getElementById("currency")?.value || "ILS";
  const paymentMethod = document.getElementById("paymentMethod")?.value || "credit";
  const budgetOwner = document.getElementById("budgetOwner")?.value || "home";
  const date = expenseDateTextFromInput();

  if(!category || !Number.isFinite(amount) || amount <= 0){
    alert("נא לבחור קטגוריה ולהזין סכום חיובי ותקין.");
    return null;
  }

  const isExistingExpense = !!editingExpenseId && !!existing && Object.keys(existing).length > 0;
  const financialInputsUnchanged = isExistingExpense
    && Number(existing.amount || 0) === amount
    && (existing.currency || "ILS") === currency
    && resolvedExpensePaymentMethod(existing) === paymentMethod
    && budgetOwnerKey(existing) === budgetOwner
    && Number.isFinite(Number(existing.ilsAtEntry));

  if(financialInputsUnchanged){
    return {
      ...existing,
      tripId: activeTripId,
      tripName: getActiveTrip().name || tripName || "",
      category,
      note,
      amount,
      currency,
      paymentMethod,
      budgetOwner,
      date,
      created: existing.created || Date.now(),
      updated: Date.now(),
      fundingTypeAtEntry: existing.fundingTypeAtEntry || expenseFundingType({currency,paymentMethod})
    };
  }

  const rateSnapshot = {
    USD: Number(rates.USD),
    JPY: Number(rates.JPY),
    EUR: Number(rates.EUR),
    GBP: Number(rates.GBP),
    CAD: Number(rates.CAD),
    source: rates.source || "",
    updatedAtText: rates.updatedAtText || ""
  };

  const baseIlsAtEntry = roundILS(convertToILS(amount, currency, rateSnapshot));
  const previewExpense = {id:"__preview__",tripId:activeTripId,amount,currency,paymentMethod,budgetOwner,created:existing.created||Date.now(),baseIlsAtEntry,
    rateUSDAtEntry:rateSnapshot.USD,rateJPYAtEntry:rateSnapshot.JPY,rateEURAtEntry:rateSnapshot.EUR,rateGBPAtEntry:rateSnapshot.GBP,rateCADAtEntry:rateSnapshot.CAD};
  const previewLedger = buildFundingLedger(getActiveTrip(),activeTripId,previewExpense,editingExpenseId);
  const previewAllocation = previewLedger.allocations.__preview__ || {};
  const foreignCreditFeeILS = roundILS(previewAllocation.feeILS || 0);
  const ilsAtEntry = roundILS(previewAllocation.effectiveILS || baseIlsAtEntry);

  return {
    ...existing,
    tripId: activeTripId,
    tripName: getActiveTrip().name || tripName || "",
    category,
    note,
    amount,
    currency,
    paymentMethod,
    fundingTypeAtEntry: expenseFundingType(previewExpense),
    coveredForeignAmountAtEntry: Number(previewAllocation.coveredNative || 0),
    uncoveredForeignAmountAtEntry: Number(previewAllocation.uncoveredNative || 0),
    budgetOwner,
    date,
    created: existing.created || Date.now(),
    updated: Date.now(),
    baseIlsAtEntry,
    foreignCreditFeeILS,
    foreignCreditFeeRateAtEntry: foreignCreditFeeILS > 0 ? FOREIGN_CREDIT_FEE_RATE : 0,
    ilsAtEntry,
    rateUSDAtEntry: rateSnapshot.USD,
    rateJPYAtEntry: rateSnapshot.JPY,
    rateEURAtEntry: rateSnapshot.EUR,
    rateGBPAtEntry: rateSnapshot.GBP,
    rateCADAtEntry: rateSnapshot.CAD,
    rateSourceAtEntry: rateSnapshot.source,
    rateUpdatedAtEntry: rateSnapshot.updatedAtText
  };
}

function resetExpenseForm(){
  editingExpenseId = null;
  const note = document.getElementById("note");
  const amount = document.getElementById("amount");
  const category = document.getElementById("category");
  const currency = document.getElementById("currency");
  const paymentMethod = document.getElementById("paymentMethod");
  const budgetOwner = document.getElementById("budgetOwner");
  const button = document.getElementById("expenseSubmitButton");
  const cancel = document.getElementById("expenseCancelEditButton");
  const title = document.getElementById("expenseFormTitle");

  if(note) note.value = "";
  if(amount) amount.value = "";
  if(category) category.selectedIndex = 0;
  if(currency) currency.value = "ILS";
  if(paymentMethod) paymentMethod.value = "credit";
  if(budgetOwner) budgetOwner.value = "home";
  ensureExpenseDateDefault();
  renderExpenseFeePreview();
  if(button) button.innerText = "הוסף הוצאה";
  if(cancel) cancel.style.display = "none";
  if(title) title.innerText = "➕ הוספת הוצאה";
}

window.cancelExpenseEdit = function(){
  resetExpenseForm();
};

window.startEditExpense = function(id){
  const e = expenses.find(x => x.id === id);
  if(!e) return;
  editingExpenseId = id;
  const category = document.getElementById("category");
  const note = document.getElementById("note");
  const amount = document.getElementById("amount");
  const currency = document.getElementById("currency");
  const paymentMethod = document.getElementById("paymentMethod");
  const budgetOwner = document.getElementById("budgetOwner");
  const button = document.getElementById("expenseSubmitButton");
  const cancel = document.getElementById("expenseCancelEditButton");
  const title = document.getElementById("expenseFormTitle");

  if(category) category.value = e.category || "";
  if(note) note.value = e.note || "";
  if(amount) amount.value = Number(e.amount || 0);
  if(currency) currency.value = e.currency || "ILS";
  if(paymentMethod) paymentMethod.value = resolvedExpensePaymentMethod(e);
  if(budgetOwner) budgetOwner.value = budgetOwnerKey(e);
  setExpenseDateInput(e.date);
  renderExpenseFeePreview();
  if(button) button.innerText = "שמירת הוצאה";
  if(cancel) cancel.style.display = "block";
  if(title) title.innerText = "✏️ עריכת הוצאה";
  document.getElementById("quickExpenseCard")?.scrollIntoView({behavior:"smooth", block:"start"});
};

window.addExpense = async function(){
  const existing = editingExpenseId ? (expenses.find(e => e.id === editingExpenseId) || {}) : {};
  const payload = buildExpensePayload(existing);
  if(!payload) return;
  try{
    if(editingExpenseId){
      await trackedSetDoc(doc(db, "expenses", editingExpenseId), payload, {merge:true});
    }else{
      await trackedAddDoc(collection(db, "expenses"), payload);
    }
    const category = payload.category;
    const date = payload.date;
    resetExpenseForm();
    setTimeout(() => checkWarnings(category, date), 800);
  }catch(err){
    showActionError("לא ניתן היה לשמור את ההוצאה.", err);
  }
};

async function freezeLegacyExpensesOnce(){
  if(legacyFreezeRunning || legacyFreezeCompleted) return;
  legacyFreezeRunning = true;
  try{
    const snap = await getDocs(collection(db, "expenses"));
    const updates = [];
    snap.forEach(d => {
      const e = d.data();
      if(typeof e.ilsAtEntry !== "number" || !e.tripId || typeof e.baseIlsAtEntry !== "number"){
        const rateSnapshot = {USD: Number(rates.USD), JPY: Number(rates.JPY), EUR: Number(rates.EUR), GBP: Number(rates.GBP), CAD: Number(rates.CAD)};
        const baseIlsAtEntry = typeof e.baseIlsAtEntry === "number" ? e.baseIlsAtEntry : convertToILS(e.amount, e.currency, rateSnapshot);
        const foreignCreditFeeILS = typeof e.foreignCreditFeeILS === "number" ? e.foreignCreditFeeILS : 0;
        updates.push(() => trackedSetDoc(doc(db, "expenses", d.id), {
          tripId: e.tripId || defaultTrip.id,
          tripName: e.tripName || "יפן 2026",
          baseIlsAtEntry,
          foreignCreditFeeILS,
          ilsAtEntry: typeof e.ilsAtEntry === "number" ? e.ilsAtEntry : baseIlsAtEntry + foreignCreditFeeILS,
          rateUSDAtEntry: Number(e.rateUSDAtEntry || rateSnapshot.USD),
          rateJPYAtEntry: Number(e.rateJPYAtEntry || rateSnapshot.JPY),
          rateEURAtEntry: Number(e.rateEURAtEntry || rateSnapshot.EUR),
          rateGBPAtEntry: Number(e.rateGBPAtEntry || rateSnapshot.GBP),
          rateCADAtEntry: Number(e.rateCADAtEntry || rateSnapshot.CAD),
          rateSourceAtEntry: e.rateSourceAtEntry || "נעילת שער להוצאה ישנה",
          rateUpdatedAtEntry: e.rateUpdatedAtEntry || new Date().toLocaleString("he-IL")
        }, {merge:true}));
      }
    });
    if(updates.length) await runInChunks(updates, 100);
    legacyFreezeCompleted = true;
  }catch(err){
    console.error("legacy expense normalization failed", err);
  }finally{
    legacyFreezeRunning = false;
  }
}

async function runInChunks(tasks, chunkSize = 100){
  for(let index = 0; index < tasks.length; index += chunkSize){
    await Promise.all(tasks.slice(index, index + chunkSize).map(task => task()));
  }
}

window.deleteExpense = async function(id){
  if(!confirm("למחוק את ההוצאה הזו?")) return;
  try{
    await trackedDeleteDoc(doc(db, "expenses", id));
  }catch(err){
    showActionError("לא ניתן היה למחוק את ההוצאה.", err);
  }
};

window.clearTrip = async function(){
  if(!confirm("לפני האיפוס מומלץ לשמור דוח PDF או HTML. למחוק את כל הוצאות הטיול הפעיל?")) return;
  let snap;
  try{
    snap = navigator.onLine
      ? await withTimeout(getDocsFromServer(collection(db, "expenses")), 15000, "טעינת ההוצאות נמשכה זמן רב מדי")
      : await getDocs(collection(db, "expenses"));
  }catch(err){
    console.warn("Server expense list unavailable; using local cache", err);
    snap = await getDocs(collection(db, "expenses"));
  }
  const deleteTasks = [];
  snap.forEach(d => {
    const e = d.data();
    if((e.tripId || defaultTrip.id) === activeTripId){
      deleteTasks.push(() => trackedDeleteDoc(doc(db, "expenses", d.id)));
    }
  });
  try{
    await runInChunks(deleteTasks, 100);
    alert("הוצאות הטיול הפעיל נמחקו בהצלחה.");
  }catch(err){
    showActionError("לא ניתן היה להשלים את איפוס הטיול.", err);
  }
};

function checkWarnings(category, date){
  const clothingTotal = getTripShoppingILS(activeTripId);

  const foodTodayILS = activeExpenses().filter(e => budgetOwnerKey(e) === "home" && e.category === "אוכל" && e.date === date)
    .reduce((s,e)=>s + expenseILS(e), 0);

  const messages = [];

  if(isShoppingCategory(category) && clothingTotal > getActiveClothingBudgetILS()){
    messages.push("חריגה מתקציב הקניות: " + fmtILS(clothingTotal) + " מתוך " + fmtILS(getActiveClothingBudgetILS()));
  }

  if(category === "אוכל" && foodTodayILS > getActiveFoodDailyBudgetILS()){
      messages.push("חריגה מתקציב אוכל יומי: " + fmtILS(foodTodayILS) + " מתוך " + fmtILS(getActiveFoodDailyBudgetILS()));
    }

  if(messages.length) alert(messages.join("\\n"));
}



function renderHomeTripSelector(){
  const homeSelect = document.getElementById("homeTripSelector");
  if(!homeSelect) return;

  const sorted = trips.slice().sort((a,b)=>(a.created||0)-(b.created||0));
  homeSelect.innerHTML = sorted.map(t => `<option value="${escapeHtml(t.id)}">${escapeHtml(t.name || t.id)}</option>`).join("");
  homeSelect.value = activeTripId;
}

window.switchTripFromHome = function(){
  const homeSelect = document.getElementById("homeTripSelector");
  if(!homeSelect) return;
  activeTripId = homeSelect.value || defaultTrip.id;
  localStorage.setItem("activeTripId", activeTripId);

  const mainSelect = document.getElementById("tripSelector");
  if(mainSelect) mainSelect.value = activeTripId;

  currentFilterType = "category";
  currentCategoryFilter = "ALL";
  currentDateFilter = "ALL";
  tripSettingsDirty = false;

  renderTripSelector();
  renderHomeTripSelector();
  renderAll();
};

function renderTripSelector(){
  const select = document.getElementById("tripSelector");
  if(!select) return;

  const sorted = trips.slice().sort((a,b)=>(a.created||0)-(b.created||0));
  select.innerHTML = sorted.map(t => `<option value="${escapeHtml(t.id)}">${escapeHtml(t.name || t.id)}</option>`).join("");

  if(!sorted.find(t => t.id === activeTripId)){
    activeTripId = "japan_2026";
    localStorage.setItem("activeTripId", activeTripId);
  }

  select.value = activeTripId;

  const trip = getActiveTrip();
  const tripNameInput = document.getElementById("tripName");
  if(tripNameInput && document.activeElement !== tripNameInput) tripNameInput.value = trip.name || "";
  tripName = trip.name || "";

  const cfg = getTripBudgetConfig(trip);
  const clothingInput = document.getElementById("tripClothingInput");
  const foodInput = document.getElementById("tripFoodInput");
  const totalFoodInput = document.getElementById("tripTotalFoodInput");
  const idoBudgetInput = document.getElementById("tripIdoBudgetInput");
  const michaelBudgetInput = document.getElementById("tripMichaelBudgetInput");
  const shouldRefreshSettings = !tripSettingsDirty || budgetSourcesRenderedTripId !== activeTripId;
  if(shouldRefreshSettings){
    if(clothingInput) clothingInput.value = Number(cfg.shoppingBudget || 0);
    if(idoBudgetInput) idoBudgetInput.value = Number(cfg.idoPrivateBudget || 0);
    if(michaelBudgetInput) michaelBudgetInput.value = Number(cfg.michaelPrivateBudget || 0);
    if(foodInput) foodInput.value = Number(cfg.dailyFoodBudget || 0);
    if(totalFoodInput) totalFoodInput.value = Number(cfg.totalFoodBudget || 0);
    renderBudgetSourceRows();
  }else{
    updateBudgetSourcesPreview();
  }

  const activeLabel = document.getElementById("activeTripLabel");
  if(activeLabel){
    activeLabel.innerHTML = `טיול פעיל: <b>${escapeHtml(trip.name || activeTripId)}</b>`;
  }
  const topBudget = document.getElementById("topBudgetLabel");
  const topClothing = document.getElementById("topClothingBudgetLabel");
  const topFood = document.getElementById("topFoodBudgetLabel");
  if(topBudget) topBudget.innerText = fmtILS(getActiveBudgetILS());
  if(topClothing) topClothing.innerText = fmtILS(getActiveClothingBudgetILS());
  if(topFood) topFood.innerText = fmtILS(getActiveFoodDailyBudgetILS());
}

window.toggleTripSettings = function(forceOpen){
  const panel = document.getElementById("tripSettingsPanel");
  if(!panel) return;
  if(forceOpen === true){
    panel.classList.add("open");
  }else{
    panel.classList.toggle("open");
  }
};

window.switchTrip = function(){
  const select = document.getElementById("tripSelector");
  activeTripId = select?.value || defaultTrip.id;
  localStorage.setItem("activeTripId", activeTripId);
  currentFilterType = "category";
  currentCategoryFilter = "ALL";
  currentDateFilter = "ALL";
  tripSettingsDirty = false;
  budgetSourcesRenderedTripId = null;
  renderTripSelector();
  renderBudgetSourceRows(true);
  toggleTripSettings(true);
  renderAll();
};

window.createNewTrip = async function(){
  const name = prompt("שם הטיול החדש:");
  if(!name || !name.trim()) return;

  const id = safeTripId(name);
  const preset = getTripDefaultBudgetProfile({id, name:name.trim()});
  const newTrip = {
    id,
    name: name.trim(),
    budgetILS: preset.totalBudget,
    budgetSources: [{id:"source_" + Date.now(), amount:preset.totalBudget, currency:"ILS", holdingType:"bank"}],
    idoPrivateBudgetILS: 0,
    michaelPrivateBudgetILS: 0,
    clothingBudgetILS: preset.shoppingBudget,
    foodDailyBudgetILS: preset.dailyFoodBudget,
    totalFoodBudgetILS: preset.totalFoodBudget || 0,
    created: Date.now()
  };

  try{
    await trackedSetDoc(doc(db, "trips", id), newTrip, {merge:true});
    trips = [...trips.filter(t => t.id !== id), newTrip].sort((a,b)=>(a.created||0)-(b.created||0));
    activeTripId = id;
    localStorage.setItem("activeTripId", activeTripId);
    tripSettingsDirty = false;
    budgetSourcesRenderedTripId = null;
    renderTripSelector();
    renderHomeTripSelector();
    renderAll();
  }catch(err){
    showActionError("לא ניתן היה ליצור את הטיול החדש.", err);
  }
};

let budgetSourcesRenderedTripId = null;
function renderBudgetSourceRows(force = false){
  const box = document.getElementById("budgetSourcesRows");
  if(!box) return;
  if(!force && budgetSourcesRenderedTripId === activeTripId && box.children.length) { updateBudgetSourcesPreview(); return; }
  const trip = getActiveTrip();
  const sources = normalizeBudgetSources(trip);
  box.innerHTML = sources.map((src,index) => `
    <div class="budgetSourceRow" data-source-id="${escapeHtml(src.id)}">
      <input class="budgetSourceAmount" type="number" inputmode="decimal" min="0" step="0.01" value="${Number(src.amount || 0)}" aria-label="סכום מקור תקציב">
      <select class="budgetSourceCurrency" aria-label="מטבע מקור תקציב">
        ${["ILS","USD","JPY","EUR","GBP","CAD"].map(code => `<option value="${code}" ${src.currency===code?"selected":""}>${currencyLabel(code)}</option>`).join("")}
      </select>
      <select class="budgetSourceHolding" aria-label="סוג אחזקת מקור התקציב">
        <option value="bank" ${src.holdingType!=="cash"?"selected":""}>חשבון בנק</option>
        <option value="cash" ${src.holdingType==="cash"?"selected":""}>מזומן</option>
      </select>
      <button type="button" class="danger smallBtn budgetSourceRemove" onclick="removeBudgetSourceRow(this)" ${sources.length===1?"disabled":""}>הסר</button>
    </div>
  `).join("");
  budgetSourcesRenderedTripId = activeTripId;
  updateBudgetSourcesPreview();
}

window.addBudgetSourceRow = function(){
  tripSettingsDirty = true;
  const box = document.getElementById("budgetSourcesRows");
  if(!box) return;
  const row = document.createElement("div");
  row.className = "budgetSourceRow";
  row.dataset.sourceId = "source_" + Date.now() + "_" + Math.random().toString(36).slice(2,7);
  row.innerHTML = `
    <input class="budgetSourceAmount" type="number" inputmode="decimal" min="0" step="0.01" value="0" aria-label="סכום מקור תקציב">
    <select class="budgetSourceCurrency" aria-label="מטבע מקור תקציב">
      ${["ILS","USD","JPY","EUR","GBP","CAD"].map(code => `<option value="${code}">${currencyLabel(code)}</option>`).join("")}
    </select>
    <select class="budgetSourceHolding" aria-label="סוג אחזקת מקור התקציב">
      <option value="bank">חשבון בנק</option><option value="cash">מזומן</option>
    </select>
    <button type="button" class="danger smallBtn budgetSourceRemove" onclick="removeBudgetSourceRow(this)">הסר</button>`;
  box.appendChild(row);
  box.querySelectorAll(".budgetSourceRemove").forEach(b => b.disabled = false);
  updateBudgetSourcesPreview();
};

window.removeBudgetSourceRow = function(button){
  tripSettingsDirty = true;
  const box = document.getElementById("budgetSourcesRows");
  if(!box || box.children.length <= 1) return;
  button.closest(".budgetSourceRow")?.remove();
  if(box.children.length === 1) box.querySelector(".budgetSourceRemove").disabled = true;
  updateBudgetSourcesPreview();
};

function readBudgetSourcesFromUI(){
  return Array.from(document.querySelectorAll("#budgetSourcesRows .budgetSourceRow")).map((row,index) => ({
    id: row.dataset.sourceId || `source_${Date.now()}_${index}`,
    amount: cleanBudgetNumber(row.querySelector(".budgetSourceAmount")?.value, 0),
    currency: row.querySelector(".budgetSourceCurrency")?.value || "ILS",
    holdingType: row.querySelector(".budgetSourceHolding")?.value === "cash" ? "cash" : "bank"
  })).filter(src => src.amount > 0);
}

window.updateBudgetSourcesPreview = function(){
  const preview = document.getElementById("budgetSourcesPreview");
  if(!preview) return;
  const sources = readBudgetSourcesFromUI();
  const total = sources.reduce((sum,src) => sum + convertToILS(src.amount, src.currency, rates), 0);
  preview.innerHTML = sources.length
    ? `<b>שווי תקציב הבית כעת:</b> ${fmtILS(total)}<br>${sources.map(src=>`${currencyLabel(src.currency)} · ${holdingTypeLabel(src.holdingType)}: <b>${fmtCurrencyByCode(src.amount,src.currency)}</b>`).join("<br>")}<br><span class="small">השווי בשקלים מתעדכן לפי שערי המטבע הפעילים.</span>`
    : `<span class="red">יש להזין לפחות מקור תקציב אחד עם סכום גדול מאפס.</span>`;
};

document.addEventListener("input", e => {
  if(e.target.matches("#tripSettingsPanel input:not(#tripName),#tripSettingsPanel select")) tripSettingsDirty = true;
  if(e.target.matches(".budgetSourceAmount")) updateBudgetSourcesPreview();
});
document.addEventListener("change", e => {
  if(e.target.matches("#tripSettingsPanel input:not(#tripName),#tripSettingsPanel select")) tripSettingsDirty = true;
  if(e.target.matches(".budgetSourceCurrency,.budgetSourceHolding")) updateBudgetSourcesPreview();
});

window.saveTripSettings = async function(){
  const trip = getActiveTrip();
  const name = document.getElementById("tripName")?.value.trim() || trip.name || "טיול ללא שם";
  const cfg = getTripBudgetConfig(trip);
  const budgetSources = readBudgetSourcesFromUI();
  if(!budgetSources.length){
    alert("יש להזין לפחות מקור תקציב אחד עם סכום גדול מאפס.");
    return;
  }
  const budget = budgetSources.reduce((sum,src) => sum + convertToILS(src.amount, src.currency, rates), 0);
  const idoPrivateBudget = cleanBudgetNumber(document.getElementById("tripIdoBudgetInput")?.value, cfg.idoPrivateBudget);
  const michaelPrivateBudget = cleanBudgetNumber(document.getElementById("tripMichaelBudgetInput")?.value, cfg.michaelPrivateBudget);
  const clothing = cleanBudgetNumber(document.getElementById("tripClothingInput")?.value, cfg.shoppingBudget);
  const food = cleanBudgetNumber(document.getElementById("tripFoodInput")?.value, cfg.dailyFoodBudget);
  const totalFood = cleanBudgetNumber(document.getElementById("tripTotalFoodInput")?.value, cfg.totalFoodBudget || 0);

  const updatedTrip = {
    ...trip,
    id: activeTripId,
    name,
    budgetILS: budget,
    budgetSources,
    idoPrivateBudgetILS: idoPrivateBudget,
    michaelPrivateBudgetILS: michaelPrivateBudget,
    clothingBudgetILS: clothing,
    foodDailyBudgetILS: food,
    totalFoodBudgetILS: totalFood
  };

  try{
    await trackedSetDoc(doc(db, "trips", activeTripId), updatedTrip, {merge:true});
    trips = trips.some(t => t.id === activeTripId)
      ? trips.map(t => t.id === activeTripId ? {...t, ...updatedTrip} : t)
      : [...trips, updatedTrip];
    tripSettingsDirty = false;
    budgetSourcesRenderedTripId = null;
    renderTripSelector();
    renderBudgetSourceRows(true);
    renderHomeTripSelector();
    renderAll();
    alert("הגדרות הטיול נשמרו בהצלחה.");
  }catch(err){
    showActionError("לא ניתן היה לשמור את הגדרות הטיול.", err);
  }
};

let compareTripsPanelOpen = false;

window.toggleCompareTripsPanel = function(){
  compareTripsPanelOpen = !compareTripsPanelOpen;
  const panel = document.getElementById("compareTripsPanel");
  if(panel){
    panel.classList.toggle("open", compareTripsPanelOpen);
  }
};

function getSelectedCompareTripIds(){
  const checked = Array.from(document.querySelectorAll(".compareTripCheck:checked")).map(i => i.value);
  if(checked.length) return checked.slice(0,5);
  return trips.slice(0, Math.min(2, trips.length)).map(t => t.id);
}

function getTripExpensesById(tripId){
  return expenses.filter(e => (e.tripId || defaultTrip.id) === tripId);
}

function getAllComparisonCategories(){
  const cats = new Set(fixedCategories);
  expenses.forEach(e => { if(e.category) cats.add(e.category); });
  return Array.from(cats).sort();
}

function getAllComparisonDates(){
  const dates = new Set();
  expenses.forEach(e => { if(e.date) dates.add(e.date); });
  return Array.from(dates).sort((a,b)=>dateSortKey(a).localeCompare(dateSortKey(b)));
}

function renderComparisonTripChecks(){
  const box = document.getElementById("compareTripsBox");
  if(!box) return;

  const existing = new Set(Array.from(document.querySelectorAll(".compareTripCheck:checked")).map(i => i.value));
  const sortedTrips = trips.slice().sort((a,b)=>(a.created||0)-(b.created||0));
  if(existing.size < 2){
    sortedTrips.forEach(t => {
      if(existing.size < 2) existing.add(t.id);
    });
  }

  box.innerHTML = sortedTrips.map(t => {
    const checked = existing.has(t.id) ? "checked" : "";
    return `<label class="tripCheckItem"><input class="compareTripCheck" type="checkbox" value="${escapeHtml(t.id)}" ${checked} onchange="handleCompareTripCheck(this)"> ${escapeHtml(t.name || t.id)}</label>`;
  }).join("");

  const panel = document.getElementById("compareTripsPanel");
  if(panel){
    panel.classList.toggle("open", compareTripsPanelOpen);
  }
}

window.handleCompareTripCheck = function(input){
  const checked = Array.from(document.querySelectorAll(".compareTripCheck:checked"));
  if(checked.length > 5){
    input.checked = false;
    alert("אפשר לבחור עד 5 טיולים להשוואה.");
    return;
  }
  renderTripComparison(false);
};

function renderCompareMetricOptions(){
  const metricSelect = document.getElementById("compareMetric");
  if(!metricSelect) return;

  const current = currentCompareMetric || metricSelect.value || "TOTAL";
  const cats = getAllComparisonCategories();

  metricSelect.innerHTML =
    `<option value="TOTAL">סה״כ הוצאות</option>` +
    `<option value="DATE">תאריך מסוים</option>` +
    `<option value="MOST_EXPENSIVE_DAY">יום הכי יקר</option>` +
    `<option value="CHEAPEST_DAY">יום הכי זול</option>` +
    cats.map(c => `<option value="CAT::${escapeHtml(c)}">${escapeHtml(c)}</option>`).join("");

  const validValues = ["TOTAL","DATE","MOST_EXPENSIVE_DAY","CHEAPEST_DAY", ...cats.map(c => "CAT::" + c)];
  currentCompareMetric = validValues.includes(current) ? current : "TOTAL";
  metricSelect.value = currentCompareMetric;
}

function renderCompareDateOptions(){
  const dateSelect = document.getElementById("compareDate");
  if(!dateSelect) return;

  const current = currentCompareDate || dateSelect.value || "ALL";
  const dates = getAllComparisonDates();
  dateSelect.innerHTML = `<option value="ALL">כל התאריכים יחד</option>` + dates.map(d => `<option value="${escapeHtml(d)}">${escapeHtml(d)}</option>`).join("");
  currentCompareDate = dates.includes(current) ? current : "ALL";
  dateSelect.value = currentCompareDate;
}

function renderCompareSelectors(){
  renderCompareMetricOptions();
  renderCompareDateOptions();

  const metric = document.getElementById("compareMetric")?.value || "TOTAL";
  const dateBox = document.getElementById("compareDateBox");
  if(dateBox) dateBox.style.display = metric === "DATE" ? "block" : "none";
}

function metricLabel(metric, date){
  if(metric === "TOTAL") return "סה״כ הוצאות";
  if(metric === "DATE") return date && date !== "ALL" ? "תאריך " + date : "כל התאריכים";
  if(metric === "MOST_EXPENSIVE_DAY") return "יום הכי יקר";
  if(metric === "CHEAPEST_DAY") return "יום הכי זול";
  if(metric.startsWith("CAT::")) return metric.replace("CAT::", "");
  return metric;
}

function tripMetricTotal(tripId, metric, date){
  let list = getTripExpensesById(tripId);

  if(metric === "DATE" && date !== "ALL"){
    list = list.filter(e => e.date === date);
  }

  if(metric.startsWith("CAT::")){
    const cat = metric.replace("CAT::", "");
    list = list.filter(e => e.category === cat);
  }

  if(metric === "MOST_EXPENSIVE_DAY" || metric === "CHEAPEST_DAY"){
    const byDate = {};
    list.forEach(e => {
      if(!e.date) return;
      byDate[e.date] = (byDate[e.date] || 0) + expenseILS(e);
    });
    const entries = Object.entries(byDate);
    if(!entries.length){
      return { total:0, count:0, extra:"אין נתונים" };
    }
    entries.sort((a,b) => metric === "MOST_EXPENSIVE_DAY" ? b[1]-a[1] : a[1]-b[1]);
    const [chosenDate, total] = entries[0];
    const count = list.filter(e => e.date === chosenDate).length;
    return { total, count, extra: chosenDate };
  }

  return {
    total: list.reduce((s,e)=>s + expenseILS(e), 0),
    count: list.length,
    extra: ""
  };
}


window.setCompareMetricFromUI = function(){
  const metricSelect = document.getElementById("compareMetric");
  currentCompareMetric = metricSelect?.value || "TOTAL";
  if(currentCompareMetric !== "DATE"){
    currentCompareDate = "ALL";
  }
  renderTripComparison(false);
};

window.setCompareDateFromUI = function(){
  const dateSelect = document.getElementById("compareDate");
  currentCompareDate = dateSelect?.value || "ALL";
  renderTripComparison(false);
};

function renderTripComparison(rebuildChecks = true){
  const head = document.getElementById("tripComparisonHead");
  const body = document.getElementById("tripComparisonBody");
  const status = document.getElementById("compareStatus");
  if(!head || !body) return;

  if(rebuildChecks) renderComparisonTripChecks();
  renderCompareSelectors();

  const selectedIds = getSelectedCompareTripIds();
  const selectedTrips = trips.filter(t => selectedIds.includes(t.id));
  const metric = currentCompareMetric || document.getElementById("compareMetric")?.value || "TOTAL";
  const date = currentCompareDate || document.getElementById("compareDate")?.value || "ALL";

  if(selectedTrips.length < 2){
    head.innerHTML = "";
    body.innerHTML = `<tr><td>בחר לפחות 2 טיולים להשוואה.</td></tr>`;
    if(status) status.innerHTML = `<span class="red">צריך לבחור לפחות 2 טיולים ועד 5.</span>`;
    return;
  }

  if(selectedTrips.length > 5){
    head.innerHTML = "";
    body.innerHTML = `<tr><td>אפשר לבחור עד 5 טיולים בלבד.</td></tr>`;
    if(status) status.innerHTML = `<span class="red">בחרת יותר מדי טיולים.</span>`;
    return;
  }

  const label = metricLabel(metric, date);
  if(status){
    status.innerHTML = `משווה: <b>${escapeHtml(label)}</b> בין <b>${selectedTrips.length}</b> טיולים.`;
  }

  if(metric === "MOST_EXPENSIVE_DAY" || metric === "CHEAPEST_DAY"){
    head.innerHTML = `<tr><th>טיול</th><th>${escapeHtml(label)}</th><th>תאריך</th><th>מס׳ הוצאות</th></tr>`;
    body.innerHTML = selectedTrips.map(t => {
      const result = tripMetricTotal(t.id, metric, date);
      return `
        <tr>
          <td><b>${escapeHtml(t.name || t.id)}</b></td>
          <td>${fmtILS(result.total)}</td>
          <td>${escapeHtml(result.extra || "")}</td>
          <td>${result.count}</td>
        </tr>
      `;
    }).join("");
    return;
  }

  head.innerHTML = `<tr><th>טיול</th><th>${escapeHtml(label)}</th><th>מס׳ הוצאות</th></tr>`;
  body.innerHTML = selectedTrips.map(t => {
    const result = tripMetricTotal(t.id, metric, date);
    return `
      <tr>
        <td><b>${escapeHtml(t.name || t.id)}</b></td>
        <td>${fmtILS(result.total)}</td>
        <td>${result.count}</td>
      </tr>
    `;
  }).join("");
}

function renderRates(){
  document.getElementById("usdRateTop").innerText = Number(rates.USD).toFixed(4);
  document.getElementById("jpyRateTop").innerText = Number(rates.JPY).toFixed(6);
  if(document.getElementById("jpy100RateTop")) document.getElementById("jpy100RateTop").innerText = (Number(rates.JPY) * 100).toFixed(4);
  if(document.getElementById("eurRateTop")) document.getElementById("eurRateTop").innerText = Number(rates.EUR).toFixed(4);
  if(document.getElementById("gbpRateTop")) document.getElementById("gbpRateTop").innerText = Number(rates.GBP).toFixed(4);
  if(document.getElementById("cadRateTop")) document.getElementById("cadRateTop").innerText = Number(rates.CAD).toFixed(4);
  document.getElementById("rateSource").innerText = rates.source || "";
  document.getElementById("rateUpdated").innerText = rates.updatedAtText || "";
  document.getElementById("usdRateInput").value = Number(rates.USD);
  document.getElementById("jpyRateInput").value = Number(rates.JPY);
  if(document.getElementById("eurRateInput")) document.getElementById("eurRateInput").value = Number(rates.EUR);
  if(document.getElementById("gbpRateInput")) document.getElementById("gbpRateInput").value = Number(rates.GBP);
  if(document.getElementById("cadRateInput")) document.getElementById("cadRateInput").value = Number(rates.CAD);
  renderExpenseFeePreview();
}

function getCategoryTotals(){
  const totals = {};
  activeExpenses().forEach(e => {
    totals[e.category] = (totals[e.category] || 0) + expenseILS(e);
  });
  return totals;
}

function getDailyTotals(){
  const days = {};
  activeExpenses().forEach(e => {
    if(!days[e.date]){
      days[e.date] = {
        total:0, food:0, transport:0, attractions:0, shopping:0, hotels:0,
        insurance:0, flights:0, carRental:0, fuel:0, other:0, categories:{}
      };
    }
    const ils = expenseILS(e);
    days[e.date].total += ils;
    days[e.date].categories[e.category] = (days[e.date].categories[e.category] || 0) + ils;
    if(e.category === "אוכל") days[e.date].food += ils;
    if(e.category === "תחבורה" || e.category === "תחבורה ציבורית") days[e.date].transport += ils;
    if(e.category === "אטרקציות") days[e.date].attractions += ils;
    if(e.category === "מלונות") days[e.date].hotels += ils;
    if(e.category === "ביטוח נסיעות") days[e.date].insurance += ils;
    if(e.category === "טיסות") days[e.date].flights += ils;
    if(e.category === "השכרת רכב") days[e.date].carRental += ils;
    if(e.category === "דלק") days[e.date].fuel += ils;
    if(["טיפים","כביסות",
  "קרוז",
  "שונות"].includes(e.category)) days[e.date].other += ils;
    if(isShoppingCategory(e.category)) days[e.date].shopping += ils;
  });
  return days;
}

window.setSummaryCurrencyFromUI = function(){
  summaryCurrencyCode = document.getElementById("summaryCurrency")?.value || "ILS";
  renderSummary();
};

function renderSummary(){
  const totalILS = activeExpenses().reduce((s,e)=>s + expenseILS(e), 0);
  const homeSpentILS = getOwnerSpentILS("home");
  const idoSpentILS = getOwnerSpentILS("ido");
  const michaelSpentILS = getOwnerSpentILS("michael");
  const remainingILS = getActiveBudgetILS() - homeSpentILS;
  const totalsILS = getCategoryTotals();
  const clothingILS = getTripShoppingILS(activeTripId);

  const select = document.getElementById("summaryCurrency");
  const selectedCurrency = select ? select.value : summaryCurrencyCode;
  summaryCurrencyCode = selectedCurrency || "ILS";

  const totalDisplay = convertFromILS(totalILS, summaryCurrencyCode);
  const remainingDisplay = convertFromILS(remainingILS, summaryCurrencyCode);
  const clothingDisplay = convertFromILS(clothingILS, summaryCurrencyCode);
  const clothingBudgetDisplay = convertFromILS(getActiveClothingBudgetILS(), summaryCurrencyCode);

  let categoryHtml = "";
  Object.keys(totalsILS).sort().forEach(cat => {
    const totalCatILS = totalsILS[cat];
    const red = cat === "ביגוד והנעלה" && totalCatILS > getActiveClothingBudgetILS();
    const converted = convertFromILS(totalCatILS, summaryCurrencyCode);
    categoryHtml += `<div class="${red ? "red" : ""}">${escapeHtml(cat)}: <b>${fmtCurrencyByCode(converted, summaryCurrencyCode)}</b></div>`;
  });

  const box = document.getElementById("summaryFinal");
  if(!box) return;

  box.innerHTML = `
    <b>מטבע תצוגה:</b> ${currencyLabel(summaryCurrencyCode)}<br><br>
    <b>סה״כ הוצאות בכל התקציבים:</b> ${fmtCurrencyByCode(totalDisplay, summaryCurrencyCode)}<br>
    <b>הוצאות מתקציב הבית:</b> ${fmtCurrencyByCode(convertFromILS(homeSpentILS, summaryCurrencyCode), summaryCurrencyCode)}<br>
    <b>יתרת תקציב הבית:</b> <span class="${remainingILS < 0 ? "red" : "green"}">${fmtCurrencyByCode(remainingDisplay, summaryCurrencyCode)}</span><br><br>

    <b>תקציבים פרטיים:</b><br>
    עידו — נוצל ${fmtCurrencyByCode(convertFromILS(idoSpentILS, summaryCurrencyCode), summaryCurrencyCode)} מתוך ${fmtCurrencyByCode(convertFromILS(getOwnerBudgetILS("ido"), summaryCurrencyCode), summaryCurrencyCode)}; יתרה ${fmtCurrencyByCode(convertFromILS(getOwnerBudgetILS("ido")-idoSpentILS, summaryCurrencyCode), summaryCurrencyCode)}<br>
    מיכאל — נוצל ${fmtCurrencyByCode(convertFromILS(michaelSpentILS, summaryCurrencyCode), summaryCurrencyCode)} מתוך ${fmtCurrencyByCode(convertFromILS(getOwnerBudgetILS("michael"), summaryCurrencyCode), summaryCurrencyCode)}; יתרה ${fmtCurrencyByCode(convertFromILS(getOwnerBudgetILS("michael")-michaelSpentILS, summaryCurrencyCode), summaryCurrencyCode)}<br><br>

    <b>בקרת ביגוד, הנעלה, ואלקטרוניקה:</b><br>
    נוצל: <span class="${clothingILS > getActiveClothingBudgetILS() ? "red" : ""}">${fmtCurrencyByCode(clothingDisplay, summaryCurrencyCode)}</span>
    מתוך ${fmtCurrencyByCode(clothingBudgetDisplay, summaryCurrencyCode)}<br><br>

    <b>סיכום לפי קטגוריות:</b><br>
    ${categoryHtml || "אין עדיין הוצאות"}
  `;
}

function renderDaily(){
  const tbody = document.getElementById("dailyBody");
  const thead = document.querySelector("#dailyTable thead");
  tbody.innerHTML = "";
  const days = getDailyTotals();
  const keys = Object.keys(days).sort((a,b)=>dateSortKey(a).localeCompare(dateSortKey(b)));
  const usedCats = Array.from(new Set(activeExpenses().map(e => e.category))).sort();

  if(thead){
    thead.innerHTML = `<tr><th>תאריך</th><th>סה״כ יומי</th>${usedCats.map(c => `<th>${escapeHtml(c)}</th>`).join("")}</tr>`;
  }

  if(!keys.length){
    tbody.innerHTML = `<tr><td colspan="${usedCats.length + 2}">אין עדיין הוצאות</td></tr>`;
    return;
  }

  tbody.innerHTML = keys.map(date => {
    const d = days[date];
    return `
      <tr>
        <td>${escapeHtml(date)}</td>
        <td><b>${fmtILS(d.total)}</b></td>
        ${usedCats.map(cat => {
          const val = d.categories[cat] || 0;
          const cls = cat === "אוכל" && val > getActiveFoodDailyBudgetILS() ? "red-cell" : "";
          return `<td class="${cls}">${val ? fmtILS(val) : "—"}</td>`;
        }).join("")}
      </tr>
    `;
  }).join("");
}


function compactDateForTable(dateText){
  const s = String(dateText || "");
  const m = s.match(/^(\d{1,2})[./-](\d{1,2})[./-](\d{2,4})$/);
  if(!m) return s;
  return `${m[1]}.${m[2]}`;
}
function compactCurrencyLabel(currency){
  if(currency === "ILS") return "₪";
  if(currency === "USD") return "$";
  if(currency === "JPY") return "¥";
  if(currency === "EUR") return "€";
  if(currency === "GBP") return "£";
  if(currency === "CAD") return "C$";
  return currency || "";
}
function compactRateLabel(e){
  if(e.currency === "ILS") return "שקל";
  if(e.currency === "USD") return Number(e.rateUSDAtEntry || rates.USD).toFixed(2);
  if(e.currency === "JPY") return Number(e.rateJPYAtEntry || rates.JPY).toFixed(4);
  if(e.currency === "EUR") return Number(e.rateEURAtEntry || rates.EUR).toFixed(2);
  if(e.currency === "GBP") return Number(e.rateGBPAtEntry || rates.GBP).toFixed(2);
  if(e.currency === "CAD") return Number(e.rateCADAtEntry || rates.CAD).toFixed(2);
  return "";
}


function refreshDynamicExpensesTableWidth(){
  const table = document.getElementById("expensesTable");
  if(table) table.style.width = "max-content";
}

function renderExpenses(){
  const tbody = document.getElementById("expensesBody");
  if(!tbody) return;

  updateFilterOptions();
  renderCategoryTotals();

  const list = visibleExpenses()
    .slice()
    .sort((a,b)=> dateSortKey(a.date).localeCompare(dateSortKey(b.date)) || (a.created||0)-(b.created||0));

  if(!list.length){
    const desc = activeFilterDescription();
    tbody.innerHTML = `<tr><td colspan="9">אין הוצאות להצגה לפי הסינון הנוכחי</td></tr>`;
    const summary = document.getElementById("selectedCategorySummary");
    if(summary) summary.innerHTML = `אין הוצאות להצגה — ${escapeHtml(desc)}.`;
    refreshDynamicExpensesTableWidth();
    return;
  }

  let total = 0;
  const rows = list.map(e => {
    const ils = expenseILS(e);
    total += ils;
    return `
      <tr>
        <td>${escapeHtml(compactDateForTable(e.date))}</td>
        <td>${escapeHtml(budgetOwnerLabel(budgetOwnerKey(e)))}</td>
        <td>${escapeHtml(e.category)}</td>
        <td>${escapeHtml(e.note || "")}</td>
        <td>${Number(e.amount).toFixed(2)}</td>
        <td>${compactCurrencyLabel(e.currency)}<br><span class="expenseSubLine">${paymentMethodLabel(resolvedExpensePaymentMethod(e))}</span></td>
        <td>${formatExpenseTotalCell(e)}</td>
        <td>${compactRateLabel(e)}</td>
        <td>
          <div class="rowActions">
            <button type="button" class="secondaryBtn smallBtn" data-expense-action="edit" data-expense-id="${escapeHtml(e.id)}">ערוך</button>
            <button type="button" class="danger smallBtn" aria-label="מחיקת הוצאה" data-expense-action="delete" data-expense-id="${escapeHtml(e.id)}">×</button>
          </div>
        </td>
      </tr>
    `;
  });

  rows.push(`
    <tr class="expensesTotalRow">
      <td colspan="6"><b>סה״כ כל ההוצאות המוצגות</b></td>
      <td><b>${fmtILS(total)}</b></td>
      <td colspan="2"></td>
    </tr>
  `);
  tbody.innerHTML = rows.join("");

  const summary = document.getElementById("selectedCategorySummary");
  if(summary){
    const desc = activeFilterDescription();
    const totalActive = activeExpenses().reduce((s,e)=>s + expenseILS(e), 0);
    if(desc === "כל ההוצאות בטיול הפעיל"){
      summary.innerHTML = `סה״כ הוצאות בטיול הפעיל: <b>${fmtILS(total)}</b>`;
    }else{
      summary.innerHTML = `סה״כ מוצג לפי ${escapeHtml(desc)}: <b>${fmtILS(total)}</b><br><span class="small">סה״כ כל הטיול הפעיל: ${fmtILS(totalActive)}</span>`;
    }
  }

  if(typeof applyMobileTableLabels === "function") setTimeout(applyMobileTableLabels, 30);
  refreshDynamicExpensesTableWidth();
}

document.getElementById("expensesBody")?.addEventListener("click", event => {
  const button = event.target.closest("button[data-expense-action]");
  if(!button) return;
  const id = button.dataset.expenseId || "";
  if(button.dataset.expenseAction === "edit") window.startEditExpense(id);
  if(button.dataset.expenseAction === "delete") window.deleteExpense(id);
});

function updateFilterOptions(){
  updateCategoryFilterOptions();
  updateDateFilterOptions();

  const typeSelect = document.getElementById("filterType");
  if(typeSelect) typeSelect.value = currentFilterType;

  const catBox = document.getElementById("categoryFilterBox");
  const dateBox = document.getElementById("dateFilterBox");
  if(catBox) catBox.style.display = currentFilterType === "category" ? "block" : "none";
  if(dateBox) dateBox.style.display = currentFilterType === "date" ? "block" : "none";
}

function updateCategoryFilterOptions(){
  const select = document.getElementById("categoryFilter");
  if(!select) return;
  const fromExpenses = Array.from(new Set(activeExpenses().map(e => e.category))).filter(Boolean);
  const cats = Array.from(new Set([...fixedCategories, ...fromExpenses])).sort();

  select.innerHTML = `<option value="ALL">כל הקטגוריות</option>` + cats.map(c => `<option value="${escapeHtml(c)}">${escapeHtml(c)}</option>`).join("");

  if(currentCategoryFilter !== "ALL" && !cats.includes(currentCategoryFilter)){
    currentCategoryFilter = "ALL";
  }
  select.value = currentCategoryFilter;
}

function updateDateFilterOptions(){
  const select = document.getElementById("dateFilter");
  const chips = document.getElementById("activeDatesChips");
  if(!select) return;

  const dates = Array.from(new Set(activeExpenses().map(e => e.date).filter(Boolean))).sort((a,b)=>dateSortKey(a).localeCompare(dateSortKey(b)));

  select.innerHTML = `<option value="ALL">כל התאריכים הפעילים</option>` + dates.map(d => `<option value="${escapeHtml(d)}">${escapeHtml(d)}</option>`).join("");

  if(currentDateFilter !== "ALL" && !dates.includes(currentDateFilter)){
    currentDateFilter = "ALL";
  }
  select.value = currentDateFilter;

  if(chips){
    if(!dates.length){
      chips.innerHTML = `<span class="small">אין עדיין תאריכים עם פעילות.</span>`;
    }else{
      chips.innerHTML = dates.map(d => `<button class="dateChip ${currentDateFilter === d ? "active" : ""}" onclick="selectDateFilter('${escapeHtml(d)}')">${escapeHtml(d)}</button>`).join("");
    }
  }
}

window.setFilterTypeFromUI = function(){
  const type = document.getElementById("filterType")?.value || "category";
  currentFilterType = type;
  renderAll();
};

window.setCategoryFilterFromUI = function(){
  const val = document.getElementById("categoryFilter")?.value || "ALL";
  currentFilterType = "category";
  currentCategoryFilter = val;
  renderAll();
};

window.setDateFilterFromUI = function(){
  const val = document.getElementById("dateFilter")?.value || "ALL";
  currentFilterType = "date";
  currentDateFilter = val;
  renderAll();
};

window.selectDateFilter = function(dateValue){
  currentFilterType = "date";
  currentDateFilter = dateValue;
  renderAll();
};


window.clearCategoryFilter = function(){
  currentFilterType = "category";
  currentCategoryFilter = "ALL";
  currentDateFilter = "ALL";
  renderAll();
};

function renderCategoryTotals(){
  const box = document.getElementById("categoryTotalsGrid");
  if(!box) return;
  const totals = getCategoryTotals();
  const entries = Object.entries(totals).sort((a,b)=>b[1]-a[1]);
  if(!entries.length){
    box.innerHTML = `<div class="categoryTotalItem">אין עדיין הוצאות</div>`;
    return;
  }
  box.innerHTML = entries.map(([cat,total]) => `
    <div class="categoryTotalItem">
      ${escapeHtml(cat)}<br>
      <span>${fmtILS(total)}</span>
    </div>
  `).join("");
}

window.toggleTheme = function(){
  document.body.classList.toggle("darkMode");
  localStorage.setItem("budgetTheme", document.body.classList.contains("darkMode") ? "dark" : "light");
};

function applySavedTheme(){
  if(localStorage.getItem("budgetTheme") === "dark"){
    document.body.classList.add("darkMode");
  }
}


const chartPalette = [
  "#2563eb","#16a34a","#f59e0b","#dc2626","#7c3aed","#0891b2",
  "#db2777","#65a30d","#ea580c","#475569","#0f766e","#9333ea",
  "#b45309","#1d4ed8","#be123c","#4d7c0f"
];

function setStatusClass(el, status){
  if(!el) return;
  el.classList.remove("statusGood","statusWarn","statusBad");
  if(status === "bad") el.classList.add("statusBad");
  else if(status === "warn") el.classList.add("statusWarn");
  else el.classList.add("statusGood");
}

function getStatusByPercent(percent){
  if(percent >= 100) return "bad";
  if(percent >= 80) return "warn";
  return "good";
}

function drawEmptyCanvas(canvas, text){
  if(!canvas) return;
  const ctx = canvas.getContext("2d");
  ctx.clearRect(0,0,canvas.width,canvas.height);
  ctx.fillStyle = document.body.classList.contains("darkMode") ? "#cbd5e1" : "#64748b";
  ctx.font = "18px Arial";
  ctx.textAlign = "center";
  ctx.fillText(text, canvas.width/2, canvas.height/2);
}

function drawPieChart(totals){
  const canvas = document.getElementById("categoryPie");
  const legend = document.getElementById("categoryLegend");
  if(!canvas || !legend) return;

  const entries = Object.entries(totals).filter(([k,v]) => v > 0).sort((a,b)=>b[1]-a[1]);
  const sum = entries.reduce((s,[k,v])=>s+v,0);
  const ctx = canvas.getContext("2d");
  ctx.clearRect(0,0,canvas.width,canvas.height);
  legend.innerHTML = "";

  if(!entries.length || sum <= 0){
    drawEmptyCanvas(canvas, "אין עדיין נתונים לגרף");
    return;
  }

  const cx = canvas.width / 2;
  const cy = canvas.height / 2;
  const radius = Math.min(canvas.width, canvas.height) * 0.34;
  let start = -Math.PI / 2;

  entries.forEach(([cat,val], i) => {
    const slice = (val / sum) * Math.PI * 2;
    ctx.beginPath();
    ctx.moveTo(cx, cy);
    ctx.arc(cx, cy, radius, start, start + slice);
    ctx.closePath();
    ctx.fillStyle = chartPalette[i % chartPalette.length];
    ctx.fill();
    start += slice;
  });

  entries.slice(0,10).forEach(([cat,val], i) => {
    const pct = ((val/sum)*100).toFixed(1);
    const div = document.createElement("div");
    div.className = "legendItem";
    div.innerHTML = `<span class="legendColor" style="background:${chartPalette[i % chartPalette.length]}"></span>
      <span>${escapeHtml(cat)} — ${fmtILS(val)} (${pct}%)</span>`;
    legend.appendChild(div);
  });
}

function drawDailyBarChart(days){
  const canvas = document.getElementById("dailyBar");
  if(!canvas) return;
  const ctx = canvas.getContext("2d");
  ctx.clearRect(0,0,canvas.width,canvas.height);

  const entries = Object.keys(days).sort((a,b)=>dateSortKey(a).localeCompare(dateSortKey(b))).map(d => [d, days[d].total]);
  if(!entries.length){
    drawEmptyCanvas(canvas, "אין עדיין נתונים לגרף");
    return;
  }

  const padding = 38;
  const chartW = canvas.width - padding * 2;
  const chartH = canvas.height - padding * 2;
  const maxVal = Math.max(...entries.map(e=>e[1]), 1);
  const barW = Math.max(8, chartW / entries.length * 0.65);
  const gap = chartW / entries.length;

  ctx.strokeStyle = document.body.classList.contains("darkMode") ? "#64748b" : "#cbd5e1";
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.moveTo(padding, padding);
  ctx.lineTo(padding, padding + chartH);
  ctx.lineTo(padding + chartW, padding + chartH);
  ctx.stroke();

  entries.forEach(([date,val], i) => {
    const h = (val / maxVal) * chartH;
    const x = padding + i * gap + (gap - barW)/2;
    const y = padding + chartH - h;
    ctx.fillStyle = val >= 3000 ? "#dc2626" : val >= 2000 ? "#f59e0b" : "#16a34a";
    ctx.fillRect(x, y, barW, h);
  });

  ctx.fillStyle = document.body.classList.contains("darkMode") ? "#e2e8f0" : "#334155";
  ctx.font = "13px Arial";
  ctx.textAlign = "center";
  ctx.fillText("שיא יומי: " + fmtILS(maxVal), canvas.width/2, 20);
}

function renderDashboard(){
  const totalILS = activeExpenses().reduce((s,e)=>s + expenseILS(e), 0);
  const homeSpentILS = getOwnerSpentILS("home");
  const remaining = getActiveBudgetILS() - homeSpentILS;
  const percent = getActiveBudgetILS() > 0 ? (homeSpentILS / getActiveBudgetILS()) * 100 : 0;
  const status = getStatusByPercent(percent);
  const days = getDailyTotals();
  const totals = getCategoryTotals();
  const today = todayText();
  const todayTotal = days[today]?.total || 0;
  const foodTodayILS = days[today]?.food || 0;
  const clothing = getTripShoppingILS(activeTripId);
  const activeDays = Object.keys(days).length || 1;
  const avgDaily = totalILS / activeDays;

  const elTotal = document.getElementById("dashTotal");
  const elRemaining = document.getElementById("dashRemaining");
  const elPercent = document.getElementById("dashPercent");
  const elAvg = document.getElementById("dashAvgDaily");
  const elToday = document.getElementById("dashToday");
  const elFood = document.getElementById("dashFoodToday");
  const elClothing = document.getElementById("dashClothing");
  const elCount = document.getElementById("dashCount");
  const bar = document.getElementById("budgetProgressBar");
  const statusText = document.getElementById("budgetStatusText");

  if(elTotal) elTotal.innerText = fmtILS(totalILS);
  if(elRemaining) elRemaining.innerText = fmtILS(remaining);
  if(elPercent) elPercent.innerText = percent.toFixed(1) + "%";
  if(elAvg) elAvg.innerText = fmtILS(avgDaily);
  if(elToday) elToday.innerText = fmtILS(todayTotal);
  if(elFood) elFood.innerText = fmtILS(foodTodayILS);
  if(elClothing) elClothing.innerText = fmtILS(clothing);
  if(elCount) elCount.innerText = String(activeExpenses().length);

  setStatusClass(elRemaining, remaining < 0 ? "bad" : status);
  setStatusClass(elPercent, status);
  setStatusClass(elFood, foodTodayILS > getActiveFoodDailyBudgetILS() ? "bad" : foodTodayILS >= getActiveFoodDailyBudgetILS()*0.8 ? "warn" : "good");
  setStatusClass(elClothing, clothing > getActiveClothingBudgetILS() ? "bad" : clothing >= getActiveClothingBudgetILS()*0.8 ? "warn" : "good");

  if(bar){
    const capped = Math.min(percent, 100);
    bar.style.width = capped.toFixed(1) + "%";
    bar.style.background = status === "bad" ? "#dc2626" : status === "warn" ? "#f59e0b" : "#16a34a";
  }

  if(statusText){
    if(percent >= 100){
      statusText.innerHTML = `<span class="statusBad"><b>חריגה:</b> עברתם את התקציב הכולל ב־${fmtILS(Math.abs(remaining))}.</span>`;
    }else if(percent >= 80){
      statusText.innerHTML = `<span class="statusWarn"><b>התראה:</b> ניצלתם ${percent.toFixed(1)}% מהתקציב. כדאי להאט קצב.</span>`;
    }else{
      statusText.innerHTML = `<span class="statusGood"><b>תקין:</b> אתם עדיין בטווח התקציב.</span>`;
    }
  }

  drawPieChart(totals);
  drawDailyBarChart(days);
}



function renderTripBudgetSummary(){
  const box = document.getElementById("tripBudgetSummaryBox");
  if(!box) return;

  const cfg = getCurrentTripBudgetConfig();
  const activeTrip = trips.find(t => t.id === activeTripId) || {};
  const activeTripName = activeTrip.name || activeTripId || "טיול פעיל";
  const ledger = fundingLedgerCache || buildFundingLedger();
  const totalSpent = ledger.homeSpentILS;
  const idoSpent = getOwnerSpentILS("ido", activeTripId);
  const michaelSpent = getOwnerSpentILS("michael", activeTripId);
  const shoppingSpent = getTripShoppingILS(activeTripId);

  const totalLeft = ledger.remainingTotalILS;
  const shoppingLeft = cfg.shoppingBudget - shoppingSpent;

  function cls(v){
    if(v < 0) return "tripBudgetBad";
    if(v < (cfg.totalBudget * 0.15)) return "tripBudgetWarn";
    return "tripBudgetGood";
  }

  box.innerHTML = `
    <div class="tripBudgetSub" style="margin-bottom:12px;font-weight:800;">טיול פעיל: ${escapeHtml(activeTripName)}</div>
    <div class="tripBudgetSummaryGrid">
      <div class="tripBudgetCard">
        <h4>💰 תקציב כולל לטיול</h4>
        <div class="tripBudgetMain ${cls(totalLeft)}">${fmtILS(totalLeft)}</div>
        <div class="tripBudgetSub">
          תקציב כולל: ${fmtILS(cfg.totalBudget)}<br>
          נוצל עד עכשיו: ${fmtILS(totalSpent)}<br>
          <div class="walletBalances">${walletBalancesHtml(ledger)}</div>
        </div>
      </div>

      <div class="tripBudgetCard">
        <h4>👦 תקציב עידו פרטי</h4>
        <div class="tripBudgetMain ${cfg.idoPrivateBudget-idoSpent < 0 ? "tripBudgetBad" : "tripBudgetGood"}">${fmtILS(cfg.idoPrivateBudget-idoSpent)}</div>
        <div class="tripBudgetSub">תקציב: ${fmtILS(cfg.idoPrivateBudget)}<br>נוצל: ${fmtILS(idoSpent)}</div>
      </div>

      <div class="tripBudgetCard">
        <h4>👦 תקציב מיכאל פרטי</h4>
        <div class="tripBudgetMain ${cfg.michaelPrivateBudget-michaelSpent < 0 ? "tripBudgetBad" : "tripBudgetGood"}">${fmtILS(cfg.michaelPrivateBudget-michaelSpent)}</div>
        <div class="tripBudgetSub">תקציב: ${fmtILS(cfg.michaelPrivateBudget)}<br>נוצל: ${fmtILS(michaelSpent)}</div>
      </div>

      <div class="tripBudgetCard">
        <h4>🛍️ תקציב קניות</h4>
        <div class="tripBudgetMain ${shoppingLeft < 0 ? "tripBudgetBad" : "tripBudgetGood"}">${fmtILS(shoppingLeft)}</div>
        <div class="tripBudgetSub">
          תקציב קניות: ${fmtILS(cfg.shoppingBudget)}<br>
          נוצל בקניות: ${fmtILS(shoppingSpent)}
        </div>
      </div>

      <div class="tripBudgetCard">
        <h4>🍽️ תקציב אוכל</h4>
        <div class="tripBudgetMain tripBudgetGood">${fmtILS(cfg.dailyFoodBudget)}</div>
        <div class="tripBudgetSub">
          יעד יומי: ${fmtILS(cfg.dailyFoodBudget)}<br>
          ${cfg.totalFoodBudget ? "מסגרת אוכל כוללת: " + fmtILS(cfg.totalFoodBudget) : "ללא מסגרת אוכל כוללת"}
        </div>
      </div>
    </div>
  `;
}



function applyMobileTableLabels(){
  document.querySelectorAll("table.mobileCards").forEach(table => {
    const headers = Array.from(table.querySelectorAll("thead th")).map(th => th.innerText.trim());
    table.querySelectorAll("tbody tr").forEach(tr => {
      Array.from(tr.children).forEach((td, i) => {
        if(headers[i]) td.setAttribute("data-label", headers[i]);
      });
    });
  });
}


function renderHomeSummary(){
  const trip = trips.find(t => t.id === activeTripId) || {};
  const cfg = getCurrentTripBudgetConfig ? getCurrentTripBudgetConfig() : {totalBudget:0,shoppingBudget:0,dailyFoodBudget:0};
  const ledger = fundingLedgerCache || buildFundingLedger();
  const totalSpent = ledger.homeSpentILS;
  const shoppingSpent = getTripShoppingILS ? getTripShoppingILS(activeTripId) : 0;
  const idoSpent = getOwnerSpentILS("ido", activeTripId);
  const michaelSpent = getOwnerSpentILS("michael", activeTripId);
  const totalLeft = Number(ledger.remainingTotalILS || 0);
  const shoppingLeft = Number(cfg.shoppingBudget || 0) - shoppingSpent;
  const idoLeft = Number(cfg.idoPrivateBudget || 0) - idoSpent;
  const michaelLeft = Number(cfg.michaelPrivateBudget || 0) - michaelSpent;

  const set = (id, value) => {
    const el = document.getElementById(id);
    if(el) el.innerText = value;
  };

  set("homeTripName", trip.name || activeTripId || "—");
  set("homeTotalLeft", fmtILS(totalLeft));
  set("homeTotalUsed", "נוצל: " + fmtILS(totalSpent) + " מתוך " + fmtILS(cfg.totalBudget || 0));
  const walletBox = document.getElementById("homeWalletBalances");
  if(walletBox) walletBox.innerHTML = walletBalancesHtml(ledger);
  set("homeShoppingLeft", fmtILS(shoppingLeft));
  set("homeShoppingUsed", "נוצל בקניות: " + fmtILS(shoppingSpent) + " מתוך " + fmtILS(cfg.shoppingBudget || 0));
  set("homeFoodBudget", fmtILS(cfg.dailyFoodBudget || 0));
  set("homeFoodSub", cfg.totalFoodBudget ? "מסגרת אוכל כוללת: " + fmtILS(cfg.totalFoodBudget) : "יעד יומי פעיל");
  set("homeIdoLeft", fmtILS(idoLeft));
  set("homeIdoUsed", "נוצל: " + fmtILS(idoSpent) + " מתוך " + fmtILS(cfg.idoPrivateBudget || 0));
  set("homeMichaelLeft", fmtILS(michaelLeft));
  set("homeMichaelUsed", "נוצל: " + fmtILS(michaelSpent) + " מתוך " + fmtILS(cfg.michaelPrivateBudget || 0));

  const totalEl = document.getElementById("homeTotalLeft");
  const shopEl = document.getElementById("homeShoppingLeft");
  const idoEl = document.getElementById("homeIdoLeft");
  const michaelEl = document.getElementById("homeMichaelLeft");
  if(totalEl) totalEl.style.color = totalLeft < 0 ? "#dc2626" : "#15803d";
  if(shopEl) shopEl.style.color = shoppingLeft < 0 ? "#dc2626" : "#15803d";
  if(idoEl) idoEl.style.color = idoLeft < 0 ? "#dc2626" : "#15803d";
  if(michaelEl) michaelEl.style.color = michaelLeft < 0 ? "#dc2626" : "#15803d";
}

function renderAll(){
  refreshFundingLedger();
  ensureExpenseDateDefault();
  renderTripSelector();
  renderRates();
  renderSummary();
  renderDaily();
  renderExpenses();
  renderTripBudgetSummary();
  renderHomeSummary();
  renderHomeTripSelector();
  if(document.body.classList.contains("tab-charts")) renderDashboard();
  if(document.body.classList.contains("tab-compare")) renderTripComparison();
  setTimeout(applyMobileTableLabels, 50);
}

function scheduleRenderAll(){
  if(renderFramePending) return;
  renderFramePending = true;
  requestAnimationFrame(() => {
    renderFramePending = false;
    renderAll();
  });
}


function sortedActiveExpenses(){
  return activeExpenses().slice().sort((a,b) =>
    dateSortKey(a.date).localeCompare(dateSortKey(b.date)) || (a.created||0)-(b.created||0)
  );
}

function safeDownloadFilename(value){
  return String(value || "חופשה")
    .trim()
    .replace(/[\/:*?"<>|]+/g, "_")
    .replace(/\s+/g, " ")
    .slice(0, 80) || "חופשה";
}

window.exportHTML = function(){
  try{
    refreshFundingLedger();
    renderSummary();
    const generated = new Date().toLocaleString("he-IL");
    const activeTripTitle = getActiveTrip().name || tripName || "חופשה";
    const list = sortedActiveExpenses();
    const reportTotal = list.reduce((sum,e) => sum + expenseILS(e), 0);

    const expensesRows = list.map(e => `
      <tr>
        <td>${escapeHtml(compactDateForTable(e.date))}</td>
        <td>${escapeHtml(budgetOwnerLabel(budgetOwnerKey(e)))}</td>
        <td>${escapeHtml(e.category)}</td>
        <td>${escapeHtml(e.note || "")}</td>
        <td>${Number(e.amount).toFixed(2)}</td>
        <td>${compactCurrencyLabel(e.currency)} / ${paymentMethodLabel(resolvedExpensePaymentMethod(e))}</td>
        <td>${formatExpenseTotalCell(e)}</td>
        <td>${compactRateLabel(e)}</td>
      </tr>
    `).join("");

    const keepTogetherClass = list.length <= 12 && list.reduce((n,e)=>n+String(e.note||"").length,0) < 900 ? " fitOnePage" : "";
    const report = `<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>דוח תקציב ${escapeHtml(activeTripTitle)}</title>
<style>
*{box-sizing:border-box}
body{font-family:Arial,"Noto Sans Hebrew",sans-serif;direction:rtl;padding:20px;color:#111827;background:#fff;margin:0}
h1,h2{text-align:center}
.meta{text-align:center;color:#475569;margin-bottom:18px}
.summary{border:1px solid #d7dce5;border-radius:14px;padding:14px;margin:14px 0;line-height:1.8}
.reportTableSection{margin-top:18px}
.reportTableTitle{margin:0 0 10px;break-after:avoid-page;page-break-after:avoid}
.printTableViewport{max-width:100%;max-height:72vh;overflow:auto;border:1px solid #d7dce5;border-radius:12px}
table{width:100%;border-collapse:collapse;font-size:12px;background:#fff}
th,td{border:1px solid #d7dce5;padding:7px;text-align:center;vertical-align:middle;overflow-wrap:anywhere}
th{background:#2563eb;color:white;position:sticky;top:0}
tbody tr:nth-child(even) td{background:#fafafa}
tfoot td{font-weight:900;background:#eef6ff}
.expenseSubLine{display:block;font-size:10px;line-height:1.25;color:#475569;font-weight:700}
@page{size:A4 portrait;margin:10mm}
@media print{
  body{padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .printTableViewport{overflow:visible;max-height:none;border:0;border-radius:0}
  .reportTableSection.fitOnePage{break-inside:avoid-page;page-break-inside:avoid}
  .reportTableTitle{break-after:avoid-page;page-break-after:avoid}
  table{font-size:9px;table-layout:fixed;break-before:avoid-page;page-break-before:avoid}
  thead{display:table-header-group}
  tfoot{display:table-row-group}
  tr{break-inside:avoid-page;page-break-inside:avoid}
  th,td{padding:4px 3px;word-break:break-word;overflow-wrap:anywhere}
  th:nth-child(1),td:nth-child(1){width:8%}
  th:nth-child(2),td:nth-child(2){width:12%}
  th:nth-child(3),td:nth-child(3){width:12%}
  th:nth-child(4),td:nth-child(4){width:22%}
  th:nth-child(5),td:nth-child(5){width:9%}
  th:nth-child(6),td:nth-child(6){width:12%}
  th:nth-child(7),td:nth-child(7){width:17%}
  th:nth-child(8),td:nth-child(8){width:8%}
}
</style>
</head>
<body>
<h1>דוח תקציב חופשה</h1>
<h2>${escapeHtml(activeTripTitle)}</h2>
<div class="meta">נוצר: ${generated}</div>
<div class="summary">${document.getElementById("summaryFinal")?.innerHTML || ""}</div>
<section class="reportDailySection">
  <h2 class="reportTableTitle">סיכום יומי</h2>
  <div class="printTableViewport">${document.getElementById("dailyTable")?.outerHTML || ""}</div>
</section>
<section class="reportTableSection${keepTogetherClass}">
  <h2 class="reportTableTitle">פירוט הוצאות</h2>
  <div class="printTableViewport">
    <table>
      <thead><tr><th>תאריך</th><th>תקציב</th><th>קטגוריה</th><th>פירוט</th><th>סכום</th><th>מטבע/תשלום</th><th>סה״כ בשקלים</th><th>שער</th></tr></thead>
      <tbody>${expensesRows || `<tr><td colspan="8">אין הוצאות להצגה</td></tr>`}</tbody>
      <tfoot><tr><td colspan="6">סה״כ הוצאות</td><td>${fmtILS(reportTotal)}</td><td></td></tr></tfoot>
    </table>
  </div>
</section>
</body>
</html>`;

    const blob = new Blob([report], {type:"text/html;charset=utf-8"});
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "דוח_תקציב_" + safeDownloadFilename(activeTripTitle) + ".html";
    document.body.appendChild(a);
    a.click();
    a.remove();
    setTimeout(() => URL.revokeObjectURL(url), 1500);
  }catch(err){
    console.error("exportHTML failed", err);
    alert("ייצוא הדוח ל־HTML נכשל. רענן את האפליקציה ונסה שוב.");
  }
};

function buildPrintableBudgetReport(){
  refreshFundingLedger();
  renderSummary();
  const activeTripTitle = getActiveTrip().name || tripName || "חופשה";
  const generated = new Date().toLocaleString("he-IL");
  const list = sortedActiveExpenses();
  const reportTotal = list.reduce((sum,e) => sum + expenseILS(e), 0);
  const keepTogetherClass = list.length <= 12 && list.reduce((n,e)=>n+String(e.note||"").length,0) < 900 ? " fitOnePage" : "";

  const rows = list.map(e => `
    <tr>
      <td>${escapeHtml(compactDateForTable(e.date))}</td>
      <td>${escapeHtml(budgetOwnerLabel(budgetOwnerKey(e)))}</td>
      <td>${escapeHtml(e.category)}</td>
      <td>${escapeHtml(e.note || "")}</td>
      <td>${Number(e.amount).toFixed(2)}</td>
      <td>${compactCurrencyLabel(e.currency)} / ${paymentMethodLabel(resolvedExpensePaymentMethod(e))}</td>
      <td>${formatExpenseTotalCell(e)}</td>
      <td>${compactRateLabel(e)}</td>
    </tr>
  `).join("");

  return `
<div class="printReport">
  <h1>דוח תקציב חופשה</h1>
  <h2>${escapeHtml(activeTripTitle)}</h2>
  <div class="meta">נוצר: ${generated}</div>
  <div class="summary">${document.getElementById("summaryFinal")?.innerHTML || ""}</div>
  <section class="reportTableSection${keepTogetherClass}">
    <h2 class="reportTableTitle">פירוט הוצאות</h2>
    <div class="printTableViewport">
      <table class="reportExpensesTable">
        <thead><tr><th>תאריך</th><th>תקציב</th><th>קטגוריה</th><th>פירוט</th><th>סכום</th><th>מטבע/תשלום</th><th>סה״כ בשקלים</th><th>שער</th></tr></thead>
        <tbody>${rows || `<tr><td colspan="8">אין הוצאות להצגה</td></tr>`}</tbody>
        <tfoot><tr><td colspan="6">סה״כ הוצאות</td><td>${fmtILS(reportTotal)}</td><td></td></tr></tfoot>
      </table>
    </div>
  </section>
</div>`;
}

window.closePdfPreview = function(){
  const overlay = document.getElementById("pdfPreviewOverlay");
  if(overlay) overlay.remove();
  const iframe = document.getElementById("pdfPrintFrame");
  if(iframe) iframe.remove();
  document.body.classList.remove("pdfPreviewOpen");
};


window.printPdfPreview = function(){
  try{
    const reportHtml = `<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>דוח תקציב</title>
<style>
*{box-sizing:border-box}
@page{size:A4 portrait;margin:10mm}
html,body{background:#fff!important;color:#111827!important;margin:0}
body{font-family:Arial,"Noto Sans Hebrew",sans-serif;direction:rtl;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact}
h1,h2{text-align:center;margin:8px 0}
.meta{text-align:center;color:#475569;margin-bottom:14px}
.summary{border:1px solid #d7dce5;border-radius:12px;padding:10px;margin:10px 0;line-height:1.55;font-size:11px}
.reportTableSection{margin-top:14px}
.reportTableSection.fitOnePage{break-inside:avoid-page;page-break-inside:avoid}
.reportTableTitle{margin:0 0 8px;break-after:avoid-page;page-break-after:avoid}
.printTableViewport{overflow:visible;max-height:none}
table{width:100%;border-collapse:collapse;font-size:9px;table-layout:fixed;break-before:avoid-page;page-break-before:avoid}
thead{display:table-header-group}
tfoot{display:table-row-group}
tr{break-inside:avoid-page;page-break-inside:avoid}
th,td{border:1px solid #d7dce5;padding:4px 3px;text-align:center;vertical-align:middle;word-break:break-word;overflow-wrap:anywhere}
th{background:#0b84f3!important;color:white!important;font-weight:900}
tbody tr:nth-child(even) td{background:#fafafa!important}
tfoot td{background:#eef6ff!important;font-weight:900}
.expenseSubLine{display:block;font-size:7.5px;line-height:1.2;color:#475569;font-weight:700}
th:nth-child(1),td:nth-child(1){width:8%}
th:nth-child(2),td:nth-child(2){width:12%}
th:nth-child(3),td:nth-child(3){width:12%}
th:nth-child(4),td:nth-child(4){width:22%}
th:nth-child(5),td:nth-child(5){width:9%}
th:nth-child(6),td:nth-child(6){width:12%}
th:nth-child(7),td:nth-child(7){width:17%}
th:nth-child(8),td:nth-child(8){width:8%}
</style>
</head>
<body>${buildPrintableBudgetReport()}</body>
</html>`;

    let iframe = document.getElementById("pdfPrintFrame");
    if(iframe) iframe.remove();

    iframe = document.createElement("iframe");
    iframe.id = "pdfPrintFrame";
    iframe.setAttribute("title", "דוח תקציב להדפסה");
    iframe.style.position = "fixed";
    iframe.style.left = "-2400px";
    iframe.style.bottom = "0";
    iframe.style.width = "794px";
    iframe.style.height = "1123px";
    iframe.style.opacity = "0";
    iframe.style.border = "0";
    iframe.style.pointerEvents = "none";
    document.body.appendChild(iframe);

    const printDoc = iframe.contentWindow.document;
    printDoc.open();
    printDoc.write(reportHtml);
    printDoc.close();

    const doPrint = () => {
      try{
        iframe.contentWindow.focus();
        iframe.contentWindow.print();
      }catch(err){
        console.error("iframe print failed", err);
        alert("ההדפסה נכשלה. נסה לפתוח את הדוח שוב או להשתמש בייצוא HTML.");
      }
    };
    if(printDoc.readyState === "complete") setTimeout(doPrint, 250);
    else iframe.addEventListener("load", () => setTimeout(doPrint, 250), {once:true});
  }catch(err){
    console.error("printPdfPreview failed", err);
    alert("ההדפסה נכשלה. נסה שוב.");
  }
};


window.exportPDF = function(){
  try{
    const old = document.getElementById("pdfPreviewOverlay");
    if(old) old.remove();

    const overlay = document.createElement("div");
    overlay.id = "pdfPreviewOverlay";
    overlay.innerHTML = `
      <div class="pdfPreviewToolbar noPrint">
        <button type="button" onclick="closePdfPreview()">חזרה לאפליקציה</button>
        <button type="button" class="export" onclick="printPdfPreview()">🖨️ הדפסה / שמירה כ־PDF</button>
      </div>
      <div class="pdfPreviewContent">
        ${buildPrintableBudgetReport()}
      </div>
    `;
    document.body.appendChild(overlay);
    document.body.classList.add("pdfPreviewOpen");
    setTimeout(() => window.scrollTo(0, 0), 50);
  }catch(err){
    console.error("exportPDF failed", err);
    alert("פתיחת תצוגת PDF נכשלה. אפשר להשתמש בייצוא HTML כחלופה.");
  }
};





applySavedTheme();
["amount","currency","paymentMethod","budgetOwner"].forEach(id => {
  const el = document.getElementById(id);
  if(el){
    el.addEventListener("input", renderExpenseFeePreview);
    el.addEventListener("change", renderExpenseFeePreview);
  }
});
["authEmail","authPassword"].forEach(id => {
  document.getElementById(id)?.addEventListener("keydown", event => {
    if(event.key === "Enter") window.loginBudgetApp();
  });
});
const versionBadge = document.getElementById("budgetAppVersionBadge");
if(versionBadge) versionBadge.innerText = "גרסת GOLD Audit · " + BUDGET_APP_VERSION;
setTimeout(renderExpenseFeePreview, 80);
setTimeout(initAppTabs, 50);

async function forceFirebaseServerRefresh(reason = "כניסה לאפליקציה"){
  if(cloudRefreshPromise) return cloudRefreshPromise;
  cloudRefreshPromise = (async () => {
    const authEmailAtStart = String(auth.currentUser?.email || "").toLowerCase();
    try{
      setSyncStatus("pending", "מרענן נתונים מהענן...", "מבצע קריאה ישירה מהענן כדי לוודא שהמידע מעודכן.");

      const [ratesSnap, tripsSnap, expensesSnap] = await withTimeout(Promise.all([
        getDocFromServer(doc(db, "settings", "rates")),
        getDocsFromServer(collection(db, "trips")),
        getDocsFromServer(collection(db, "expenses"))
      ]), 15000, "רענון הנתונים נמשך זמן רב מדי");

      if(!auth.currentUser || String(auth.currentUser.email || "").toLowerCase() !== authEmailAtStart) return;
      if(ratesSnap.exists()) rates = normalizeRatesData(ratesSnap.data(), rates);

      trips = [];
      tripsSnap.forEach(d => trips.push({id:d.id, ...d.data()}));
      if(!trips.find(t => t.id === defaultTrip.id)) trips.push(defaultTrip);

      expenses = [];
      expensesSnap.forEach(d => expenses.push({id:d.id, ...d.data()}));

      renderAll();
      setSyncStatus("online", "מחובר ומסונכרן", "הנתונים רועננו מהענן: " + reason);
      schedulePendingWriteConfirmation();
    }catch(err){
      console.error("forceFirebaseServerRefresh failed", err);
      setSyncStatus("pending", "הרענון הישיר נכשל", "ממשיך לעבוד עם הנתונים המקומיים והסנכרון הרגיל. בדוק חיבור אינטרנט ונסה שוב.");
    }
  })();
  try{
    return await cloudRefreshPromise;
  }finally{
    cloudRefreshPromise = null;
  }
}

window.forceFirebaseServerRefresh = forceFirebaseServerRefresh;

function handleSnapshotError(context, err){
  console.error(context + " snapshot failed", err);
  setSyncStatus("pending", "הסנכרון הושהה", "לא ניתן לעדכן כרגע את " + context + ". הנתונים המקומיים נשמרו וייעשה ניסיון נוסף כשיחזור החיבור.");
}

async function startBudgetAppData(sessionId){
  clearDataListeners();
  await withTimeout(ensureDefaultSettings(), 15000, "בדיקת הגדרות ברירת המחדל נמשכה זמן רב מדי");
  if(sessionId !== dataSessionId || !auth.currentUser) return;
  await forceFirebaseServerRefresh("כניסה לאפליקציה");
  if(sessionId !== dataSessionId || !auth.currentUser) return;

  dataUnsubscribers.push(onSnapshot(doc(db, "settings", "rates"), async (snap) => {
    if(snap.exists()) rates = normalizeRatesData(snap.data(), rates);
    scheduleRenderAll();
    await freezeLegacyExpensesOnce();
  }, err => handleSnapshotError("שערי המטבע", err)));

  dataUnsubscribers.push(onSnapshot(collection(db, "trips"), (snap) => {
    trips = [];
    snap.forEach(d => trips.push({id:d.id, ...d.data()}));
    if(!trips.find(t => t.id === defaultTrip.id)) trips.push(defaultTrip);
    scheduleRenderAll();
  }, err => handleSnapshotError("רשימת הטיולים", err)));

  dataUnsubscribers.push(onSnapshot(collection(db, "expenses"), (snap) => {
    expenses = [];
    snap.forEach(d => expenses.push({id:d.id, ...d.data()}));
    scheduleRenderAll();
  }, err => handleSnapshotError("ההוצאות", err)));
}
</script>

<style>


.budgetAppVersionBadge{
  display:inline-block;
  margin-top:12px;
  padding:7px 12px;
  border-radius:999px;
  background:rgba(255,255,255,.16);
  border:1px solid rgba(255,255,255,.35);
  color:#fff;
  font-size:12px;
  font-weight:900;
  direction:ltr;
  unicode-bidi:isolate;
}
*{box-sizing:border-box}
html{direction:rtl}
body{
  font-family:Arial,"Noto Sans Hebrew",sans-serif;
  background:#f4f7fb;
  color:#111827;
  padding:15px;
  margin:0;
}
.container{
  max-width:980px;
  margin:auto;
  background:#fff;
  padding:18px;
  border-radius:18px;
  box-shadow:0 2px 14px rgba(15,23,42,.10);
}
.card{
  background:#eef6ff;
  border:1px solid #dbeafe;
  border-radius:16px;
  padding:14px;
  margin-top:14px;
  line-height:1.7;
}
h1,h2,h3{margin-top:0}
input,select,button{
  width:100%;
  box-sizing:border-box;
  padding:13px;
  margin-top:10px;
  border-radius:10px;
  border:1px solid #cbd5e1;
  font-size:16px;
}
button{
  background:#2563eb;
  color:white;
  border:none;
  font-weight:900;
  cursor:pointer;
}
button:disabled{opacity:.55;cursor:not-allowed}
@media(hover:hover){button:not(:disabled):hover{filter:brightness(.96);transform:translateY(-1px)}}
:is(button,input,select):focus-visible{outline:3px solid rgba(37,99,235,.35);outline-offset:2px}
button.export{background:#2e7d32}
button.orange{background:#c2410c}
button.danger{background:#c62828}
button.secondaryBtn,.secondaryBtn{background:#475569}
.smallBtn{padding:6px;margin:0;font-size:12px}
.small{font-size:13px;color:#555}
.expenseSubLine{display:block;font-size:11px;line-height:1.25;color:#475569;font-weight:800;white-space:normal}
.fxFeePreview{
  margin-top:10px;
  padding:10px 12px;
  border-radius:14px;
  background:#f8fafc;
  border:1px solid #dbeafe;
  color:#334155;
  font-size:14px;
  line-height:1.45;
  font-weight:800;
}
.fxFeePreview.feeActive{background:#fff7ed;border-color:#fed7aa;color:#7c2d12}

.rowActions{display:flex;gap:6px;justify-content:center;align-items:center;flex-wrap:wrap}
.rowActions .smallBtn{width:auto;min-width:54px}
label{display:block;margin-top:10px;color:#1f2937}
#expenseCancelEditButton{margin-top:8px}
.red{color:#b71c1c;font-weight:900}
.green{color:#1b5e20;font-weight:900}
.red-cell{background:#ffcdd2;color:#b71c1c;font-weight:900}
.noPrint{}

.authOverlay{
  position:fixed;
  inset:0;
  z-index:999999;
  background:linear-gradient(135deg,#0f172a,#1e293b,#111827);
  display:flex;
  align-items:center;
  justify-content:center;
  padding:18px;
  direction:rtl;
}
.authCard{
  width:100%;
  max-width:460px;
  background:#fff;
  border-radius:26px;
  padding:24px;
  box-shadow:0 22px 60px rgba(0,0,0,.32);
}
.authCard h1{margin:0 0 8px;font-size:30px;text-align:center}
.authCard p{color:#475569;text-align:center;margin:0 0 18px;font-weight:700}
.authCard label{font-weight:900;margin-top:10px;display:block}
.authCard input{direction:ltr;text-align:left}
.authCard button{background:linear-gradient(135deg,#2563eb,#06b6d4)}
.authCard .authSecondary{background:#475569}
.authMessage{margin-top:12px;text-align:center;font-weight:900;color:#991b1b;min-height:24px}
.authHint{
  margin-top:14px;
  background:#f8fafc;
  border:1px solid #e2e8f0;
  border-radius:14px;
  padding:12px;
  font-size:14px;
  color:#475569;
  line-height:1.45;
}
body.authLocked .container,
body.authLocked #floatingBottomButton,
body.authLocked #force-cloud-refresh-btn{display:none!important}

.authTopBar,.syncStatusBar{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:10px;
  border-radius:16px;
  padding:10px 12px;
  margin:10px 0 14px;
  font-size:14px;
  font-weight:900;
}
.authTopBar{background:#ecfeff;border:1px solid #a5f3fc;color:#155e75}
.authTopBar button{width:auto;padding:8px 12px;border-radius:12px;background:#0f766e}
.syncStatusBar{background:#f8fafc;border:1px solid #cbd5e1;color:#334155}
.syncStatusBar .dot{width:11px;height:11px;border-radius:999px;display:inline-block;margin-left:7px;background:#64748b}
.syncStatusBar.online .dot{background:#16a34a}
.syncStatusBar.offline .dot{background:#f97316}
.syncStatusBar.pending .dot{background:#dc2626}
.syncStatusBar small{display:block;font-size:12px;color:#64748b;font-weight:700;margin-top:2px}

.proHeader{
  padding:18px 16px;
  border-radius:28px;
  background:linear-gradient(135deg,#0f172a,#1e3a8a 58%,#0891b2);
  color:#fff;
  margin:12px 0 14px;
  box-shadow:0 16px 40px rgba(15,23,42,.28);
}
.proHeader h1{font-size:28px;margin:0;color:#fff}
.proHeaderSub{margin-top:8px;font-weight:800;opacity:.9;line-height:1.35}
.featureVerificationBox{margin-bottom:12px;padding:10px 12px;border-radius:12px;background:#ecfdf5;border:1px solid #86efac;color:#166534;font-size:13px;font-weight:900;line-height:1.45}

.appTabs{
  position:sticky;
  top:0;
  z-index:999;
  display:flex;
  gap:8px;
  overflow-x:auto;
  padding:12px 6px;
  margin:10px 0 16px;
  background:rgba(248,250,252,.96);
  backdrop-filter:blur(10px);
  border:1px solid #dbeafe;
  border-radius:18px;
  -webkit-overflow-scrolling:touch;
}
.appTabs button{
  width:auto;
  min-width:max-content;
  flex:0 0 auto;
  border-radius:999px;
  padding:12px 18px;
  font-size:16px;
  background:#e2e8f0;
  color:#0f172a;
  margin:0;
  box-shadow:none;
}
.appTabs button.active{
  background:linear-gradient(135deg,#2563eb,#06b6d4);
  color:#fff;
}

.tabPanel{display:none!important}
.tabPanel.active{display:block!important}
body.tab-main #tabMain,
body.tab-summary #tabSummary,
body.tab-charts #tabCharts,
body.tab-compare #tabCompare,
body.tab-settings #tabSettings{display:block!important}
body.tab-main #tabSummary,body.tab-main #tabCharts,body.tab-main #tabCompare,body.tab-main #tabSettings,
body.tab-summary #tabMain,body.tab-summary #tabCharts,body.tab-summary #tabCompare,body.tab-summary #tabSettings,
body.tab-charts #tabMain,body.tab-charts #tabSummary,body.tab-charts #tabCompare,body.tab-charts #tabSettings,
body.tab-compare #tabMain,body.tab-compare #tabSummary,body.tab-compare #tabCharts,body.tab-compare #tabSettings,
body.tab-settings #tabMain,body.tab-settings #tabSummary,body.tab-settings #tabCharts,body.tab-settings #tabCompare{display:none!important}

.homeKpiGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:12px 0}
.homeKpi{
  background:#fff;
  border:1px solid #e5e7eb;
  border-radius:20px;
  padding:14px;
  box-shadow:0 8px 22px rgba(15,23,42,.06);
}
.homeKpiTitle{font-size:13px;color:#64748b;font-weight:900;margin-bottom:8px}
.homeKpiValue{font-size:22px;font-weight:1000;color:#0f172a}
.homeKpiSub{font-size:12px;color:#64748b;margin-top:6px;line-height:1.35;font-weight:700}
.homeQuickActions{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px}
.homeQuickActions button{border-radius:18px;padding:14px 12px;font-weight:1000;font-size:15px;background:#e0f2fe;color:#075985}
.homeQuickActions button.primary{background:linear-gradient(135deg,#2563eb,#06b6d4);color:#fff}
.homeMiniNote{
  margin-top:12px;
  padding:12px;
  border-radius:16px;
  background:#f8fafc;
  border:1px solid #e2e8f0;
  color:#475569;
  font-size:13px;
  font-weight:800;
  line-height:1.45;
}

table{width:100%;border-collapse:collapse;margin-top:12px;background:white;font-size:13px}
th,td{border:1px solid #ddd;padding:8px;text-align:center}
th{background:#2563eb;color:white}
.tableWrap{overflow-x:auto}


.expensesLedgerWrap{
  width:100%;
  max-height:520px;
  overflow:auto;
  -webkit-overflow-scrolling:touch;
  margin-top:14px;
  background:#fff;
  border:1px solid #d7dce5;
}
#expensesTable.expensesLedgerTable{
  width:max-content;
  min-width:100%;
  max-width:none;
  table-layout:auto;
  border-collapse:collapse;
  direction:rtl;
  background:#fff;
  color:#111827;
  font-size:clamp(9px,2.45vw,13px);
}
#expensesTable.expensesLedgerTable :is(thead,tbody,tr,th,td){display:revert}
#expensesTable.expensesLedgerTable :is(th,td){
  border:1px solid #d7dce5;
  padding:6px 4px;
  text-align:center;
  vertical-align:middle;
  line-height:1.18;
  white-space:nowrap;
  word-break:normal;
  overflow-wrap:normal;
}
#expensesTable.expensesLedgerTable thead th{
  background:#2563eb;
  color:#fff;
  font-weight:900;
  position:sticky;
  top:0;
  z-index:6;
}
#expensesTable.expensesLedgerTable tbody tr:nth-child(odd) td{background:#fff}
#expensesTable.expensesLedgerTable tbody tr:nth-child(even) td{background:#fafafa}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(1){min-width:54px}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(2){min-width:64px;font-weight:800}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(3){min-width:120px}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(4){min-width:58px}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(5){min-width:38px}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(6){min-width:70px;font-weight:900}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(7){min-width:48px}
#expensesTable.expensesLedgerTable :is(th,td):nth-child(8){min-width:42px}
#expensesTable.expensesLedgerTable td::before{content:none!important;display:none!important}
#expensesTable.expensesLedgerTable .danger.smallBtn{
  background:#d32027;
  color:#fff;
  border:none;
  border-radius:10px;
  width:100%;
  max-width:42px;
  height:28px;
  padding:0;
  font-size:12px;
  line-height:28px;
  font-weight:900;
}

.dashboardGrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:14px}
.dashCard{
  background:#fff;
  border:1px solid #dbeafe;
  border-radius:16px;
  padding:14px;
  box-shadow:0 2px 8px rgba(0,0,0,.06);
}
.dashTitle{font-size:13px;color:#475569;font-weight:bold}
.dashValue{font-size:22px;font-weight:900;margin-top:4px}
.statusGood{color:#1b5e20}
.statusWarn{color:#ef6c00}
.statusBad{color:#b71c1c}
.progressWrap{width:100%;height:22px;background:#e5e7eb;border-radius:999px;overflow:hidden;margin-top:10px;border:1px solid #cbd5e1}
.progressBar{height:100%;width:0%;background:#16a34a;transition:width .25s ease,background .25s ease}
.dashboardNote{margin-top:8px;font-size:14px;color:#475569}
.chartGrid{display:grid;grid-template-columns:1fr;gap:14px;margin-top:12px}
.chartBox{background:#fff;border:1px solid #e5e7eb;border-radius:16px;padding:14px}
.chartCanvas{width:100%;max-width:100%;height:220px}
.legendItem{display:flex;align-items:center;gap:8px;margin:5px 0;font-size:14px}
.legendColor{width:14px;height:14px;border-radius:4px;display:inline-block;flex:0 0 auto}

.filterPanel,.compareControls{
  background:#f8fafc;
  border:1px solid #dbeafe;
  border-radius:14px;
  padding:12px;
  margin-top:12px;
}
.categoryTotalsGrid{display:grid;grid-template-columns:1fr;gap:8px;margin-top:10px}
.categoryTotalItem{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:10px;font-weight:800}
.dateChips{display:flex;flex-wrap:wrap;gap:7px;margin-top:10px}
.dateChip{
  width:auto;
  padding:8px 10px;
  border-radius:999px;
  background:#e0f2fe;
  color:#075985;
  border:1px solid #7dd3fc;
  font-size:13px;
  font-weight:800;
}
.dateChip.active{background:#2563eb;color:white;border-color:#2563eb}
.tripCheckGrid{display:grid;grid-template-columns:1fr;gap:8px;margin-top:8px}
.tripCheckItem{
  background:#fff;
  border:1px solid #e5e7eb;
  border-radius:12px;
  padding:10px;
  display:flex;
  align-items:center;
  gap:8px;
  font-weight:800;
}
.tripCheckItem input{width:auto;margin:0}
.compareResultCard{margin-top:12px;background:#fff;border:1px solid #e5e7eb;border-radius:14px;padding:12px}
.collapsiblePanel{display:none;margin-top:12px}
.collapsiblePanel.open{display:block}
.tripActions{display:flex;gap:8px;flex-wrap:wrap}
.tripActions button{flex:1 1 160px}

.tripBudgetSummaryBox{
  margin-top:14px;
  padding:16px;
  border-radius:18px;
  background:linear-gradient(135deg,#eff6ff 0%,#f8fafc 100%);
  border:1px solid #bfdbfe;
}
.tripBudgetSummaryGrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}
.tripBudgetCard{background:white;border-radius:16px;padding:14px;border:1px solid #dbeafe}
.tripBudgetCard h4{margin:0 0 10px;font-size:16px}
.tripBudgetMain{font-size:22px;font-weight:900;margin-bottom:6px}
.tripBudgetSub{font-size:13px;color:#475569;line-height:1.45}
.tripBudgetGood{color:#15803d}
.tripBudgetWarn{color:#d97706}
.tripBudgetBad{color:#dc2626}

.ratesHeadline{
  background:#fff8e1;
  border:2px solid #ffca28;
  border-radius:12px;
  padding:12px;
  margin-top:10px;
  font-size:17px;
}
.rateLine{
  display:flex;
  justify-content:space-between;
  align-items:center;
  gap:10px;
  direction:rtl;
  border-bottom:1px solid rgba(0,0,0,.08);
  padding:6px 0;
}
.rateLine:last-child{border-bottom:0}
.rateLine span:first-child{font-weight:700}
.rateLine b{white-space:nowrap}
.rateInputWrap{margin-top:12px}
.rateInputWrap label{display:block;font-weight:900;margin:10px 0 4px}
.budgetSourceRow{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,.8fr) minmax(115px,.8fr) auto;gap:8px;align-items:end;margin-top:8px}
.budgetSourceRow input,.budgetSourceRow select,.budgetSourceRow button{margin-top:0}
.budgetSourceRow .budgetSourceRemove{width:auto;min-width:62px;height:48px}
.budgetSourceRow .budgetSourceRemove:disabled{opacity:.45;cursor:not-allowed}
@media(max-width:520px){.budgetSourceRow{grid-template-columns:1fr 1fr}.budgetSourceRow .budgetSourceHolding,.budgetSourceRow .budgetSourceRemove{grid-column:1/-1;width:100%}}

.appCreatorFooter{
  margin-top:40px;
  padding:18px;
  border-top:1px solid #dbeafe;
  text-align:center;
  font-size:14px;
  color:#475569;
  opacity:.95;
}
.appCreatorFooter .creatorName{font-weight:800;font-size:16px;margin-bottom:6px}

#force-cloud-refresh-btn{
  position:fixed;
  left:14px;
  bottom:calc(126px + env(safe-area-inset-bottom,0px));
  z-index:2147483647;
  min-width:118px;
  min-height:42px;
  padding:10px 14px;
  border:1px solid rgba(255,255,255,.35);
  border-radius:999px;
  background:linear-gradient(135deg,#0f766e,#2563eb);
  color:#fff;
  font-size:14px;
  font-weight:900;
  box-shadow:0 6px 18px rgba(0,0,0,.28);
}

#pdfPreviewOverlay{
  position:fixed;
  inset:0;
  z-index:2147483647;
  display:flex;
  flex-direction:column;
  background:#f8fafc;
  color:#111827;
  direction:rtl;
  overflow:hidden;
  height:100vh;
}
body.pdfPreviewOpen{
  overflow:hidden;
  height:100vh;
}
.pdfPreviewToolbar{
  flex:0 0 auto;
  display:flex;
  gap:10px;
  padding:12px;
  background:rgba(248,250,252,.96);
  border-bottom:1px solid #d7dce5;
  backdrop-filter:blur(10px);
  z-index:10;
}
.pdfPreviewToolbar button{
  width:auto;
  flex:1;
  margin:0;
  border-radius:14px;
  padding:12px;
  font-weight:900;
}
.pdfPreviewContent{
  flex:1 1 auto;
  min-height:0;
  overflow:auto;
  -webkit-overflow-scrolling:touch;
  padding:18px;
}
.printReport{
  max-width:980px;
  margin:0 auto;
  background:#fff;
  border:1px solid #d7dce5;
  border-radius:16px;
  padding:18px;
}
.printReport h1,.printReport h2{text-align:center;margin:10px 0}
.printReport .meta{text-align:center;color:#475569;margin-bottom:18px}
.printReport .summary{border:1px solid #d7dce5;border-radius:14px;padding:14px;margin:14px 0;line-height:1.8}
.reportTableSection{margin-top:18px}
.reportTableTitle{margin:0 0 10px!important;break-after:avoid-page;page-break-after:avoid}
.printTableViewport{max-width:100%;overflow:auto;border:1px solid #d7dce5;border-radius:12px;-webkit-overflow-scrolling:touch}
.printReport table{width:100%;min-width:760px;border-collapse:collapse;margin:0;font-size:12px;background:#fff}
.printReport thead th{position:sticky;top:0;z-index:2}
.printReport th,.printReport td{border:1px solid #d7dce5;padding:8px;text-align:center;vertical-align:middle;color:#111827;overflow-wrap:anywhere}
.printReport th{background:#0b84f3!important;color:#fff!important}
.printReport tbody tr:nth-child(even) td{background:#fafafa}
.printReport tfoot td{background:#eef6ff;font-weight:900}
body.pdfPreviewOpen .container,
body.pdfPreviewOpen #authOverlay,
body.pdfPreviewOpen #force-cloud-refresh-btn{display:none!important}
#pdfPrintFrame{display:block!important}

body.darkMode{
  background:#0f172a;
  color:#e5e7eb;
}
body.darkMode .container,
body.darkMode .card,
body.darkMode .chartBox,
body.darkMode .dashCard,
body.darkMode .categoryTotalItem,
body.darkMode .compareResultCard,
body.darkMode .tripCheckItem{
  background:#111827;
  color:#e5e7eb;
  border-color:#374151;
}
body.darkMode input,body.darkMode select{
  background:#0f172a;
  color:#e5e7eb;
  border-color:#475569;
}
body.darkMode .appTabs{background:rgba(15,23,42,.96);border-color:#334155}
body.darkMode .appTabs button{background:#1f2937;color:#e5e7eb}
body.darkMode .appTabs button.active{background:linear-gradient(135deg,#2563eb,#06b6d4);color:#fff}
body.darkMode .homeKpi,
body.darkMode .tripBudgetCard{background:#1f2937;border-color:#374151}
body.darkMode .homeKpiValue{color:#e5e7eb}
body.darkMode .homeKpiTitle,body.darkMode .homeKpiSub,body.darkMode .dashboardNote,body.darkMode .small{color:#cbd5e1}
body.darkMode .expensesLedgerWrap,
body.darkMode #expensesTable.expensesLedgerTable{background:#fff!important;color:#111827!important}
body.darkMode #expensesTable.expensesLedgerTable th,
body.darkMode #expensesTable.expensesLedgerTable td{border-color:#d7dce5!important;color:#111827!important}
body.darkMode #expensesTable.expensesLedgerTable thead th{background:#0b84f3!important;color:#fff!important}


body.darkMode label{color:#e5e7eb}
body.darkMode .filterPanel,
body.darkMode .compareControls,
body.darkMode .tripBudgetSummaryBox,
body.darkMode .homeMiniNote,
body.darkMode .fxFeePreview,
body.darkMode .syncStatusBar,
body.darkMode .featureVerificationBox{
  background:#1f2937;
  color:#e5e7eb;
  border-color:#475569;
}
body.darkMode .ratesHeadline{background:#292524;border-color:#a16207;color:#fef3c7}
body.darkMode .rateLine{border-color:#475569}
body.darkMode .tripBudgetSub,
body.darkMode .expenseSubLine,
body.darkMode .walletBalances .small{color:#cbd5e1}
@media(max-width:700px){
  body.darkMode table.mobileCards tr{background:#1f2937;border-color:#475569;color:#e5e7eb}
  body.darkMode table.mobileCards td::before{color:#cbd5e1}
}
@media(min-width:650px){
  .categoryTotalsGrid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(min-width:700px){
  .tripCheckGrid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(min-width:760px){
  .dashboardGrid{grid-template-columns:repeat(4,minmax(0,1fr))}
  .chartGrid{grid-template-columns:1fr 1fr}
}
@media(max-width:700px){
  body{padding:10px}
  .container{padding:12px;border-radius:14px}
  .homeKpiGrid,.homeQuickActions{grid-template-columns:1fr}
  .proHeader h1{font-size:24px}
  .appTabs button{font-size:14px;padding:10px 14px}
  .chartGrid{grid-template-columns:1fr}
  .chartCanvas{width:100%;max-width:100%}
  table.mobileCards,table.mobileCards thead,table.mobileCards tbody,table.mobileCards tr,table.mobileCards th,table.mobileCards td{display:block;width:100%}
  table.mobileCards thead{display:none}
  table.mobileCards tr{background:rgba(255,255,255,.92);border:1px solid #e5e7eb;border-radius:16px;margin:10px 0;padding:10px;box-shadow:0 4px 12px rgba(15,23,42,.06)}
  table.mobileCards td{border:0!important;padding:8px 4px!important;text-align:right!important;display:flex;justify-content:space-between;gap:12px;align-items:flex-start;white-space:normal!important;word-break:break-word}
  table.mobileCards td::before{content:attr(data-label);font-weight:900;color:#64748b;min-width:100px;flex:0 0 42%}
}
@media(max-width:430px){
  .expensesLedgerWrap{max-height:500px;margin-left:-10px;margin-right:-10px}
  #expensesTable.expensesLedgerTable{font-size:9px}
  #expensesTable.expensesLedgerTable th,#expensesTable.expensesLedgerTable td{padding:5px 2px}
}
@page{size:A4 portrait;margin:10mm}
@media print{
  *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
  .noPrint,.pdfPreviewToolbar,#force-cloud-refresh-btn{display:none!important}
  body.pdfPreviewOpen{overflow:visible!important;height:auto!important;background:#fff!important}
  body.pdfPreviewOpen #pdfPreviewOverlay{position:static!important;display:block!important;height:auto!important;overflow:visible!important;background:#fff!important}
  body.pdfPreviewOpen .pdfPreviewContent{overflow:visible!important;padding:0!important}
  body.pdfPreviewOpen .printReport{max-width:none!important;border:0!important;border-radius:0!important;padding:0!important}
  body.pdfPreviewOpen .printTableViewport{overflow:visible!important;border:0!important;border-radius:0!important}
  body.pdfPreviewOpen .reportTableSection.fitOnePage{break-inside:avoid-page!important;page-break-inside:avoid!important}
  body.pdfPreviewOpen .reportTableTitle{break-after:avoid-page!important;page-break-after:avoid!important}
  body.pdfPreviewOpen .reportExpensesTable{min-width:0!important;width:100%!important;table-layout:fixed!important;font-size:9px!important;break-before:avoid-page!important;page-break-before:avoid!important}
  body.pdfPreviewOpen .reportExpensesTable thead{display:table-header-group!important}
  body.pdfPreviewOpen .reportExpensesTable tfoot{display:table-row-group!important}
  body.pdfPreviewOpen .reportExpensesTable tr{break-inside:avoid-page!important;page-break-inside:avoid!important}
  body.pdfPreviewOpen .reportExpensesTable th,body.pdfPreviewOpen .reportExpensesTable td{padding:4px 3px!important;word-break:break-word!important;overflow-wrap:anywhere!important}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(1),body.pdfPreviewOpen .reportExpensesTable td:nth-child(1){width:8%}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(2),body.pdfPreviewOpen .reportExpensesTable td:nth-child(2){width:12%}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(3),body.pdfPreviewOpen .reportExpensesTable td:nth-child(3){width:12%}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(4),body.pdfPreviewOpen .reportExpensesTable td:nth-child(4){width:22%}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(5),body.pdfPreviewOpen .reportExpensesTable td:nth-child(5){width:9%}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(6),body.pdfPreviewOpen .reportExpensesTable td:nth-child(6){width:12%}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(7),body.pdfPreviewOpen .reportExpensesTable td:nth-child(7){width:17%}
  body.pdfPreviewOpen .reportExpensesTable th:nth-child(8),body.pdfPreviewOpen .reportExpensesTable td:nth-child(8){width:8%}
  .container{box-shadow:none}
}



.tabPanel,.card[id$="Anchor"]{scroll-margin-top:92px}
</style>

<script>
(function(){
  function showTabVisual(tabId, shouldScroll){
    const map = {
      tabMain: "tab-main",
      tabSummary: "tab-summary",
      tabCharts: "tab-charts",
      tabCompare: "tab-compare",
      tabSettings: "tab-settings"
    };
    const scrollMap = {
      tabMain: "tabMain",
      tabSummary: "summaryAnchor",
      tabCharts: "chartsInfoAnchor",
      tabCompare: "compareAnchor",
      tabSettings: "settingsExportAnchor"
    };
    const cls = map[tabId] || "tab-main";
    document.body.classList.remove("tab-main","tab-summary","tab-charts","tab-compare","tab-settings");
    document.body.classList.add(cls);
    document.querySelectorAll(".tabPanel").forEach(p => p.classList.remove("active"));
    const panel = document.getElementById(tabId);
    if(panel) panel.classList.add("active");
    document.querySelectorAll(".appTabs button").forEach(b => b.classList.remove("active"));
    const btn = document.querySelector(`[data-tab-btn="${tabId}"]`);
    if(btn) btn.classList.add("active");
    try{ localStorage.setItem("activeBudgetTab", tabId); }catch(err){}
    if(shouldScroll !== false){
      const target = document.getElementById(scrollMap[tabId] || tabId) || panel;
      if(target){
        setTimeout(() => {
          try{ target.scrollIntoView({behavior:"smooth", block:"start"}); }
          catch(err){ target.scrollIntoView(true); }
        }, 30);
      }
    }
  }
  if(typeof window.showAppTab !== "function"){
    window.showAppTab = showTabVisual;
  }
  window.addEventListener("DOMContentLoaded", function(){
    if(typeof window.showAppTab !== "function") window.showAppTab = showTabVisual;
    const saved = localStorage.getItem("activeBudgetTab") || "tabMain";
    showTabVisual(saved, false);
  });
})();
</script>

</head>
<body class="authLocked tab-main">
<div id="authOverlay" class="authOverlay">
  <div class="authCard">
    <h1>🔐 כניסה מאובטחת</h1>
    <p>אפליקציית ניהול תקציב החופשה</p>
    <label for="authEmail">אימייל</label>
    <input id="authEmail" type="email" autocomplete="username" placeholder="example@gmail.com">
    <label for="authPassword">סיסמה</label>
    <input id="authPassword" type="password" autocomplete="current-password" placeholder="הכנס סיסמה">
    <button onclick="loginBudgetApp()">כניסה לאפליקציה</button>
    <button class="authSecondary" onclick="createBudgetUser()">יצירת משתמש מורשה ראשון</button>
    <div id="authMessage" class="authMessage"></div>
    <div class="authHint">ניתן לשמור את הסיסמה באייפון ולהשתמש ב־Face ID למילוי מהיר.</div>
  </div>
</div>
<div class="container">
<div id="authTopBar" class="authTopBar" style="display:none;">
  <span id="authUserLabel">מחובר</span>
  <button onclick="logoutBudgetApp()">התנתקות</button>
</div>
<div id="syncStatusBar" class="syncStatusBar offline" role="status" aria-live="polite">
  <div>
    <span class="dot"></span><span id="syncStatusText">בודק חיבור וסנכרון...</span>
    <small id="syncStatusSub">שינויים שתזין אופליין יישמרו מקומית ויסונכרנו כשיחזור אינטרנט.</small>
  </div>
</div>
<div class="proHeader">
  <div class="proHeaderTop">
    <h1>ניהול תקציב חופשה</h1>
  </div>
  <div class="proHeaderSub">תקציב חי, הוצאות, סיכומים והשוואת טיולים</div>
  <div id="budgetAppVersionBadge" class="budgetAppVersionBadge">גרסת GOLD Audit · 2026.07.17-GOLD-AUDIT-PDF-COMPAT-v11</div>
</div>
<div class="appTabs noPrint">
  <button data-tab-btn="tabMain" onclick="showAppTab('tabMain')">🏠 ראשי</button>
  <button data-tab-btn="tabSummary" onclick="showAppTab('tabSummary')">💰 סיכומים</button>
  <button data-tab-btn="tabCharts" onclick="showAppTab('tabCharts')">📊 מידע גרפי</button>
  <button data-tab-btn="tabCompare" onclick="showAppTab('tabCompare')">⚖️ השוואה</button>
  <button data-tab-btn="tabSettings" onclick="showAppTab('tabSettings')">⚙️ הגדרות וייצוא</button>
</div>



<div id="tabMain" class="tabPanel active">
<div class="card section-main-home noPrint">
  <h2>🧳 בחירת טיול פעיל</h2>
  <label for="homeTripSelector"><b>טיול פעיל:</b></label>
  <select id="homeTripSelector" onchange="switchTripFromHome()"></select>
</div>

<div class="card section-main-home">
  <h2>🏠 מסך ראשי</h2>
  <div class="homeKpiGrid">
    <div class="homeKpi">
      <div class="homeKpiTitle">טיול פעיל</div>
      <div class="homeKpiValue" id="homeTripName">—</div>
      <div class="homeKpiSub">לפי בחירת הטיול הפעיל</div>
    </div>
    <div class="homeKpi">
      <div class="homeKpiTitle">נותר בתקציב הכולל</div>
      <div class="homeKpiValue" id="homeTotalLeft">—</div>
      <div class="homeKpiSub" id="homeTotalUsed">—</div><div id="homeWalletBalances" class="walletBalances"></div>
    </div>
    <div class="homeKpi">
      <div class="homeKpiTitle">נותר בקניות</div>
      <div class="homeKpiValue" id="homeShoppingLeft">—</div>
      <div class="homeKpiSub" id="homeShoppingUsed">—</div>
    </div>
    <div class="homeKpi">
      <div class="homeKpiTitle">תקציב אוכל יומי</div>
      <div class="homeKpiValue" id="homeFoodBudget">—</div>
      <div class="homeKpiSub" id="homeFoodSub">יעד יומי פעיל</div>
    </div>
    <div class="homeKpi">
      <div class="homeKpiTitle">נותר בתקציב עידו הפרטי</div>
      <div class="homeKpiValue" id="homeIdoLeft">—</div>
      <div class="homeKpiSub" id="homeIdoUsed">—</div>
    </div>
    <div class="homeKpi">
      <div class="homeKpiTitle">נותר בתקציב מיכאל הפרטי</div>
      <div class="homeKpiValue" id="homeMichaelLeft">—</div>
      <div class="homeKpiSub" id="homeMichaelUsed">—</div>
    </div>
  </div>
  <div class="homeQuickActions noPrint">
    <button class="primary" onclick="document.getElementById('quickExpenseCard')?.scrollIntoView({behavior:'smooth'});">➕ הוספת הוצאה</button>
    <button onclick="showAppTab('tabSummary')">💰 סיכום מלא</button>
    <button onclick="showAppTab('tabCharts')">📊 גרפים</button>
    <button onclick="showAppTab('tabCompare')">⚖️ השוואת טיולים</button>
  </div>
  <div class="homeMiniNote">עדכון מהיר של הוצאות ומעקב תקציב בזמן אמת.</div>
</div>

<div id="quickExpenseCard" class="card noPrint section-main">
<h2 id="expenseFormTitle">➕ הוספת הוצאה</h2>
<label for="category"><b>קטגוריה:</b></label>
<select id="category" aria-label="קטגוריה">
<option value="">בחר קטגוריה</option>
<option>כרטיסי סים</option>
<option>אוכל</option>
<option>קניות עידו</option>
<option>קניות מיכאל</option>
<option>ביגוד והנעלה</option>
<option>תחבורה ציבורית</option>
<option>אטרקציות</option>
<option>תשלומים במזומן</option>
<option>מלונות</option>
<option>ביטוח נסיעות</option>
<option>טיסות</option>
<option>השכרת רכב</option>
<option>דלק</option>
<option>טיפים</option>
<option>כביסות</option>
<option>קרוז</option>
<option>שונות</option>
</select>
<label for="note"><b>פירוט:</b></label>
<input id="note" maxlength="300" placeholder="שם מקום / פירוט" autocomplete="off" aria-label="פירוט ההוצאה">
<label for="amount"><b>סכום:</b></label>
<input id="amount" type="number" inputmode="decimal" min="0" step="0.01" placeholder="סכום" aria-label="סכום ההוצאה">
<label for="currency"><b>מטבע:</b></label>
<select id="currency" aria-label="מטבע">
<option value="ILS">₪ שקל</option>
<option value="USD">$ דולר אמריקאי</option>
<option value="JPY">¥ ין</option>
<option value="EUR">€ יורו</option>
<option value="GBP">£ לירה סטרלינג</option>
<option value="CAD">C$ דולר קנדי</option>
</select>
<label for="budgetOwner"><b>מאיזה תקציב ההוצאה?</b></label>
<select id="budgetOwner" aria-label="שיוך תקציב">
<option value="home" selected>תקציב הבית</option>
<option value="ido">תקציב עידו פרטי</option>
<option value="michael">תקציב מיכאל פרטי</option>
</select>
<label for="paymentMethod"><b>אמצעי תשלום:</b></label>
<select id="paymentMethod" aria-label="אמצעי תשלום">
<option value="credit" selected>אשראי</option>
<option value="cash">מזומן</option>
</select>
<div id="fxFeePreview" class="fxFeePreview" role="status" aria-live="polite">הזן סכום כדי לראות מאיזו קופה תחויב ההוצאה והאם תחול עמלת המרה.</div>
<label for="expenseDate"><b>תאריך ההוצאה:</b></label>
<input id="expenseDate" type="date" aria-label="תאריך ההוצאה">
<button id="expenseSubmitButton" onclick="addExpense()">הוסף הוצאה</button>
<button id="expenseCancelEditButton" class="secondaryBtn" style="display:none" onclick="cancelExpenseEdit()">ביטול עריכה</button>
</div>



<div class="card section-main">
<h2 id="mainExpensesAnchor">פירוט כל ההוצאות בטיול הפעיל</h2>

<button id="refreshMainExpensesButton" class="noPrint" onclick="renderExpenses()">רענון טבלת הוצאות</button>
<div class="expensesLedgerWrap">
<table id="expensesTable" class="expensesLedgerTable">
<thead><tr><th>תאריך</th><th>תקציב</th><th>קטגוריה</th><th>פירוט</th><th>סכום</th><th>מטבע</th><th>סה״כ ₪</th><th>שער</th><th>פעולות</th></tr></thead>
<tbody id="expensesBody"></tbody>
</table>
</div>
</div>
</div>

<div id="tabSummary" class="tabPanel">
<div id="summaryAnchor" class="card section-summary">
<h2>💰 סיכום סופי ותקציבים</h2>
<div class="noPrint" style="margin-bottom:12px;">
<label for="summaryCurrency"><b>בחר מטבע להצגת הסיכום:</b></label>
<select id="summaryCurrency" onchange="setSummaryCurrencyFromUI()">
<option value="ILS">₪ שקל ישראלי</option>
<option value="USD">$ דולר אמריקאי</option>
<option value="JPY">¥ ין יפני</option>
<option value="EUR">€ יורו</option>
<option value="GBP">£ לירה סטרלינג</option>
<option value="CAD">C$ דולר קנדי</option>
</select>
</div>
<div class="small">תצוגת הסיכום לפי המטבע שנבחר.</div>
<div id="summaryFinal"></div>
<div class="summaryUnifiedBox"><div id="tripBudgetSummaryBox" class="tripBudgetSummaryBox"></div></div>
</div>

<div class="card section-summary">
<h2>סיכום יומי</h2>
<div class="tableWrap">
<table id="dailyTable" class="mobileCards">
<thead><tr><th>תאריך</th><th>סה״כ יומי</th></tr></thead>
<tbody id="dailyBody"></tbody>
</table>
</div>
</div>




<div class="card noPrint section-summary">
<h2>🔎 סינון וסיכום לפי קטגוריה</h2>
<div class="filterPanel">
<label for="filterType"><b>סוג סינון:</b></label>
<select id="filterType" onchange="setFilterTypeFromUI()">
<option value="category">לפי קטגוריה</option>
<option value="date">לפי תאריך</option>
</select>

<div id="categoryFilterBox">
<label for="categoryFilter"><b>בחר קטגוריה להצגה:</b></label>
<select id="categoryFilter" onchange="setCategoryFilterFromUI()">
<option value="ALL">כל הקטגוריות</option>
</select>
</div>

<div id="dateFilterBox" style="display:none;">
<label for="dateFilter"><b>בחר תאריך פעיל:</b></label>
<select id="dateFilter" onchange="setDateFilterFromUI()">
<option value="ALL">כל התאריכים</option>
</select>
<div id="activeDatesChips" class="dateChips"></div>
</div>

<button onclick="clearCategoryFilter()">נקה סינון</button>
<div id="selectedCategorySummary" class="dashboardNote"></div>
</div>
<h3>סה״כ לפי קטגוריות</h3>

<div id="categoryTotalsGrid" class="categoryTotalsGrid"></div>
</div>

</div>

<div id="tabCharts" class="tabPanel">
<div class="card section-charts">
<h2>📊 דשבורד תקציב מקצועי</h2>

<div class="dashboardGrid">
  <div class="dashCard">
    <div class="dashTitle">סה״כ הוצאות בכל התקציבים</div>
    <div class="dashValue" id="dashTotal">₪0</div>
  </div>
  <div class="dashCard">
    <div class="dashTitle">יתרת תקציב הבית</div>
    <div class="dashValue" id="dashRemaining">₪0</div>
  </div>
  <div class="dashCard">
    <div class="dashTitle">ניצול תקציב</div>
    <div class="dashValue" id="dashPercent">0%</div>
  </div>
  <div class="dashCard">
    <div class="dashTitle">ממוצע יומי</div>
    <div class="dashValue" id="dashAvgDaily">₪0</div>
  </div>
</div>

<div class="progressWrap">
  <div class="progressBar" id="budgetProgressBar"></div>
</div>
<div class="dashboardNote" id="budgetStatusText">אין עדיין נתונים.</div>

<div class="dashboardGrid">
  <div class="dashCard">
    <div class="dashTitle">הוצאות היום</div>
    <div class="dashValue" id="dashToday">₪0</div>
  </div>
  <div class="dashCard">
    <div class="dashTitle">אוכל היום מול יעד יומי</div>
    <div class="dashValue" id="dashFoodToday">₪0</div>
  </div>
  <div class="dashCard">
    <div class="dashTitle">ביגוד, הנעלה, ואלקטרוניקה</div>
    <div class="dashValue" id="dashClothing">₪0</div>
  </div>
  <div class="dashCard">
    <div class="dashTitle">מספר הוצאות</div>
    <div class="dashValue" id="dashCount">0</div>
  </div>
</div>




</div>

<div id="chartsInfoAnchor" class="card section-charts">
<h2>📊 מידע גרפי</h2>
<div class="chartGrid section-charts">
  <div class="chartBox">
    <h3>🍰 פריסת הוצאות לפי קטגוריה</h3>
    <canvas id="categoryPie" class="chartCanvas" width="420" height="240" role="img" aria-label="תרשים התפלגות הוצאות לפי קטגוריה">תרשים התפלגות הוצאות לפי קטגוריה.</canvas>
    <div id="categoryLegend"></div>
  </div>
  <div class="chartBox">
    <h3>📈 הוצאות לפי יום</h3>
    <canvas id="dailyBar" class="chartCanvas" width="420" height="240" role="img" aria-label="תרשים הוצאות לפי יום">תרשים הוצאות לפי יום.</canvas>
  </div>
</div>
</div>
</div>

<div id="tabCompare" class="tabPanel">
<div id="compareAnchor" class="card section-compare">
<h2>⚖️ השוואה בין טיולים</h2>
<div class="compareControls">
<div class="small">בחר טיולים ומדד להשוואה.</div>

<button class="secondaryBtn" onclick="toggleCompareTripsPanel()">🧳 בחירת טיולים להשוואה</button>
<div id="compareTripsPanel" class="collapsiblePanel">
<label><b>סמן טיולים להשוואה:</b></label>
<div id="compareTripsBox" class="tripCheckGrid"></div>
</div>

<label for="compareMetric"><b>בחר מדד להשוואה:</b></label>
<select id="compareMetric" onchange="setCompareMetricFromUI()">
<option value="TOTAL">סה״כ הוצאות</option>
<option value="DATE">תאריך מסוים</option>
<option value="MOST_EXPENSIVE_DAY">יום הכי יקר</option>
<option value="CHEAPEST_DAY">יום הכי זול</option>
<option value="CAT::כרטיסי סים">כרטיסי סים</option>
<option value="CAT::אוכל">אוכל</option>
<option value="CAT::קניות עידו">קניות עידו</option>
<option value="CAT::קניות מיכאל">קניות מיכאל</option>
<option value="CAT::ביגוד והנעלה">ביגוד והנעלה</option>
<option value="CAT::תחבורה ציבורית">תחבורה ציבורית</option>
<option value="CAT::אטרקציות">אטרקציות</option>
<option value="CAT::תשלומים במזומן">תשלומים במזומן</option>
<option value="CAT::מלונות">מלונות</option>
<option value="CAT::ביטוח נסיעות">ביטוח נסיעות</option>
<option value="CAT::טיסות">טיסות</option>
<option value="CAT::השכרת רכב">השכרת רכב</option>
<option value="CAT::דלק">דלק</option>
<option value="CAT::טיפים">טיפים</option>
<option value="CAT::כביסות">כביסות</option>
<option value="CAT::קרוז">קרוז</option>
<option value="CAT::שונות">שונות</option>
</select>

<div id="compareDateBox" style="display:none;">
<label for="compareDate"><b>בחר תאריך פעיל:</b></label>
<select id="compareDate" onchange="setCompareDateFromUI()"></select>
</div>

<div id="compareStatus" class="dashboardNote"></div>
</div>

<div class="compareResultCard">
<div class="tableWrap">
<table class="compareMiniTable mobileCards">
<thead id="tripComparisonHead"></thead>
<tbody id="tripComparisonBody"></tbody>
</table>
</div>
</div>
</div>

</div>

<div id="tabSettings" class="tabPanel">
<div id="settingsExportAnchor" class="card section-settings">
<h2>⚙️ הגדרות וייצוא</h2>
<h3>🧳 ניהול טיולים</h3>
<div class="small">הטיול הראשון מוגדר כ־<b>יפן 2026</b>. ניתן ליצור טיולים נוספים ולבחור ביניהם.</div>


<div id="activeTripLabel" class="dashboardNote">טיול פעיל: <b>יפן 2026</b></div>

<label for="tripSelector"><b>בחר טיול פעיל:</b></label>
<select id="tripSelector" onchange="switchTrip()"></select>

<div class="tripActions noPrint">
<button onclick="createNewTrip()">➕ יצירת טיול חדש</button>
<button class="secondaryBtn" onclick="toggleTripSettings()">⚙️ הגדרות הטיול הפעיל</button>
</div>

<div id="tripSettingsPanel" class="filterPanel collapsiblePanel">
<div class="featureVerificationBox"><b>אפשרויות התקציב:</b> קופות מטבע חוץ, תקציבים פרטיים לעידו ולמיכאל, ושיוך כל הוצאה לתקציב המתאים.</div>
<label for="tripName"><b>שם הטיול:</b></label>
<input id="tripName" maxlength="80" placeholder="לדוגמה: יפן 2026 / ארה״ב 2027" oninput="scheduleTripNameSave()" onblur="saveTripName()">

<label><b>מקורות תקציב הבית:</b></label>
<div class="small">אפשר לשלב שקלים ומטבעות זרים. שווי התקציב הכולל בשקלים מחושב בכל רגע לפי השערים הפעילים.</div>
<div class="small">לכל קופה בחר מטבע וסוג אחזקה. הוצאה במזומן תחויב מקופת מזומן תואמת; הוצאה באשראי תחויב מחשבון בנק תואם. על חלק שאינו מכוסה במטבע המתאים תחול עמלת המרה של 1%.</div><div id="budgetSourcesRows"></div>
<button type="button" class="secondaryBtn" onclick="addBudgetSourceRow()">➕ הוספת מקור תקציב במטבע נוסף</button>
<div id="budgetSourcesPreview" class="fxFeePreview"></div>

<label for="tripIdoBudgetInput"><b>תקציב עידו פרטי בשקלים:</b></label>
<input id="tripIdoBudgetInput" type="number" inputmode="decimal" min="0" step="0.01" placeholder="0">

<label for="tripMichaelBudgetInput"><b>תקציב מיכאל פרטי בשקלים:</b></label>
<input id="tripMichaelBudgetInput" type="number" inputmode="decimal" min="0" step="0.01" placeholder="0">

<label for="tripClothingInput"><b>תקציב ביגוד, הנעלה, ואלקטרוניקה בשקלים:</b></label>
<input id="tripClothingInput" type="number" inputmode="decimal" placeholder="לדוגמה: 20000">

<label for="tripFoodInput"><b>תקציב אוכל יומי בשקלים:</b></label>
<input id="tripFoodInput" type="number" inputmode="decimal" min="0" placeholder="לדוגמה: 750">

<label for="tripTotalFoodInput"><b>מסגרת אוכל כוללת בשקלים:</b></label>
<input id="tripTotalFoodInput" type="number" inputmode="decimal" min="0" placeholder="0 אם אין מסגרת כוללת">

<button class="noPrint" onclick="saveTripSettings()">💾 שמירת הגדרות טיול</button>
</div>
</div>




<div class="card section-settings">
<b>תקציב כולל:</b> <span id="topBudgetLabel">₪93,063</span><br>
<b>תקציב ביגוד, הנעלה, ואלקטרוניקה:</b> <span id="topClothingBudgetLabel">₪20,000</span><br>
<b>תקציב אוכל יומי:</b> <span id="topFoodBudgetLabel">₪750</span><br><br>
<div class="ratesHeadline">
<b>שערי המרה פעילים להוצאות חדשות:</b><br>
<div class="rateLine"><span>USD דולר אמריקאי</span><b><span id="usdRateTop">2.9070</span> ₪</b></div>
<div class="rateLine"><span>JPY ין יפני</span><b><span id="jpyRateTop">0.018548</span> ₪</b></div>
<div class="rateLine"><span>100 ין יפני</span><b><span id="jpy100RateTop">1.8548</span> ₪</b></div>
<div class="rateLine"><span>EUR יורו</span><b><span id="eurRateTop">3.4225</span> ₪</b></div>
<div class="rateLine"><span>GBP לירה סטרלינג</span><b><span id="gbpRateTop">3.9572</span> ₪</b></div>
<div class="rateLine"><span>CAD דולר קנדי</span><b><span id="cadRateTop">2.1282</span> ₪</b></div>







</div>
<span class="small">מקור: <span id="rateSource"></span> | עודכן: <span id="rateUpdated"></span></span><br>
<span class="small"><b>חשוב:</b> הוצאות שכבר הוזנו נשארות לפי שער הכניסה שלהן ולא משתנות אחרי עדכון שערים.</span>
</div>

<div class="card noPrint section-settings">
<h2>עדכון שערים</h2>
<button onclick="updateRatesLive()">עדכון שערי מטבע מהאינטרנט</button>
<div id="ratesStatus" class="small" role="status" aria-live="polite"></div><div class="small">שערים שעודכנו מהאינטרנט או ידנית נשמרים ומשמשים לכל הוצאה חדשה עד העדכון הבא.</div>
<div class="rateInputWrap">
<label for="usdRateInput">דולר אמריקאי (USD) — שער בשקלים</label>
<input id="usdRateInput" type="number" step="0.0001" placeholder="לדוגמה: 2.9090">

<label for="jpyRateInput">ין יפני (JPY) — שער בשקלים ל־1 ין</label>
<input id="jpyRateInput" type="number" step="0.000001" placeholder="לדוגמה: 0.018511">

<label for="eurRateInput">יורו (EUR) — שער בשקלים</label>
<input id="eurRateInput" type="number" step="0.0001" placeholder="לדוגמה: 3.4225">

<label for="gbpRateInput">לירה סטרלינג (GBP) — שער בשקלים</label>
<input id="gbpRateInput" type="number" step="0.0001" placeholder="לדוגמה: 3.9572">

<label for="cadRateInput">דולר קנדי (CAD) — שער בשקלים</label>
<input id="cadRateInput" type="number" step="0.0001" placeholder="לדוגמה: 2.1282">
</div>
<button onclick="saveManualRates()">עדכון שערים ידני וסנכרון</button>
</div>

<div class="card noPrint section-settings">
<h2>ייצוא ואיפוס</h2>
<button class="export" onclick="exportHTML()">ייצוא HTML</button>
<button class="export" type="button" onclick="exportPDF()">תצוגת דוח / שמירה כ־PDF</button>
<div class="small">נפתח בתוך האפליקציה עם כפתור חזרה ברור.</div>
<button class="orange" onclick="clearTrip()">איפוס הוצאות הטיול הפעיל</button>
</div>

<div class="appCreatorFooter section-settings">
  <div class="creatorName">פותח ועוצב על ידי: איתן פרידמן</div>
</div>
</div>
</div>
</body>
</html>