import React, { useState, useRef, useEffect } from "react";
import { SIA_PHASES, EXPENSE_CATEGORIES } from "../constants.js";
import { generateId, formatDate, formatHours, getAbsenzTypes } from "../utils.js";
import { Header, FormField, Modal, DateInput, DatePicker, CalendarPopup, useCalendarNav, NavArrows } from "../components/UI.jsx";
import { ReceiptViewer } from "./Expenses.jsx";

const SLOT_START_H = 0;
const SLOT_END_H = 24;
const SLOT_COUNT = (SLOT_END_H - SLOT_START_H) * 4;
const SLOT_H = 12;
const slotToTime = (idx) => { const m = SLOT_START_H * 60 + idx * 15; return `${String(Math.floor(m/60)).padStart(2,"0")}:${String(m%60).padStart(2,"0")}`; };
const PROJ_COLORS = ["#b07848","#4a7c8c","#6b8c4a","#8c5a7c","#5a6c8c","#8c7a4a","#5a8c7a","#8c6a5a"];

export default
function Time({ data, update, currentUser, setPrintContent }) {
  const myEmployeeId = currentUser?.employeeId || null;
  const isAdmin = !myEmployeeId;
  const [currentDate, setCurrentDate] = useState(new Date().toISOString().slice(0, 10));
  const [selectedEmpId, setSelectedEmpId] = useState(() => myEmployeeId || "");
  const [viewMode, setViewMode] = useState("woche");
  const [weekSelectStart, setWeekSelectStart] = useState(null);
  const [weekHover, setWeekHover] = useState(null);
  const [weekForm, setWeekForm] = useState(null);
  const [weekAbsForm, setWeekAbsForm] = useState(null);
  const [weekEditForm, setWeekEditForm] = useState(null);
  const [contextMenu, setContextMenu] = useState(null);
  const weekGridRef = useRef(null);
  const resizingRef = useRef(null);
  const [resizeTick, setResizeTick] = useState(0);
  const dataRef = useRef(data);
  const updateRef = useRef(update);
  dataRef.current = data;
  updateRef.current = update;

  const employees = data.employees || [];
  const selectedEmp = employees.find(e => e.id === selectedEmpId) || null;

  const changeDate = (delta) => {
    const d = new Date(currentDate);
    d.setDate(d.getDate() + delta);
    setCurrentDate(d.toISOString().slice(0, 10));
  };
  const goToday = () => setCurrentDate(new Date().toISOString().slice(0, 10));

  // Wenn MA gewählt: nur dessen Einträge + Einträge ohne MA-Zuweisung ausblenden
  // Wenn kein MA gewählt: alle Einträge (inkl. ohne MA)
  const dayEntries = data.timeEntries.filter(e => {
    if (e.date !== currentDate) return false;
    if (selectedEmpId) return e.employeeId === selectedEmpId;
    return true;
  }).sort((a, b) => (a.createdAt || "").localeCompare(b.createdAt || ""));
  const dayTotalMins = dayEntries.reduce((s, e) => s + (e.minutes || 0), 0);

  // Hilfsfunktion: Feiertage für ein Jahr auflösen
  const resolveFTs = (yr) => {
    const fts = (data.feiertage || []).map(f =>
      f.repeatsYearly ? { ...f, date: `${yr}-${f.date.slice(5, 10)}` } : f
    );
    // Tag vor Feiertag: -1h (nur wenn stundenDelta nicht schon gesetzt)
    const ftDates = new Set(fts.filter(f => f.stundenDelta === 0 || f.stundenDelta === null || f.stundenDelta === undefined).map(f => f.date));
    const vortage = [];
    ftDates.forEach(ftDate => {
      const d = new Date(ftDate);
      d.setDate(d.getDate() - 1);
      const ds = d.toISOString().slice(0, 10);
      const dow = d.getDay();
      if (dow !== 0 && dow !== 6 && !ftDates.has(ds) && !fts.some(f => f.date === ds)) {
        vortage.push({ id: `vortag-${ds}`, date: ds, label: `Vortag ${ftDate}`, stundenDelta: -1 });
      }
    });
    return [...fts, ...vortage];
  };

  // Prüft ob ein Mitarbeiter an einem Tag bestätigte Ferien hat
  const isApprovedVacationDay = (empId, dateStr) =>
    (data.ferienEntries || []).some(f => {
      if (f.employeeId !== empId) return false;
      const active = f.status === "approved" || !f.status || (f.status === "pending" && f.originalData);
      if (!active) return false;
      const df = (f.status === "pending" && f.originalData) ? f.originalData.dateFrom : f.dateFrom;
      const dt = (f.status === "pending" && f.originalData) ? f.originalData.dateTo : f.dateTo;
      return dateStr >= df && dateStr <= dt;
    });

  // Tages-Soll für gewählten Mitarbeiter
  const getSollForDay = (emp, dateStr) => {
    if (!emp) return 0;
    // Vor Eintrittsdatum: 0
    if (emp.eintrittsdatum && dateStr < emp.eintrittsdatum) return 0;
    const d = new Date(dateStr);
    const dow = d.getDay();
    if (dow === 0 || dow === 6) return 0;
    const yr = d.getFullYear();
    const fts = resolveFTs(yr);
    const ft = fts.find(f => f.date === dateStr);
    const pensum = (emp.pensum || 100) / 100;
    const tagessoll = ((emp.wochenstunden || 35) * pensum) / 5;
    if (ft && (ft.stundenDelta === 0 || ft.stundenDelta === null || ft.stundenDelta === undefined)) return 0;
    if (isApprovedVacationDay(emp.id, dateStr)) return 0;
    if (ft) return Math.max(0, tagessoll + (ft.stundenDelta || 0));
    return tagessoll;
  };
  const daySoll = getSollForDay(selectedEmp, currentDate);

  // Monatssaldo
  const viewDate2 = new Date(currentDate);
  const calcMonthSaldo = (emp) => {
    if (!emp) return null;
    const yr = viewDate2.getFullYear();
    const mo = viewDate2.getMonth();
    const monthStr = `${yr}-${String(mo + 1).padStart(2, "0")}`;
    const today = new Date().toISOString().slice(0, 10);
    const pensum = (emp.pensum || 100) / 100;
    const tagessoll = ((emp.wochenstunden || 35) * pensum) / 5;
    const fts = resolveFTs(yr);
    let soll = 0, sollTotal = 0;
    const d = new Date(`${yr}-${String(mo + 1).padStart(2, "0")}-01`);
    while (d.toISOString().slice(0, 7) === monthStr) {
      const ds = d.toISOString().slice(0, 10);
      if (!(emp.eintrittsdatum && ds < emp.eintrittsdatum)) {
        const dow = d.getDay();
        if (dow !== 0 && dow !== 6 && !isApprovedVacationDay(emp.id, ds)) {
          const ft = fts.find(f => f.date === ds);
          let h = 0;
          if (!ft || (ft.stundenDelta === 0 || ft.stundenDelta === null || ft.stundenDelta === undefined)) {
            h = ft ? 0 : tagessoll;
          } else {
            h = Math.max(0, tagessoll + (ft.stundenDelta || 0));
          }
          sollTotal += h;
          if (ds <= today) soll += h;
        }
      }
      d.setDate(d.getDate() + 1);
    }
    const istMins = (data.timeEntries || []).filter(e => e.date?.startsWith(monthStr) && e.employeeId === emp.id).reduce((s, e) => s + (e.minutes || 0), 0);
    const absenzH = (data.absences || []).filter(a => a.employeeId === emp.id).reduce((s, a) => {
      const from = a.dateFrom || a.date; const to = a.dateTo || a.date;
      if (!from) return s;
      const isSingleDay = from === to;
      const ad = new Date(from); const ae = new Date(to);
      let h = 0;
      while (ad <= ae) {
        const ds = ad.toISOString().slice(0, 10);
        if (ds.startsWith(monthStr) && ds <= today && ad.getDay()!==0 && ad.getDay()!==6 && !fts.some(ft=>ft.date===ds&&(ft.stundenDelta===0||ft.stundenDelta===null||ft.stundenDelta===undefined))) {
          if (isSingleDay && a.startTime && a.endTime) { const [sh,sm]=a.startTime.split(":").map(Number); const [eh,em]=a.endTime.split(":").map(Number); h+=(eh*60+em-sh*60-sm)/60; }
          else if (isSingleDay) { const ex=(a.hours||0)*60+(a.minutes||0); h+=ex>0?ex/60:tagessoll; }
          else { h+=tagessoll; }
        }
        ad.setDate(ad.getDate()+1);
      }
      return s + h;
    }, 0);
    const istH = Math.round(istMins / 60 * 10) / 10;
    return { soll: Math.round(soll*10)/10, sollTotal: Math.round(sollTotal*10)/10, ist: istH, absenz: Math.round(absenzH*10)/10, saldo: Math.round((istH+absenzH-soll)*10)/10, saldoTotal: Math.round((istH+absenzH-sollTotal)*10)/10 };
  };
  const monthSaldo = calcMonthSaldo(selectedEmp);

  // Totalsaldo (Eintrittsdatum bis heute)
  const calcTotalSaldo = (emp) => {
    if (!emp) return null;
    const today = new Date().toISOString().slice(0, 10);
    const start = emp.eintrittsdatum || `${new Date().getFullYear()}-01-01`;
    if (start > today) return { soll: 0, ist: 0, ferien: 0, absenz: 0, saldo: 0 };
    const pensum = (emp.pensum || 100) / 100;
    const tagessoll = ((emp.wochenstunden || 35) * pensum) / 5;
    const ftsCache = {};
    const getFts = (yr) => { if (!ftsCache[yr]) ftsCache[yr] = resolveFTs(yr); return ftsCache[yr]; };
    const todayDate = new Date(today);
    let soll = 0;
    const d = new Date(start);
    while (d <= todayDate) {
      const ds = d.toISOString().slice(0, 10);
      const yr = d.getFullYear();
      const fts = getFts(yr);
      const dow = d.getDay();
      if (dow !== 0 && dow !== 6 && !isApprovedVacationDay(emp.id, ds)) {
        const ft = fts.find(f => f.date === ds);
        if (!ft || ft.stundenDelta === 0 || ft.stundenDelta === null || ft.stundenDelta === undefined) {
          soll += ft ? 0 : tagessoll;
        } else {
          soll += Math.max(0, tagessoll + (ft.stundenDelta || 0));
        }
      }
      d.setDate(d.getDate() + 1);
    }
    const istMins = (data.timeEntries || []).filter(e => e.employeeId === emp.id && e.date >= start && e.date <= today).reduce((s, e) => s + (e.minutes || 0), 0);
    const absenzH = (data.absences || []).filter(a => a.employeeId === emp.id).reduce((s, a) => {
      const from = a.dateFrom || a.date; const to = a.dateTo || a.date;
      if (!from) return s;
      const isSingleDay = from === to;
      const ad = new Date(from < start ? start : from);
      const ae = new Date(to > today ? today : to);
      let h = 0;
      while (ad <= ae) {
        const ds = ad.toISOString().slice(0, 10);
        const fts = getFts(ad.getFullYear());
        if (ad.getDay()!==0 && ad.getDay()!==6 && !fts.some(ft=>ft.date===ds&&(ft.stundenDelta===0||ft.stundenDelta===null||ft.stundenDelta===undefined))) {
          if (isSingleDay && a.startTime && a.endTime) { const [sh,sm]=a.startTime.split(":").map(Number); const [eh,em]=a.endTime.split(":").map(Number); h+=(eh*60+em-sh*60-sm)/60; }
          else if (isSingleDay) { const ex=(a.hours||0)*60+(a.minutes||0); h+=ex>0?ex/60:tagessoll; }
          else { h+=tagessoll; }
        }
        ad.setDate(ad.getDate()+1);
      }
      return s + h;
    }, 0);
    const istH = Math.round(istMins / 60 * 10) / 10;
    return { soll: Math.round(soll*10)/10, ist: istH, absenz: Math.round(absenzH*10)/10, saldo: Math.round((istH+absenzH-soll)*10)/10 };
  };
  const totalSaldo = calcTotalSaldo(selectedEmp);

  // Feriensaldo
  const calcFerienSaldo = (emp) => {
    if (!emp) return null;
    const yr = viewDate2.getFullYear();
    const yearStr = String(yr);
    const pensum = (emp.pensum || 100) / 100;
    const tagessoll = ((emp.wochenstunden || 35) * pensum) / 5;
    const yearStart = `${yr}-01-01`;
    const eintrittsdatum = emp.eintrittsdatum || null;
    let proRata = 1;
    if (eintrittsdatum && eintrittsdatum > yearStart) {
      const entryMonth = parseInt(eintrittsdatum.slice(5, 7));
      proRata = (13 - entryMonth) / 12;
    }
    const anspruchH = (emp.ferienWochen || 4) * (emp.wochenstunden || 35) * pensum * proRata;
    const ubertragH = (emp.ferienUebertragVorjahr || {})[yr] || 0;
    const fts = resolveFTs(yr);
    const today = new Date().toISOString().slice(0, 10);
    const isFtFree = (ds) => fts.some(ft => ft.date === ds && (ft.stundenDelta === 0 || ft.stundenDelta === null || ft.stundenDelta === undefined));
    const countWorkdays = (from, to, onlyPast, onlyFuture) => {
      let cnt = 0;
      const d = new Date(from); const e = new Date(to);
      while (d <= e) {
        const ds = d.toISOString().slice(0, 10);
        if (ds.startsWith(yearStr) && d.getDay() !== 0 && d.getDay() !== 6 && !isFtFree(ds)) {
          if (onlyPast && ds > today) { d.setDate(d.getDate() + 1); continue; }
          if (onlyFuture && ds <= today) { d.setDate(d.getDate() + 1); continue; }
          cnt++;
        }
        d.setDate(d.getDate() + 1);
      }
      return cnt;
    };
    // Bestätigte Ferien: approved OR pending-with-originalData (use originalData dates)
    const confirmedEntries = (data.ferienEntries || []).filter(f =>
      f.employeeId === emp.id && (f.status === "approved" || !f.status || (f.status === "pending" && f.originalData))
    ).map(f => ({
      from: (f.status === "pending" && f.originalData) ? f.originalData.dateFrom : f.dateFrom,
      to: (f.status === "pending" && f.originalData) ? f.originalData.dateTo : f.dateTo,
    }));
    const bezogenH = confirmedEntries.reduce((s, f) => s + countWorkdays(f.from, f.to, true, false) * tagessoll, 0);
    const geplantH = confirmedEntries.filter(f => f.from > today).reduce((s, f) => s + countWorkdays(f.from, f.to, false, true) * tagessoll, 0);
    // Beantragt: pending entries (use their requested dates)
    const beantragtH = (data.ferienEntries || []).filter(f =>
      f.employeeId === emp.id && f.status === "pending" &&
      (f.dateFrom.startsWith(yearStr) || f.dateTo.startsWith(yearStr))
    ).reduce((s, f) => s + countWorkdays(f.dateFrom, f.dateTo, false, false) * tagessoll, 0);
    return {
      anspruch: Math.round(anspruchH*10)/10,
      ubertrag: Math.round(ubertragH*10)/10,
      bezogen: Math.round(bezogenH*10)/10,
      geplant: Math.round(geplantH*10)/10,
      beantragt: Math.round(beantragtH*10)/10,
      rest: Math.round((anspruchH + ubertragH - bezogenH - geplantH) * 10) / 10,
    };
  };
  const ferienSaldo = calcFerienSaldo(selectedEmp);

  const projectHasPhases = (proj) => {
    if (!proj) return false;
    return (proj.enabledPhases || []).length > 0 || (proj.customPhases || []).length > 0 ||
      (proj.positions || []).some(pos => (pos.enabledPhases || []).length > 0);
  };

  const getFirstPhaseForProject = (proj) => {
    if (!proj) return { phaseId: "", positionId: "" };
    const cp = proj.customPhases || [];
    if (cp.length > 0) return { phaseId: cp[0].id, positionId: "" };
    const ep = proj.enabledPhases || [];
    if (ep.length > 0) return { phaseId: ep[0], positionId: "" };
    for (const pos of (proj.positions || [])) {
      if ((pos.enabledPhases || []).length > 0) return { phaseId: pos.enabledPhases[0], positionId: pos.code };
    }
    return { phaseId: "", positionId: "" };
  };

  const closedMonths = data.settings.closedMonths || [];
  const isMonthClosed = (date) => closedMonths.includes((date || "").slice(0, 7));

  const addEntry = () => {
    if (isMonthClosed(currentDate)) { alert("Dieser Monat ist abgeschlossen und kann nicht mehr bearbeitet werden."); return; }
    const firstActive = data.projects.find(p => p.status === "aktiv") || data.projects[0];
    const { phaseId, positionId } = getFirstPhaseForProject(firstActive);
    const toTime = (m) => `${String(Math.floor(m / 60)).padStart(2, "0")}:${String(m % 60).padStart(2, "0")}`;
    const todayEnds = data.timeEntries.filter(e => e.date === currentDate && (!selectedEmpId || e.employeeId === selectedEmpId) && e.endTime);
    let startMins = 8 * 60;
    if (todayEnds.length > 0) {
      const maxEnd = todayEnds.reduce((max, e) => { const [h, m] = e.endTime.split(":").map(Number); return Math.max(max, h * 60 + m); }, 0);
      if (maxEnd > 0) startMins = maxEnd;
    }
    const defaultMins = 60;
    const newEntry = { id: generateId(), date: currentDate, projectId: firstActive?.id || "", phaseId, positionId, minutes: defaultMins, startTime: toTime(startMins), endTime: toTime(startMins + defaultMins), description: "", createdAt: new Date().toISOString(), ...(selectedEmpId ? { employeeId: selectedEmpId } : {}) };
    update("timeEntries", [...data.timeEntries, newEntry]);
  };

  const updateEntry = (id, changes) => {
    const entry = data.timeEntries.find(e => e.id === id);
    if (entry?.invoiceId) { alert("Dieser Eintrag ist bereits verrechnet und kann nicht mehr geändert werden. Lösche zuerst die zugehörige Rechnung."); return; }
    if (isMonthClosed(entry?.date)) { alert("Dieser Monat ist abgeschlossen und kann nicht mehr bearbeitet werden."); return; }
    const merged = { ...entry, ...changes };
    if ("minutes" in changes && merged.startTime) {
      const [h, m] = merged.startTime.split(":").map(Number);
      const endMins = h * 60 + m + (merged.minutes || 0);
      changes = { ...changes, endTime: `${String(Math.floor(endMins / 60)).padStart(2, "0")}:${String(endMins % 60).padStart(2, "0")}` };
    }
    update("timeEntries", data.timeEntries.map(e => e.id === id ? { ...e, ...changes } : e));
  };

  const delEntry = (id) => {
    const entry = data.timeEntries.find(e => e.id === id);
    if (entry?.invoiceId) { alert("Dieser Eintrag ist bereits verrechnet und kann nicht gelöscht werden. Lösche zuerst die zugehörige Rechnung."); return; }
    if (isMonthClosed(entry?.date)) { alert("Dieser Monat ist abgeschlossen und kann nicht mehr gelöscht werden."); return; }
    update("timeEntries", data.timeEntries.filter(e => e.id !== id));
  };

  // Tages-Spesen — inline editing
  const mwstRate = data.settings.mwstRate || 8.1;
  const dayExpenses = (data.expenses || []).filter(e => {
    if (e.date !== currentDate) return false;
    if (selectedEmpId) return e.employeeId === selectedEmpId;
    return true;
  });

  const addExp = () => {
    update("expenses", [...(data.expenses || []), { id: generateId(), date: currentDate, category: (data.settings.expenseCategories || EXPENSE_CATEGORIES)[0], projectId: "", description: "", amount: 0, mwstRate, inclMwst: true, employeeId: selectedEmpId || "" }]);
  };
  const updateExp = (id, changes) => update("expenses", (data.expenses || []).map(e => e.id === id ? { ...e, ...changes } : e));
  const delExp = (id) => update("expenses", (data.expenses || []).filter(e => e.id !== id));

  const [receiptView, setReceiptView] = useState(null);
  const [uploadingExpId, setUploadingExpId] = useState(null);
  const expReceiptInputRef = useRef(null);
  const handleExpReceiptUpload = (ev) => {
    const file = ev.target.files?.[0];
    if (!file || !uploadingExpId) return;
    const isPdf = file.type === "application/pdf";
    const reader = new FileReader();
    reader.onload = (e) => {
      if (isPdf) {
        updateExp(uploadingExpId, { receiptData: e.target.result, receiptName: file.name });
      } else {
        const img = new Image();
        img.onload = () => {
          const maxW = 1600;
          const scale = img.width > maxW ? maxW / img.width : 1;
          const canvas = document.createElement("canvas");
          canvas.width = Math.round(img.width * scale);
          canvas.height = Math.round(img.height * scale);
          canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height);
          updateExp(uploadingExpId, { receiptData: canvas.toDataURL("image/jpeg", 0.82), receiptName: file.name });
        };
        img.src = e.target.result;
      }
    };
    reader.readAsDataURL(file);
    ev.target.value = "";
  };

  // Intern / Absenz — inline editing
  const absenzTypes = getAbsenzTypes(data);
  const dayAbs = (data.absences || []).filter(a => {
    const matchDate = a.date === currentDate || (a.dateFrom && a.dateTo && currentDate >= a.dateFrom && currentDate <= a.dateTo);
    return matchDate && (!selectedEmpId || a.employeeId === selectedEmpId);
  });

  const dayAbsMins = selectedEmp ? dayAbs.reduce((s, a) => {
    if (a.startTime && a.endTime) { const [sh,sm]=a.startTime.split(":").map(Number); const [eh,em]=a.endTime.split(":").map(Number); return s+(eh*60+em)-(sh*60+sm); }
    const ex=(a.hours||0)*60+(a.minutes||0); return s+(ex>0?ex:daySoll*60);
  }, 0) : 0;
  const dayIst = (dayTotalMins + dayAbsMins) / 60;
  const dayDiff = Math.round((dayIst - daySoll) * 10) / 10;

  const toAbsTime = (m) => `${String(Math.floor(m / 60)).padStart(2, "0")}:${String(m % 60).padStart(2, "0")}`;
  const addAbs = () => {
    const todayAbs = (data.absences || []).filter(a =>
      (a.date === currentDate || (a.dateFrom && a.dateTo && currentDate >= a.dateFrom && currentDate <= a.dateTo)) &&
      (!selectedEmpId || a.employeeId === selectedEmpId)
    );
    let startMins = 8 * 60;
    if (todayAbs.length > 0) {
      const maxEnd = todayAbs.reduce((max, a) => {
        let endM;
        if (a.endTime) { const [h,m]=a.endTime.split(":").map(Number); endM=h*60+m; }
        else { const dur=(a.hours||0)*60+(a.minutes||0); endM=8*60+(dur>0?dur:Math.round(daySoll*60)); }
        return Math.max(max, endM);
      }, 8 * 60);
      if (maxEnd > 8 * 60) startMins = maxEnd;
    }
    const endMins = startMins + 60;
    update("absences", [...(data.absences || []), { id: generateId(), date: currentDate, employeeId: selectedEmpId || (employees[0]?.id || ""), type: absenzTypes[0]?.id || "", startTime: toAbsTime(startMins), endTime: toAbsTime(endMins), hours: 1, minutes: 0, note: "", status: "pending", createdAt: new Date().toISOString() }]);
  };
  const updateAbs = (id, changes) => {
    const abs = (data.absences || []).find(a => a.id === id);
    if (!abs) return;
    if (abs.startTime && ("hours" in changes || "minutes" in changes)) {
      const [sh, sm] = abs.startTime.split(":").map(Number);
      const newH = "hours" in changes ? changes.hours : (abs.hours || 0);
      const newM = "minutes" in changes ? changes.minutes : (abs.minutes || 0);
      const endMins = Math.min(sh * 60 + sm + newH * 60 + newM, 23 * 60 + 59);
      changes = { ...changes, endTime: toAbsTime(endMins) };
    }
    update("absences", (data.absences || []).map(a => a.id === id ? { ...a, ...changes } : a));
  };
  const delAbs = (id) => update("absences", (data.absences || []).filter(a => a.id !== id));

  // Ferien-Antrag Modal
  const [antragModal, setAntragModal] = useState(null);
  const [antragForm, setAntragForm] = useState({});

  const openFerienAntrag = () => {
    setAntragForm({ employeeId: selectedEmpId || (employees[0]?.id || ""), dateFrom: currentDate, dateTo: currentDate, note: "" });
    setAntragModal("ferien");
  };
  const [antragSaved, setAntragSaved] = useState(false);
  const saveAntrag = () => {
    if (!antragForm.employeeId || !antragForm.dateFrom || !antragForm.dateTo) return;
    if (antragForm.id) {
      const existing = (data.ferienEntries || []).find(f => f.id === antragForm.id);
      let entry;
      if (existing?.status === "approved" || !existing?.status) {
        entry = {
          ...antragForm,
          status: "pending",
          originalData: existing?.originalData || { dateFrom: existing.dateFrom, dateTo: existing.dateTo, note: existing.note || "" },
        };
      } else {
        entry = { ...antragForm, status: "pending" };
      }
      update("ferienEntries", (data.ferienEntries || []).map(f => f.id === entry.id ? entry : f));
    } else {
      const entry = { ...antragForm, id: generateId(), status: "pending", createdAt: new Date().toISOString() };
      update("ferienEntries", [...(data.ferienEntries || []), entry]);
    }
    setAntragModal(null);
    setAntragSaved(true);
    setTimeout(() => setAntragSaved(false), 3000);
  };
  const deleteFerien = (id) => update("ferienEntries", (data.ferienEntries || []).filter(f => f.id !== id));
  const markFerienSeen = (id) => update("ferienEntries", (data.ferienEntries || []).map(f => f.id === id ? { ...f, seen: true } : f));
  const approveFerien = (id) => update("ferienEntries", (data.ferienEntries || []).map(f => {
    if (f.id !== id) return f;
    const { originalData, ...rest } = f;
    return { ...rest, status: "approved" };
  }));
  const cancelFerienEdit = (id) => update("ferienEntries", (data.ferienEntries || []).map(f => {
    if (f.id !== id || !f.originalData) return f;
    const { originalData, ...rest } = f;
    return { ...rest, dateFrom: originalData.dateFrom, dateTo: originalData.dateTo, note: originalData.note, status: "approved" };
  }));
  const editFerien = (f) => { setAntragForm({ ...f }); setAntragModal("ferien"); };

  const [ferienUebersicht, setFerienUebersicht] = useState(false);
  const navCal = useCalendarNav();

  // Pendente Ferienanträge
  const myPendingFerien = (data.ferienEntries || []).filter(f => (!selectedEmpId || f.employeeId === selectedEmpId) && f.status === "pending");
  const totalPending = myPendingFerien.length;

  const viewDate = new Date(currentDate);
  const year = viewDate.getFullYear();
  const month = viewDate.getMonth();
  const firstDay = new Date(year, month, 1);
  const lastDay = new Date(year, month + 1, 0);
  const startWeekday = (firstDay.getDay() + 6) % 7; // Montag = 0

  const calFTs = resolveFTs(year);

  const todayStr = new Date().toISOString().slice(0, 10);
  const monthCells = [];
  for (let i = 0; i < startWeekday; i++) monthCells.push(null);
  for (let d = 1; d <= lastDay.getDate(); d++) {
    const dateStr = `${year}-${String(month + 1).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
    const mins = data.timeEntries.filter(e => e.date === dateStr && (!selectedEmpId || e.employeeId === selectedEmpId || !e.employeeId)).reduce((s, e) => s + (e.minutes || 0), 0);
    const ft = calFTs.find(f => f.date === dateStr);
    const dow = new Date(dateStr).getDay();
    const isWeekend = dow === 0 || dow === 6;
    const isFeiertag = ft && (ft.stundenDelta === 0 || ft.stundenDelta === null || ft.stundenDelta === undefined);
    const isVortag = ft && ft.stundenDelta === -1 && (ft.id || "").startsWith("vortag-");
    const sollH = selectedEmp ? getSollForDay(selectedEmp, dateStr) : null;
    // Ferien/Absenz für diesen Tag (nur wenn Mitarbeiter gewählt)
    const isFerien = selectedEmpId ? isApprovedVacationDay(selectedEmpId, dateStr) : false;
    const isPendingFerien = selectedEmpId ? (data.ferienEntries || []).some(f => f.employeeId === selectedEmpId && f.status === "pending" && dateStr >= f.dateFrom && dateStr <= f.dateTo) : false;
    const isAbsenz = selectedEmpId ? (data.absences || []).some(a => a.employeeId === selectedEmpId && (!a.status || a.status === "approved") && (a.date === dateStr || (a.dateFrom && a.dateTo && dateStr >= a.dateFrom && dateStr <= a.dateTo))) : false;
    const isPendingAbsenz = selectedEmpId ? (data.absences || []).some(a => a.employeeId === selectedEmpId && a.status === "pending" && (a.date === dateStr || (a.dateFrom && a.dateTo && dateStr >= a.dateFrom && dateStr <= a.dateTo))) : false;
    monthCells.push({ day: d, dateStr, mins, ft, isWeekend, isFeiertag, isVortag, sollH, isFerien, isPendingFerien, isAbsenz, isPendingAbsenz, isPast: dateStr <= todayStr });
  }

  const monthTotalMins = monthCells.filter(c => c).reduce((s, c) => s + c.mins, 0);

  const weekdayLabel = new Date(currentDate).toLocaleDateString("de-CH", { weekday: "long" });
  const dateLabel = new Date(currentDate).toLocaleDateString("de-CH", { day: "numeric", month: "long", year: "numeric" });
  const monthLabel = viewDate.toLocaleDateString("de-CH", { month: "long", year: "numeric" });
  const isToday = currentDate === new Date().toISOString().slice(0, 10);

  const getWeekDays = (ds) => {
    const d = new Date(ds);
    const dow = d.getDay();
    const mon = new Date(d);
    mon.setDate(d.getDate() - (dow === 0 ? 6 : dow - 1));
    return Array.from({ length: 7 }, (_, i) => {
      const day = new Date(mon); day.setDate(mon.getDate() + i);
      return day.toISOString().slice(0, 10);
    });
  };
  const weekDays = getWeekDays(currentDate);
  const weekLabel = (() => {
    const from = new Date(weekDays[0]); const to = new Date(weekDays[6]);
    return `${from.toLocaleDateString("de-CH", { day: "numeric", month: "short" })} – ${to.toLocaleDateString("de-CH", { day: "numeric", month: "short", year: "numeric" })}`;
  })();
  const changeWeek = (delta) => {
    const d = new Date(currentDate); d.setDate(d.getDate() + delta * 7);
    setCurrentDate(d.toISOString().slice(0, 10)); setWeekForm(null); setWeekSelectStart(null); setWeekAbsForm(null); setWeekEditForm(null);
  };
  const handleSlotClick = (dayStr, slotIdx) => {
    if (isMonthClosed(dayStr)) return;
    if (selectedEmp?.eintrittsdatum && dayStr < selectedEmp.eintrittsdatum) return;
    if (!weekSelectStart) { setWeekSelectStart({ dayStr, slotIdx }); return; }
    if (weekSelectStart.dayStr !== dayStr) { setWeekSelectStart({ dayStr, slotIdx }); return; }
    const startSlot = Math.min(weekSelectStart.slotIdx, slotIdx);
    const endSlot = Math.max(weekSelectStart.slotIdx, slotIdx);
    const firstActive = data.projects.find(p => p.status === "aktiv") || data.projects[0];
    setWeekForm({ dayStr, startSlot, endSlot, mode: "projekt", projectId: firstActive?.id || "", phaseId: "", positionId: "", description: "", absType: absenzTypes[0]?.id || "", absNote: "" });
    setWeekEditForm(null); setWeekSelectStart(null);
  };
  const saveWeekEntry = () => {
    if (isMonthClosed(weekForm?.dayStr)) return;
    const mins = (weekForm.endSlot - weekForm.startSlot + 1) * 15;
    if (weekForm?.mode === "absenz") {
      update("absences", [...(data.absences || []), {
        id: generateId(), date: weekForm.dayStr,
        employeeId: selectedEmpId || "",
        type: weekForm.absType || (absenzTypes[0]?.id || ""),
        startTime: slotToTime(weekForm.startSlot), endTime: slotToTime(weekForm.endSlot + 1),
        minutes: mins, hours: 0,
        note: weekForm.absNote || "", status: "pending",
        createdAt: new Date().toISOString(),
      }]);
      setWeekForm(null);
      return;
    }
    if (!weekForm?.projectId) return;
    update("timeEntries", [...data.timeEntries, {
      id: generateId(), date: weekForm.dayStr,
      projectId: weekForm.projectId, phaseId: weekForm.phaseId || "", positionId: weekForm.positionId || "",
      minutes: mins, startTime: slotToTime(weekForm.startSlot), endTime: slotToTime(weekForm.endSlot + 1),
      description: weekForm.description || "", employeeId: selectedEmpId || "",
      createdAt: new Date().toISOString(),
    }]);
    setWeekForm(null);
  };

  const saveWeekAbs = () => {
    if (!weekAbsForm) return;
    const h = weekAbsForm.hours || 0; const m = weekAbsForm.minutes || 0;
    const startMins = 8 * 60;
    const durMins = (h > 0 || m > 0) ? h * 60 + m : Math.round(getSollForDay(selectedEmp, weekAbsForm.dayStr) * 60) || 60;
    update("absences", [...(data.absences || []), {
      id: generateId(), date: weekAbsForm.dayStr,
      employeeId: selectedEmpId || (employees[0]?.id || ""),
      type: weekAbsForm.type || (absenzTypes[0]?.id || ""),
      startTime: toAbsTime(startMins), endTime: toAbsTime(startMins + durMins),
      hours: h, minutes: m,
      note: weekAbsForm.note || "", status: "pending",
      createdAt: new Date().toISOString(),
    }]);
    setWeekAbsForm(null);
  };

  useEffect(() => {
    if (viewMode === "woche" && weekGridRef.current) {
      weekGridRef.current.scrollTop = (7 - SLOT_START_H) * 4 * SLOT_H;
    }
  }, [viewMode]);

  useEffect(() => {
    const onMove = (ev) => {
      const r = resizingRef.current;
      if (!r) return;
      const scrollTop = weekGridRef.current?.scrollTop || 0;
      const absY = ev.clientY + scrollTop;
      const deltaSlots = Math.round((absY - r.startAbsY) / SLOT_H);
      if (deltaSlots !== 0) r.moved = true;
      const origDur = Math.round(r.origMinutes / 15);
      let newStart, newDur;
      if (r.edge === "bottom") {
        newStart = r.origSlot;
        newDur = Math.max(1, origDur + deltaSlots);
      } else if (r.edge === "top") {
        const s = Math.max(0, Math.min(r.origSlot + deltaSlots, r.origSlot + origDur - 1));
        newStart = s; newDur = r.origSlot + origDur - s;
      } else {
        newStart = Math.max(0, Math.min(SLOT_COUNT - origDur, r.origSlot + deltaSlots));
        newDur = origDur;
      }
      if (newStart + newDur > SLOT_COUNT) newDur = SLOT_COUNT - newStart;
      const overlaps = !r.isAbs && r.edge !== "move" && dataRef.current.timeEntries.filter(e2 =>
        e2.date === r.dayStr && e2.startTime && e2.id !== r.entryId && (!r.empId || e2.employeeId === r.empId)
      ).map(e2 => { const [h,m] = e2.startTime.split(":").map(Number); const s = Math.round((h*60+m-SLOT_START_H*60)/15); return { start: s, end: s + Math.round(e2.minutes/15) }; })
      .some(o => newStart < o.end && newStart + newDur > o.start);
      if (!overlaps) { r.previewStart = slotToTime(newStart); r.previewMins = newDur * 15; }
      setResizeTick(t => t + 1);
    };
    const onUp = () => {
      const r = resizingRef.current;
      if (r) {
        if (!r.moved) {
          setWeekEditForm({ entryId: r.entryId, isAbs: r.isAbs || false });
        } else if (r.previewMins !== undefined) {
          const [sh, sm] = r.previewStart.split(":").map(Number);
          const endTime = slotToTime((sh * 60 + sm - SLOT_START_H * 60) / 15 + r.previewMins / 15);
          const pStart = Math.round((sh * 60 + sm - SLOT_START_H * 60) / 15);
          const pEnd = pStart + Math.round(r.previewMins / 15);
          if (r.isAbs) {
            updateRef.current("absences", dataRef.current.absences.map(a =>
              a.id === r.entryId ? { ...a, startTime: r.previewStart, endTime, minutes: r.previewMins } : a
            ));
          } else {
            let newEntries = dataRef.current.timeEntries.map(e =>
              e.id === r.entryId ? { ...e, startTime: r.previewStart, endTime, minutes: r.previewMins } : e
            );
            if (r.edge === "move") {
              newEntries = newEntries.map(e2 => {
                if (e2.id === r.entryId || e2.date !== r.dayStr || !e2.startTime) return e2;
                if (r.empId && e2.employeeId !== r.empId) return e2;
                const [h, m] = e2.startTime.split(":").map(Number);
                const s = Math.round((h * 60 + m - SLOT_START_H * 60) / 15);
                const dur = Math.round(e2.minutes / 15);
                if (s >= pEnd || s + dur <= pStart) return e2;
                const newS = s >= pStart ? pEnd : pStart - dur;
                if (newS < 0 || newS + dur > SLOT_COUNT) return e2;
                return { ...e2, startTime: slotToTime(newS), endTime: slotToTime(newS + dur) };
              });
            }
            updateRef.current("timeEntries", newEntries);
          }
        }
      }
      resizingRef.current = null;
      setResizeTick(t => t + 1);
    };
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
    return () => { document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); };
  }, []);

  const duplicateToNextDay = (entry) => {
    const d = new Date(entry.date);
    d.setDate(d.getDate() + 1);
    const nextDay = d.toISOString().slice(0, 10);
    update("timeEntries", [...data.timeEntries, { ...entry, id: generateId(), date: nextDay, createdAt: new Date().toISOString() }]);
  };

  return (
    <div>
      <Header title="Zeiterfassung" action={selectedEmp ? (
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn btn-ghost" onClick={() => setPrintContent({ type: "timeReport", employee: selectedEmp, entries: (data.timeEntries || []).filter(e => e.employeeId === selectedEmpId), month: currentDate.slice(0, 7), data, settings: data.settings })}>PDF Stunden</button>
          <button className="btn btn-ghost" onClick={() => setFerienUebersicht(true)}>Übersicht ↗</button>
          <button className="btn btn-primary" onClick={openFerienAntrag}>Ferien beantragen</button>
        </div>
      ) : null} />


      <div className="responsive-grid-2" style={{ display: "grid", gridTemplateColumns: "1fr 320px", gap: 20, alignItems: "start" }}>
        {/* Tages-Erfassung */}
        <div style={{ position: "relative" }}>
          {!selectedEmpId && isAdmin && (
            <div style={{ position: "absolute", inset: 0, background: "rgba(250,248,245,0.88)", zIndex: 10, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", backdropFilter: "blur(1px)" }}>
              <div style={{ textAlign: "center", color: "#888" }}>
                {employees.length === 0 ? (
                  <>
                    <div style={{ fontSize: 13, fontWeight: 500, color: "#555" }}>Noch keine Mitarbeiter erfasst</div>
                    <div style={{ fontSize: 11, color: "#aaa", marginTop: 4 }}>Mitarbeiter unter «Mitarbeiter» anlegen</div>
                  </>
                ) : (
                  <>
                    <div style={{ fontSize: 13, fontWeight: 500, color: "#555" }}>Bitte zuerst einen Mitarbeiter wählen</div>
                    <div style={{ fontSize: 11, color: "#aaa", marginTop: 4 }}>Mitarbeiterwahl rechts →</div>
                  </>
                )}
              </div>
            </div>
          )}
          <div className="card" style={{ marginBottom: 16, display: "flex", alignItems: "center", gap: 8, padding: "12px 16px" }}>
            <NavArrows onPrev={() => viewMode === "tag" ? changeDate(-1) : changeWeek(-1)} onNext={() => viewMode === "tag" ? changeDate(1) : changeWeek(1)} />
            <div ref={navCal.ref} style={{ flex: 1, textAlign: "center", position: "relative" }}>
              {viewMode === "tag" ? (
                <div onClick={() => navCal.setOpen(o => !o)} style={{ cursor: "pointer", display: "inline-block" }} title="Datum wählen">
                  <div style={{ fontFamily: "'Playfair Display', serif", fontSize: 18, fontWeight: 400 }}>
                    {`${weekdayLabel}, ${new Date(currentDate).toLocaleDateString("de-CH", { day: "numeric", month: "long" })}`}
                  </div>
                </div>
              ) : (
                <div style={{ fontFamily: "'Playfair Display', serif", fontSize: 18, fontWeight: 400 }}>{weekLabel}</div>
              )}
              {navCal.open && (
                <CalendarPopup
                  value={currentDate}
                  onChange={ds => { setCurrentDate(ds); navCal.setOpen(false); }}
                  onClose={() => navCal.setOpen(false)}
                  showClear={false}
                />
              )}
            </div>
            {!isToday && <button className="btn btn-ghost" onClick={goToday} style={{ fontSize: 11 }}>Heute</button>}
            <div style={{ display: "flex", border: "1px solid var(--border2)", borderRadius: 20, overflow: "hidden", marginLeft: 4 }}>
              {[["tag", "Tag"], ["woche", "Woche"]].map(([mode, label], i) => (
                <React.Fragment key={mode}>
                  {i > 0 && <div style={{ width: 1, background: "var(--border2)", alignSelf: "stretch" }} />}
                  <button onClick={() => { setViewMode(mode); setWeekSelectStart(null); setWeekForm(null); setWeekAbsForm(null); setWeekEditForm(null); }} style={{
                    fontSize: 11, padding: "0 12px", height: 30, border: "none", cursor: "pointer", fontFamily: "inherit",
                    background: viewMode === mode ? "var(--text)" : "transparent",
                    color: viewMode === mode ? "var(--bg)" : "var(--text3)", transition: "all 0.15s",
                  }}>{label}</button>
                </React.Fragment>
              ))}
            </div>
          </div>

          {viewMode === "tag" ? (
            <>
              <div className="card" style={{ padding: 0 }}>
                <table style={{ width: "100%" }}>
                  <thead>
                    <tr>
                      <th style={{ width: "24%" }}>Projekt</th>
                      <th style={{ width: 130 }}>Phase / Position</th>
                      <th>Tätigkeit</th>
                      <th style={{ width: 80 }}>Std</th>
                      <th style={{ width: 80 }}>Min</th>
                      <th style={{ width: 40 }}></th>
                    </tr>
                  </thead>
                  <tbody>
                    {dayEntries.length === 0 && (
                      <tr><td colSpan={6} style={{ textAlign: "center", color: "#aaa", padding: 28 }}>Keine Einträge an diesem Tag</td></tr>
                    )}
                    {dayEntries.map(e => {
                      const proj = data.projects.find(p => p.id === e.projectId);
                      const projEnabledIds = proj?.enabledPhases || [];
                      const enabledPhases = projEnabledIds.map(id => SIA_PHASES.find(ph => ph.id === id)).filter(Boolean);
                      const positions = proj?.positions || [];
                      const customPhases = proj?.customPhases || [];
                      const isInvoiced = !!e.invoiceId;
                      const disabled = isInvoiced || isMonthClosed(e.date);
                      const hasAnyPhase = enabledPhases.length > 0 || customPhases.length > 0 || positions.some(pos => (pos.enabledPhases || []).length > 0);
                      const combinedVal = e.phaseId ? (e.positionId ? e.phaseId + "|" + e.positionId : e.phaseId) : "";
                      const onCombinedChange = (val) => {
                        if (!val) { updateEntry(e.id, { phaseId: "", positionId: "" }); return; }
                        const [ph, pos] = val.split("|");
                        updateEntry(e.id, { phaseId: ph, positionId: pos || "" });
                      };
                      return (
                        <tr key={e.id} style={isInvoiced ? { opacity: 0.6, background: "#f5f2ed" } : {}}>
                          <td style={{ padding: "6px 8px" }}>
                            <select disabled={disabled} value={e.projectId} onChange={ev => { const proj = data.projects.find(p => p.id === ev.target.value); const { phaseId, positionId } = getFirstPhaseForProject(proj); updateEntry(e.id, { projectId: ev.target.value, phaseId, positionId }); }} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }}>
                              <option value="">—</option>
                              {data.projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
                            </select>
                          </td>
                          <td style={{ padding: "6px 8px" }}>
                            <select disabled={disabled || !hasAnyPhase} value={combinedVal} onChange={ev => onCombinedChange(ev.target.value)} style={{ border: `1px solid ${hasAnyPhase && !combinedVal && !disabled ? "#b5621e" : "#e0dbd4"}`, height: 32, fontSize: 12, color: e.positionId ? "#7a6a00" : "inherit", fontWeight: e.positionId ? 600 : 400, opacity: !hasAnyPhase ? 0.4 : 1 }}>
                              <option value="">—</option>
                              {customPhases.length > 0 && (enabledPhases.length > 0 || positions.length > 0) && (<optgroup label="Eigene Phasen">{customPhases.map(cp => <option key={cp.id} value={cp.id}>{cp.label}</option>)}</optgroup>)}
                              {customPhases.length > 0 && enabledPhases.length === 0 && positions.length === 0 && customPhases.map(cp => <option key={cp.id} value={cp.id}>{cp.label}</option>)}
                              {positions.length === 0 && enabledPhases.map(ph => <option key={ph.id} value={ph.id}>{ph.id} · {ph.label.split(" ").slice(1).join(" ")}</option>)}
                              {positions.length > 0 && (<>{enabledPhases.length > 0 && (<optgroup label="Hauptauftrag">{enabledPhases.map(ph => <option key={ph.id} value={ph.id}>{ph.id} · {ph.label.split(" ").slice(1).join(" ")}</option>)}</optgroup>)}{positions.map(pos => { const pp = (pos.enabledPhases||[]).map(id=>SIA_PHASES.find(ph=>ph.id===id)).filter(Boolean); if(!pp.length) return null; return <optgroup key={pos.code} label={pos.code+(pos.label?" · "+pos.label:"")}>{pp.map(ph=><option key={ph.id} value={ph.id+"|"+pos.code}>{ph.id} · {ph.label.split(" ").slice(1).join(" ")}</option>)}</optgroup>; })}</>)}
                            </select>
                          </td>
                          <td style={{ padding: "6px 8px" }}>
                            <input disabled={disabled} value={e.description || ""} onChange={ev => updateEntry(e.id, { description: ev.target.value })} placeholder="Was wurde gemacht?" style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }} />
                          </td>
                          <td style={{ padding: "6px 8px" }}>
                            <input disabled={disabled} type="number" min={0} max={24} value={Math.floor((e.minutes || 0) / 60)} onChange={ev => updateEntry(e.id, { minutes: (+ev.target.value * 60) + ((e.minutes || 0) % 60) })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }} />
                          </td>
                          <td style={{ padding: "6px 8px" }}>
                            <select disabled={disabled} value={(e.minutes || 0) % 60} onChange={ev => updateEntry(e.id, { minutes: Math.floor((e.minutes || 0) / 60) * 60 + +ev.target.value })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }}>
                              {[0, 15, 30, 45].map(m => <option key={m} value={m}>{m}</option>)}
                            </select>
                          </td>
                          <td style={{ padding: "6px 8px", textAlign: "right" }}>
                            <button disabled={disabled} className="btn btn-sm btn-danger" onClick={() => delEntry(e.id)}><span className="material-icons" style={{fontSize:16,verticalAlign:"middle"}}>close</span></button>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                  <tfoot>
                    <tr>
                      <td colSpan={3} style={{ padding: "12px 12px" }}>
                        <button className="btn btn-ghost" onClick={addEntry} style={{ fontSize: 12 }}>+ Zeile hinzufügen</button>
                      </td>
                      <td colSpan={2} style={{ padding: "12px 8px", textAlign: "right", fontSize: 13 }}>
                        <span style={{ color: "#888" }}>Tagestotal:&nbsp;</span><strong>{formatHours(dayTotalMins)}</strong>
                      </td>
                      <td></td>
                    </tr>
                    {selectedEmp && (daySoll > 0 || dayTotalMins > 0) && (
                      <tr style={{ borderTop: "1px solid #ece8e2", background: "#faf8f5" }}>
                        <td colSpan={3} style={{ padding: "6px 12px", fontSize: 11, color: "#888" }}>{selectedEmp.name}{daySoll === 0 ? " · Wochenende / Feiertag" : " · Soll heute"}</td>
                        <td colSpan={2} style={{ padding: "6px 8px", textAlign: "right", fontSize: 12 }}>
                          {daySoll > 0 ? (<><span style={{ color: "#888" }}>{Math.round(daySoll * 10) / 10}h Soll&nbsp;·&nbsp;</span><span style={{ fontWeight: 600, color: dayDiff === 0 ? "#555" : dayDiff > 0 ? "#2d6a4f" : "#8a1a1a" }}>{dayDiff === 0 ? "±0h" : `${dayDiff > 0 ? "+" : ""}${dayDiff}h`}</span></>) : (<span style={{ fontWeight: 600, color: "#b07848" }}>{Math.round(dayIst * 10) / 10}h</span>)}
                        </td>
                        <td></td>
                      </tr>
                    )}
                  </tfoot>
                </table>
              </div>

              {dayAbs.length > 0 && (
                <div className="card" style={{ marginTop: 16, padding: 0 }}>
                  <div style={{ padding: "10px 16px", borderBottom: "1px solid #ece8e2", fontSize: 11, letterSpacing: "0.1em", color: "#888" }}>ABSENZEN</div>
                  <table style={{ fontSize: 12 }}>
                    <thead><tr><th style={{ width: "22%" }}>Typ</th><th>Bemerkung</th><th style={{ width: 70 }}>Std</th><th style={{ width: 70 }}>Min</th><th style={{ width: 40 }}></th></tr></thead>
                    <tbody>
                      {dayAbs.map(a => {
                        const absTotalMins = a.startTime && a.endTime
                          ? (() => { const [sh,sm]=a.startTime.split(":").map(Number); const [eh,em]=a.endTime.split(":").map(Number); return Math.max(0,(eh*60+em)-(sh*60+sm)); })()
                          : (a.hours||0)*60+(a.minutes||0);
                        const absH = Math.floor(absTotalMins / 60);
                        const absM = absTotalMins % 60;
                        return (
                          <tr key={a.id}>
                            <td style={{ padding: "6px 8px" }}>
                              <select value={a.type || ""} onChange={ev => updateAbs(a.id, { type: ev.target.value })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }}>
                                {absenzTypes.map(t => <option key={t.id} value={t.id}>{t.label}</option>)}
                              </select>
                            </td>
                            <td style={{ padding: "6px 8px" }}>
                              <input value={a.note || ""} onChange={ev => updateAbs(a.id, { note: ev.target.value })} placeholder="Bemerkung…" style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }} />
                            </td>
                            <td style={{ padding: "6px 8px" }}>
                              <input type="number" min={0} max={24} value={absH} onChange={ev => updateAbs(a.id, { hours: +ev.target.value, minutes: absM })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }} />
                            </td>
                            <td style={{ padding: "6px 8px" }}>
                              <select value={absM} onChange={ev => updateAbs(a.id, { hours: absH, minutes: +ev.target.value })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }}>
                                {[0, 15, 30, 45].map(m => <option key={m} value={m}>{m}</option>)}
                              </select>
                            </td>
                            <td style={{ padding: "6px 8px", textAlign: "right" }}>
                              <button className="btn btn-danger" style={{ padding: "0 8px", height: 28, fontSize: 12 }} onClick={() => delAbs(a.id)}><span className="material-icons" style={{ fontSize: 16 }}>close</span></button>
                            </td>
                          </tr>
                        );
                      })}
                    </tbody>
                    <tfoot><tr><td colSpan={4} style={{ padding: "10px 12px" }}><button className="btn btn-ghost" onClick={addAbs} style={{ fontSize: 12 }}>+ Zeile hinzufügen</button></td><td></td></tr></tfoot>
                  </table>
                </div>
              )}

              {dayAbs.length === 0 && (
                <div style={{ marginTop: 10, display: "flex", gap: 6, justifyContent: "flex-end" }}>
                  <button className="btn btn-ghost" style={{ fontSize: 11, padding: "4px 12px", background: "#fff" }} onClick={addAbs}>+ Absenz erfassen</button>
                </div>
              )}

              {dayExpenses.length > 0 && (
                <div className="card" style={{ marginTop: 16, padding: 0 }}>
                  <div style={{ padding: "10px 16px", borderBottom: "1px solid #ece8e2", fontSize: 11, letterSpacing: "0.1em", color: "#888" }}>SPESEN</div>
                  <input ref={expReceiptInputRef} type="file" accept="image/*,application/pdf" onChange={handleExpReceiptUpload} style={{ display: "none" }} />
                  <table style={{ fontSize: 12 }}>
                    <thead><tr><th style={{ width: "22%" }}>Kategorie</th><th style={{ width: "20%" }}>Projekt</th><th>Beschreibung</th><th style={{ width: 90 }}>Betrag CHF</th><th style={{ width: 36, textAlign: "center" }}>Beleg</th><th style={{ width: 40 }}></th></tr></thead>
                    <tbody>
                      {dayExpenses.map(e => (
                        <tr key={e.id}>
                          <td style={{ padding: "6px 8px" }}><select value={e.category} onChange={ev => updateExp(e.id, { category: ev.target.value })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }}>{(data.settings.expenseCategories || EXPENSE_CATEGORIES).map(c => <option key={c}>{c}</option>)}</select></td>
                          <td style={{ padding: "6px 8px" }}><select value={e.projectId || ""} onChange={ev => updateExp(e.id, { projectId: ev.target.value })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }}><option value="">— kein Projekt —</option>{data.projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}</select></td>
                          <td style={{ padding: "6px 8px" }}><input value={e.description || ""} onChange={ev => updateExp(e.id, { description: ev.target.value })} placeholder="Beschreibung…" style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }} /></td>
                          <td style={{ padding: "6px 8px" }}><input type="number" step="0.05" min={0} value={e.amount || ""} onChange={ev => updateExp(e.id, { amount: +ev.target.value })} style={{ border: "1px solid #e0dbd4", height: 32, fontSize: 12 }} /></td>
                          <td style={{ padding: "6px 8px", textAlign: "center" }}>
                            {e.receiptData ? (
                              <button className="btn btn-ghost" title={e.receiptName || "Beleg anzeigen"} onClick={() => setReceiptView(e)} style={{ padding: "0 6px", height: 28 }}>
                                <span className="material-icons" style={{ fontSize: 16, verticalAlign: "middle", color: "#2d6a4f" }}>receipt_long</span>
                              </button>
                            ) : (
                              <button className="btn btn-ghost" title="Beleg hochladen" onClick={() => { setUploadingExpId(e.id); expReceiptInputRef.current?.click(); }} style={{ padding: "0 6px", height: 28 }}>
                                <span className="material-icons" style={{ fontSize: 16, verticalAlign: "middle", color: "#bbb" }}>upload</span>
                              </button>
                            )}
                          </td>
                          <td style={{ padding: "6px 8px", textAlign: "right" }}><button className="btn btn-danger" style={{ padding: "0 8px", height: 28, fontSize: 12 }} onClick={() => delExp(e.id)}><span className="material-icons" style={{ fontSize: 16 }}>close</span></button></td>
                        </tr>
                      ))}
                    </tbody>
                    <tfoot><tr><td colSpan={4} style={{ padding: "10px 12px" }}><button className="btn btn-ghost" onClick={addExp} style={{ fontSize: 12 }}>+ Zeile hinzufügen</button></td><td style={{ padding: "10px 8px", textAlign: "right", fontSize: 13 }}><span style={{ color: "#888" }}>Total:&nbsp;</span><strong>{(dayExpenses.reduce((s, e) => s + (e.amount || 0), 0)).toFixed(2)}</strong></td><td></td></tr></tfoot>
                  </table>
                </div>
              )}

              {dayExpenses.length === 0 && (
                <div style={{ marginTop: 10, display: "flex", gap: 6, justifyContent: "flex-end" }}>
                  <button className="btn btn-ghost" style={{ fontSize: 11, padding: "4px 12px", background: "#fff" }} onClick={addExp}>+ Spese erfassen</button>
                </div>
              )}
            </>
          ) : (
            /* WOCHENANSICHT */
            <div className="card" style={{ padding: 0, overflow: "hidden" }}>
              {void resizeTick /* trigger re-render during resize */}
              {resizingRef.current && <style>{`* { cursor: ${resizingRef.current.edge === "move" ? "grabbing" : "ns-resize"} !important; user-select: none !important; }`}</style>}
              {/* Zeitraster mit sticky Tageskopf */}
              <div ref={weekGridRef} style={{ isolation: "isolate", overflowY: "auto", maxHeight: "calc(100vh - 260px)" }} onMouseLeave={() => setWeekHover(null)}>

                {/* Sticky Tageskopf */}
                <div style={{ display: "flex", position: "sticky", top: 0, zIndex: 20, background: "#fff", borderBottom: "2px solid #e8e3dc", transform: "translateZ(0)", willChange: "transform" }}>
                  <div style={{ width: 44, flexShrink: 0 }} />
                  {weekDays.map(ds => {
                    const d = new Date(ds);
                    const isThisToday = ds === todayStr;
                    const isCurDay = ds === currentDate;
                    const ft = calFTs.find(f => f.date === ds);
                    const isFeiertag = ft && (ft.stundenDelta === 0 || ft.stundenDelta === null || ft.stundenDelta === undefined);
                    const dow = d.getDay();
                    const noWork = dow === 0 || dow === 6 || isFeiertag;
                    const isBeforeEintritt = !!(selectedEmp?.eintrittsdatum && ds < selectedEmp.eintrittsdatum);
                    const wkTotal = data.timeEntries.filter(e => e.date === ds && (!selectedEmpId || e.employeeId === selectedEmpId)).reduce((s, e) => s + (e.minutes || 0), 0);
                    const absMinutes = selectedEmpId ? (data.absences || []).filter(a => {
                      const matchDate = a.date === ds || (a.dateFrom && a.dateTo && ds >= a.dateFrom && ds <= a.dateTo);
                      return matchDate && a.employeeId === selectedEmpId;
                    }).reduce((s, a) => {
                      if (a.startTime && a.endTime) { const [sh,sm]=a.startTime.split(":").map(Number); const [eh,em]=a.endTime.split(":").map(Number); return s+(eh*60+em)-(sh*60+sm); }
                      const ex=(a.hours||0)*60+(a.minutes||0); return s+(ex>0?ex:getSollForDay(selectedEmp,ds)*60);
                    }, 0) : 0;
                    const istH = (wkTotal + absMinutes) / 60;
                    const sollH = selectedEmp ? getSollForDay(selectedEmp, ds) : 0;
                    const isPast = ds <= todayStr;
                    const isVacDay = selectedEmpId ? isApprovedVacationDay(selectedEmpId, ds) : false;
                    const isPendingVacDay = !isVacDay && selectedEmpId ? (data.ferienEntries || []).some(f => f.employeeId === selectedEmpId && f.status === "pending" && !f.originalData && ds >= f.dateFrom && ds <= f.dateTo) : false;
                    const diff = Math.round((istH - sollH) * 10) / 10;
                    return (
                      <div key={ds} style={{ flex: 1, textAlign: "center", padding: "8px 4px 6px", position: "relative", borderLeft: "1px solid #ece8e2", cursor: "pointer", background: isCurDay ? "#1a1a18" : isVacDay ? ((!isPast && Math.ceil((new Date(ds) - new Date(todayStr)) / 86400000) > 21) ? "#e8f0fa" : "#e8f5ee") : isPendingVacDay ? "#fff8e8" : isFeiertag ? "#f5f2ed" : "#fff" }}
                        onClick={() => { setCurrentDate(ds); setViewMode("tag"); }}>
                        <div style={{ fontSize: 9, letterSpacing: "0.1em", color: isCurDay ? "#e8e5df" : isBeforeEintritt ? "#ddd" : "#aaa" }}>{d.toLocaleDateString("de-CH", { weekday: "short" }).toUpperCase()}</div>
                        <div style={{ fontFamily: "'Playfair Display', serif", fontSize: 18, lineHeight: 1.2, color: isCurDay ? "#e8e5df" : isBeforeEintritt ? "#ccc" : isThisToday ? "var(--accent)" : isFeiertag ? "#bbb" : "#1a1a18" }}>{d.getDate()}</div>
                        <div style={{ position: "absolute", top: 8, right: 4, textAlign: "right", lineHeight: 1.2 }}>
                          {(() => {
                            if (!selectedEmp || noWork || isBeforeEintritt) return wkTotal > 0 ? <div style={{ fontSize: 9, fontWeight: 500, color: isCurDay ? "#e8e5df" : "#8a6a3a" }}>{istH.toFixed(1)}h</div> : null;
                            if (isVacDay && wkTotal === 0) {
                              const daysAway = Math.ceil((new Date(ds) - new Date(todayStr)) / 86400000);
                              const isPlanned = !isPast && daysAway > 21;
                              const color = isCurDay ? "#a0c4e8" : isPlanned ? "#1a4e8a" : "#2d6a4f";
                              return <div style={{ fontSize: 8, fontWeight: 600, color }}>Ferien</div>;
                            }
                            if (isPendingVacDay && wkTotal === 0) return <div style={{ fontSize: 8, fontWeight: 600, color: isCurDay ? "#e8e5df" : "#b5621e" }}>Ferien?</div>;
                            if (!isPast) return wkTotal > 0 ? <div style={{ fontSize: 9, fontWeight: 500, color: isCurDay ? "#e8e5df" : "#8a6a3a" }}>{istH.toFixed(1)}h</div> : null;
                            const saldoColor = isCurDay ? "#e8e5df" : diff === 0 ? "#555" : diff > 0 ? "#2d6a4f" : "#b5621e";
                            const saldoText = (wkTotal + absMinutes) === 0 ? `-${sollH.toFixed(1)}h` : diff === 0 ? `${istH.toFixed(1)}h` : `${diff > 0 ? "+" : ""}${diff.toFixed(1)}h`;
                            return <div style={{ fontSize: 9, fontWeight: 600, color: saldoColor }}>{saldoText}</div>;
                          })()}
                        </div>
                      </div>
                    );
                  })}
                </div>


                {/* Zeitgitter */}
                <div style={{ display: "flex" }}>
                <div style={{ width: 44, flexShrink: 0, pointerEvents: "none" }}>
                  {Array.from({ length: SLOT_COUNT }, (_, i) => {
                    const hour = i / 4;
                    const isOffHour = hour < 7 || hour >= 19;
                    const isHourMark = i % 4 === 0 && i > 0;
                    const labelColor = isOffHour ? "#7a9db8" : "#888";
                    return (
                      <div key={i} style={{ height: SLOT_H, display: "flex", alignItems: "flex-start", justifyContent: "flex-end", paddingRight: 6, paddingTop: 2, fontSize: 10, color: labelColor, lineHeight: 1, boxSizing: "border-box", borderTop: isHourMark ? `1px solid ${isOffHour ? "#e8eef2" : "#f0ede8"}` : "none", fontWeight: isOffHour ? 400 : 500 }}>
                        {isHourMark ? slotToTime(i) : ""}
                      </div>
                    );
                  })}
                </div>
                {weekDays.map(ds => {
                  const colTimed = data.timeEntries.filter(e => e.date === ds && e.startTime && (!selectedEmpId || e.employeeId === selectedEmpId));
                  const ft = calFTs.find(f => f.date === ds);
                  const isFeiertag = ft && (ft.stundenDelta === 0 || ft.stundenDelta === null || ft.stundenDelta === undefined);
                  const colBeforeEintritt = !!(selectedEmp?.eintrittsdatum && ds < selectedEmp.eintrittsdatum);
                  const isMaiTag = ds.slice(5) === "05-01";
                  const maiTagBlocked = isMaiTag && (data.settings.blockMaiTag !== false);
                  return (
                    <div key={ds} style={{ flex: 1, position: "relative", borderLeft: "1px solid #ece8e2", background: maiTagBlocked || colBeforeEintritt ? "#faf8f5" : isFeiertag ? "#f7f5f1" : "#fff" }}>
                      {Array.from({ length: SLOT_COUNT }, (_, slotIdx) => {
                        const isHour = slotIdx % 4 === 0;
                        const isHalf = slotIdx % 2 === 0 && !isHour;
                        const slotHour = slotIdx / 4;
                        const isOffHours = slotHour < 7 || slotHour >= 19;
                        const inHover = weekSelectStart?.dayStr === ds && weekHover?.dayStr === ds && slotIdx >= Math.min(weekSelectStart.slotIdx, weekHover.slotIdx) && slotIdx <= Math.max(weekSelectStart.slotIdx, weekHover.slotIdx);
                        const isStart = weekSelectStart?.dayStr === ds && slotIdx === weekSelectStart.slotIdx && !weekHover;
                        const inSel = weekForm?.dayStr === ds && slotIdx >= weekForm.startSlot && slotIdx <= weekForm.endSlot;
                        const blocked = maiTagBlocked || colBeforeEintritt;
                        const offBg = isOffHours ? "rgba(100,140,175,0.06)" : "transparent";
                        return (
                          <div key={slotIdx} style={{ height: SLOT_H, boxSizing: "border-box", borderTop: slotIdx > 0 ? `1px solid ${isHour ? (isOffHours ? "#e4eaf0" : "#e8e3dc") : isHalf ? "#f0ede8" : "#f7f5f2"}` : "none", background: inSel ? "rgba(212,168,90,0.15)" : (inHover || isStart) ? "rgba(212,168,90,0.08)" : offBg, cursor: blocked ? "default" : "crosshair" }}
                            onClick={() => !blocked && !resizingRef.current && handleSlotClick(ds, slotIdx)}
                            onMouseEnter={() => setWeekHover({ dayStr: ds, slotIdx })} />
                        );
                      })}
                      {colTimed.map(e => {
                        const r = resizingRef.current;
                        const isRes = r?.entryId === e.id;
                        const dispStart = isRes && r.previewStart !== undefined ? r.previewStart : e.startTime;
                        const dispMins = isRes && r.previewMins !== undefined ? r.previewMins : e.minutes;
                        const [sh, sm] = dispStart.split(":").map(Number);
                        const top = ((sh * 60 + sm) - SLOT_START_H * 60) / 15 * SLOT_H;
                        if (top < 0) return null;
                        const height = Math.max(SLOT_H, (dispMins / 15) * SLOT_H) - 2;
                        const proj = data.projects.find(p => p.id === e.projectId);
                        const bg = PROJ_COLORS[data.projects.findIndex(p => p.id === e.projectId) % PROJ_COLORS.length] || PROJ_COLORS[0];
                        const handleH = Math.min(5, Math.floor(height / 3));
                        const phaseDisplay = e.phaseId ? (() => {
                          const cp = (proj?.customPhases||[]).find(c => c.id === e.phaseId);
                          if (cp) return cp.label;
                          const sia = SIA_PHASES.find(p => p.id === e.phaseId);
                          return sia ? (e.positionId ? `${e.positionId}·${e.phaseId}` : e.phaseId) : e.phaseId;
                        })() : null;
                        const startResizing = (ev, edge) => {
                          if (isMonthClosed(ds)) return;
                          ev.stopPropagation(); ev.preventDefault();
                          const [s, m] = e.startTime.split(":").map(Number);
                          const origSlot = Math.round((s * 60 + m - SLOT_START_H * 60) / 15);
                          resizingRef.current = { entryId: e.id, dayStr: ds, empId: selectedEmpId, edge, moved: false, startAbsY: ev.clientY + (weekGridRef.current?.scrollTop || 0), origMinutes: e.minutes, origStartTime: e.startTime, origSlot, previewStart: e.startTime, previewMins: e.minutes };
                          setResizeTick(t => t + 1);
                        };
                        const isMoving = isRes && resizingRef.current?.edge === "move";
                        return (
                          <div key={e.id} title={`${proj?.name||"—"}: ${(dispMins/60).toFixed(1)}h${e.description?" · "+e.description:""}`}
                            onContextMenu={ev => { ev.preventDefault(); setContextMenu({ x: ev.clientX, y: ev.clientY, entry: e }); }}
                            style={{ position: "absolute", top, left: 2, right: 2, height, background: bg, borderRadius: 3, padding: `${handleH}px 4px`, fontSize: 9, color: "#fff", overflow: "hidden", zIndex: isRes ? 10 : 2, boxSizing: "border-box", userSelect: "none" }}>
                            <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: handleH, cursor: "ns-resize", background: "rgba(0,0,0,0.15)", borderRadius: "3px 3px 0 0", zIndex: 1 }} onMouseDown={ev => startResizing(ev, "top")} />
                            <div style={{ position: "absolute", top: handleH, left: 0, right: 0, bottom: handleH, cursor: isMoving ? "grabbing" : "grab", zIndex: 1 }} onMouseDown={ev => startResizing(ev, "move")} />
                            <div style={{ fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", paddingTop: 1, pointerEvents: "none" }}>{proj?.name||"—"}</div>
                            {phaseDisplay && height > 24 && <div style={{ fontSize: 7, opacity: 0.8, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", marginTop: 0, pointerEvents: "none" }}>{phaseDisplay}</div>}
                            {height > 36 && <div style={{ fontSize: 8, opacity: 0.85, marginTop: 1, pointerEvents: "none" }}>{slotToTime((sh*60+sm-SLOT_START_H*60)/15)} – {slotToTime((sh*60+sm-SLOT_START_H*60)/15 + dispMins/15)}</div>}
                            {height > 52 && e.description && <div style={{ opacity: 0.75, fontSize: 8, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", pointerEvents: "none" }}>{e.description}</div>}
                            <div style={{ position: "absolute", bottom: 0, left: 0, right: 0, height: handleH, cursor: "ns-resize", background: "rgba(0,0,0,0.15)", borderRadius: "0 0 3px 3px", zIndex: 1 }} onMouseDown={ev => startResizing(ev, "bottom")} />
                          </div>
                        );
                      })}
                      {/* Absenz-Blöcke im Zeitraster */}
                      {(data.absences || []).filter(a => a.date === ds && a.startTime && a.endTime && (!selectedEmpId || a.employeeId === selectedEmpId)).map(a => {
                        const rA = resizingRef.current;
                        const isResA = rA?.entryId === a.id;
                        const dispStartA = isResA && rA.previewStart !== undefined ? rA.previewStart : a.startTime;
                        const [shA, smA] = dispStartA.split(":").map(Number);
                        const dispMinsA = isResA && rA.previewMins !== undefined ? rA.previewMins : (() => { const [eh,em]=a.endTime.split(":").map(Number); return (eh*60+em)-(shA*60+smA); })();
                        const topA = ((shA * 60 + smA) - SLOT_START_H * 60) / 15 * SLOT_H;
                        if (topA < 0) return null;
                        const heightA = Math.max(SLOT_H, (dispMinsA / 15) * SLOT_H) - 2;
                        const handleHA = Math.min(5, Math.floor(heightA / 3));
                        const t = absenzTypes.find(x => x.id === a.type);
                        const bg = t?.color || "#888";
                        const isMovingA = isResA && rA?.edge === "move";
                        const origDurA = (() => { const [eh,em]=a.endTime.split(":").map(Number); return (eh*60+em)-(shA*60+smA); })();
                        const startResizingA = (ev, edge) => {
                          if (isMonthClosed(ds)) return;
                          ev.stopPropagation(); ev.preventDefault();
                          const [s, m] = a.startTime.split(":").map(Number);
                          const origSlot = Math.round((s * 60 + m - SLOT_START_H * 60) / 15);
                          resizingRef.current = { entryId: a.id, dayStr: ds, empId: selectedEmpId, isAbs: true, edge, moved: false, startAbsY: ev.clientY + (weekGridRef.current?.scrollTop || 0), origMinutes: origDurA, origStartTime: a.startTime, origSlot, previewStart: a.startTime, previewMins: origDurA };
                          setResizeTick(t => t + 1);
                        };
                        return (
                          <div key={a.id} title={`${t?.label || a.type}: ${(dispMinsA/60).toFixed(1)}h${a.note ? " · " + a.note : ""}`}
                            style={{ position: "absolute", top: topA, left: 2, right: 2, height: heightA, background: bg, opacity: 0.85, borderRadius: 3, padding: `${handleHA}px 5px`, fontSize: 9, color: "#fff", overflow: "hidden", zIndex: isResA ? 10 : 2, boxSizing: "border-box", userSelect: "none" }}>
                            <div style={{ position: "absolute", top: 0, left: 0, right: 0, height: handleHA, cursor: "ns-resize", background: "rgba(0,0,0,0.15)", borderRadius: "3px 3px 0 0", zIndex: 1 }} onMouseDown={ev => startResizingA(ev, "top")} />
                            <div style={{ position: "absolute", top: handleHA, left: 0, right: 0, bottom: handleHA, cursor: isMovingA ? "grabbing" : "grab", zIndex: 1 }} onMouseDown={ev => startResizingA(ev, "move")} />
                            <div style={{ fontWeight: 700, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", pointerEvents: "none" }}>{t?.label || a.type}</div>
                            {heightA > 28 && <div style={{ fontSize: 8, opacity: 0.85, marginTop: 1, pointerEvents: "none" }}>{dispStartA} – {slotToTime((shA*60+smA-SLOT_START_H*60)/15 + dispMinsA/15)}</div>}
                            {heightA > 44 && a.note && <div style={{ opacity: 0.75, fontSize: 8, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", pointerEvents: "none" }}>{a.note}</div>}
                            <div style={{ position: "absolute", bottom: 0, left: 0, right: 0, height: handleHA, cursor: "ns-resize", background: "rgba(0,0,0,0.15)", borderRadius: "0 0 3px 3px", zIndex: 1 }} onMouseDown={ev => startResizingA(ev, "bottom")} />
                          </div>
                        );
                      })}
                      {ds === todayStr && (() => {
                        const now = new Date();
                        const top = (now.getHours() * 60 + now.getMinutes() - SLOT_START_H * 60) / 15 * SLOT_H;
                        if (top < 0 || top > SLOT_COUNT * SLOT_H) return null;
                        return <div style={{ position: "absolute", left: 0, right: 0, top, height: 2, background: "var(--accent)", zIndex: 5, pointerEvents: "none" }} />;
                      })()}
                    </div>
                  );
                })}
                </div>{/* end display:flex time grid */}
              </div>{/* end scroll container */}


              {weekSelectStart && !weekForm && (
                <div style={{ borderTop: "1px solid #ece8e2", padding: "8px 16px 8px 56px", fontSize: 11, color: "#888", background: "#faf8f5", display: "flex", alignItems: "center", gap: 8 }}>
                  <span style={{ color: "var(--accent)", fontWeight: 600 }}>{slotToTime(weekSelectStart.slotIdx)}</span>
                  <span>ausgewählt — Endzeit anklicken</span>
                  <button className="btn btn-ghost" onClick={() => setWeekSelectStart(null)} style={{ marginLeft: "auto", fontSize: 11, padding: "2px 8px" }}>Abbrechen</button>
                </div>
              )}

              {weekForm && (() => {
                const isAbsMode = weekForm.mode === "absenz";
                const accentColor = isAbsMode ? (absenzTypes.find(t => t.id === weekForm.absType)?.color || "#6b5a8a") : "var(--accent)";
                const proj = data.projects.find(p => p.id === weekForm.projectId);
                const enabledPhases = (proj?.enabledPhases||[]).map(id => SIA_PHASES.find(ph => ph.id === id)).filter(Boolean);
                const customPhases = proj?.customPhases || [];
                const positions = proj?.positions || [];
                const hasPhase = enabledPhases.length > 0 || customPhases.length > 0 || positions.some(pos => (pos.enabledPhases||[]).length > 0);
                const combinedVal = weekForm.phaseId ? (weekForm.positionId ? weekForm.phaseId+"|"+weekForm.positionId : weekForm.phaseId) : "";
                const onPhaseChange = (val) => { if (!val) { setWeekForm({...weekForm, phaseId:"", positionId:""}); return; } const [ph,pos]=val.split("|"); setWeekForm({...weekForm, phaseId:ph, positionId:pos||""}); };
                const mins = (weekForm.endSlot - weekForm.startSlot + 1) * 15;
                return (
                  <div style={{ borderTop: `2px solid ${accentColor}`, padding: "12px 16px", background: "#faf8f5" }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 10 }}>
                      <div style={{ fontSize: 10, letterSpacing: "0.1em", color: accentColor, fontWeight: 600 }}>
                        {new Date(weekForm.dayStr).toLocaleDateString("de-CH", { weekday: "long", day: "numeric", month: "long" }).toUpperCase()} · {slotToTime(weekForm.startSlot)} – {slotToTime(weekForm.endSlot + 1)} · {mins >= 60 ? `${mins%60===0?mins/60:(mins/60).toFixed(1)}h` : `${mins} min`}
                      </div>
                      <div style={{ display: "flex", gap: 4, marginLeft: "auto", flexShrink: 0 }}>
                        {[["projekt", "Projekt"], ["absenz", "Intern / Absenz"]].map(([m, label]) => (
                          <button key={m} className={`pill${weekForm.mode === m ? " active" : ""}`} onClick={() => setWeekForm({ ...weekForm, mode: m })}>{label}</button>
                        ))}
                      </div>
                    </div>
                    <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "flex-end" }}>
                      {isAbsMode ? (
                        <>
                          <div style={{ flex: "1 1 180px" }}>
                            <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>TYP</div>
                            <select value={weekForm.absType || ""} onChange={e => setWeekForm({ ...weekForm, absType: e.target.value })} style={{ height: 34, fontSize: 12, width: "100%" }} autoFocus>
                              <option value="">— wählen —</option>
                              {absenzTypes.map(t => <option key={t.id} value={t.id}>{t.label}</option>)}
                            </select>
                          </div>
                          <div style={{ flex: "2 1 180px" }}>
                            <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>NOTIZ</div>
                            <input value={weekForm.absNote || ""} onChange={e => setWeekForm({ ...weekForm, absNote: e.target.value })} placeholder="Notiz…" style={{ height: 34, fontSize: 12, width: "100%", boxSizing: "border-box" }} onKeyDown={e => e.key === "Enter" && saveWeekEntry()} />
                          </div>
                        </>
                      ) : (
                        <>
                          <div style={{ flex: "1 1 150px" }}>
                            <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>PROJEKT</div>
                            <select value={weekForm.projectId} onChange={e => { const proj = data.projects.find(p => p.id === e.target.value); const { phaseId, positionId } = getFirstPhaseForProject(proj); setWeekForm({...weekForm, projectId:e.target.value, phaseId, positionId}); }} style={{ height: 34, fontSize: 12, width: "100%" }} autoFocus>
                              <option value="">— wählen —</option>
                              {data.projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
                            </select>
                          </div>
                          {hasPhase && (
                            <div style={{ flex: "1 1 120px" }}>
                              <div style={{ fontSize: 10, color: !combinedVal ? "#b5621e" : "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>PHASE *</div>
                              <select value={combinedVal} onChange={e => onPhaseChange(e.target.value)} style={{ height: 34, fontSize: 12, width: "100%", borderColor: !combinedVal ? "#b5621e" : undefined }}>
                                <option value="">— wählen —</option>
                                {customPhases.map(cp => <option key={cp.id} value={cp.id}>{cp.label}</option>)}
                                {enabledPhases.map(ph => <option key={ph.id} value={ph.id}>{ph.id} · {ph.label.split(" ").slice(1).join(" ")}</option>)}
                                {positions.flatMap(pos => (pos.enabledPhases||[]).map(id=>SIA_PHASES.find(ph=>ph.id===id)).filter(Boolean).map(ph => <option key={ph.id+"|"+pos.code} value={ph.id+"|"+pos.code}>{pos.code} · {ph.id}</option>))}
                              </select>
                            </div>
                          )}
                          <div style={{ flex: "2 1 180px" }}>
                            <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>TÄTIGKEIT</div>
                            <input value={weekForm.description} onChange={e => setWeekForm({...weekForm, description:e.target.value})} placeholder="Beschreibung…" style={{ height: 34, fontSize: 12, width: "100%", boxSizing: "border-box" }} onKeyDown={e => e.key === "Enter" && saveWeekEntry()} />
                          </div>
                        </>
                      )}
                      <div style={{ display: "flex", gap: 6 }}>
                        <button className="btn btn-primary" onClick={saveWeekEntry} disabled={isAbsMode ? !weekForm.absType : (!weekForm.projectId || (hasPhase && !combinedVal))} style={{ fontSize: 12, height: 34 }}>Speichern</button>
                        <button className="btn btn-ghost" onClick={() => { setWeekForm(null); setWeekSelectStart(null); }} style={{ fontSize: 12, height: 34 }}><span className="material-icons" style={{fontSize:16,verticalAlign:"middle"}}>close</span></button>
                      </div>
                    </div>
                  </div>
                );
              })()}

              {weekEditForm && !weekForm && (() => {
                if (weekEditForm.isAbs) {
                  const a = (data.absences||[]).find(x => x.id === weekEditForm.entryId);
                  if (!a) return null;
                  const t = absenzTypes.find(x => x.id === a.type);
                  const accentColor = t?.color || "#6b5a8a";
                  const [shE,smE]=a.startTime.split(":").map(Number); const [ehE,emE]=a.endTime.split(":").map(Number);
                  const durE = (ehE*60+emE)-(shE*60+smE);
                  return (
                    <div style={{ borderTop: `2px solid ${accentColor}`, padding: "12px 16px", background: "#faf8f5" }}>
                      <div style={{ fontSize: 10, color: accentColor, fontWeight: 600, letterSpacing: "0.1em", marginBottom: 10 }}>
                        {new Date(a.date).toLocaleDateString("de-CH", { weekday: "long", day: "numeric", month: "long" }).toUpperCase()} · {a.startTime} – {a.endTime} · {(durE/60).toFixed(1)}h
                      </div>
                      <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "flex-end" }}>
                        <div style={{ flex: "1 1 180px" }}>
                          <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>TYP</div>
                          <select value={a.type || ""} onChange={ev => update("absences", (data.absences||[]).map(x => x.id === a.id ? {...x, type: ev.target.value} : x))} style={{ height: 34, fontSize: 12, width: "100%" }} autoFocus>
                            <option value="">— wählen —</option>
                            {absenzTypes.map(at => <option key={at.id} value={at.id}>{at.label}</option>)}
                          </select>
                        </div>
                        <div style={{ flex: "2 1 180px" }}>
                          <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>NOTIZ</div>
                          <input value={a.note || ""} onChange={ev => update("absences", (data.absences||[]).map(x => x.id === a.id ? {...x, note: ev.target.value} : x))} placeholder="Notiz…" style={{ height: 34, fontSize: 12, width: "100%", boxSizing: "border-box" }} />
                        </div>
                        <div style={{ display: "flex", gap: 6 }}>
                          <button className="btn btn-danger" onClick={() => { update("absences", (data.absences||[]).filter(x => x.id !== a.id)); setWeekEditForm(null); }} style={{ fontSize: 12, height: 34 }}>Löschen</button>
                          <button className="btn btn-ghost" onClick={() => setWeekEditForm(null)} style={{ fontSize: 12, height: 34 }}><span className="material-icons" style={{fontSize:16,verticalAlign:"middle"}}>close</span></button>
                        </div>
                      </div>
                    </div>
                  );
                } else {
                  const e = data.timeEntries.find(x => x.id === weekEditForm.entryId);
                  if (!e) return null;
                  const projE = data.projects.find(p => p.id === e.projectId);
                  const enabledPhasesE = (projE?.enabledPhases||[]).map(id => SIA_PHASES.find(ph => ph.id === id)).filter(Boolean);
                  const customPhasesE = projE?.customPhases || [];
                  const positionsE = projE?.positions || [];
                  const hasPhaseE = enabledPhasesE.length > 0 || customPhasesE.length > 0 || positionsE.some(pos => (pos.enabledPhases||[]).length > 0);
                  const combinedValE = e.phaseId ? (e.positionId ? e.phaseId+"|"+e.positionId : e.phaseId) : "";
                  const onPhaseChangeE = (val) => { if (!val) { updateEntry(e.id, { phaseId: "", positionId: "" }); return; } const [ph, pos] = val.split("|"); updateEntry(e.id, { phaseId: ph, positionId: pos || "" }); };
                  const isInvoiced = !!e.invoiceId;
                  const disabledE = isInvoiced || isMonthClosed(e.date);
                  const bgE = PROJ_COLORS[data.projects.findIndex(p => p.id === e.projectId) % PROJ_COLORS.length] || PROJ_COLORS[0];
                  const durE = e.startTime && e.endTime ? (() => { const [sh,sm]=e.startTime.split(":").map(Number); const [eh,em]=e.endTime.split(":").map(Number); return (eh*60+em)-(sh*60+sm); })() : e.minutes;
                  return (
                    <div style={{ borderTop: `2px solid ${bgE}`, padding: "12px 16px", background: "#faf8f5" }}>
                      <div style={{ fontSize: 10, color: bgE, fontWeight: 600, letterSpacing: "0.1em", marginBottom: 10, display: "flex", alignItems: "center", gap: 8 }}>
                        <span>{new Date(e.date).toLocaleDateString("de-CH", { weekday: "long", day: "numeric", month: "long" }).toUpperCase()} · {e.startTime} – {e.endTime} · {(durE/60).toFixed(1)}h</span>
                        {isInvoiced && <span style={{ fontSize: 9, color: "#b5621e", fontWeight: 400 }}>verrechnet</span>}
                      </div>
                      <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "flex-end" }}>
                        <div style={{ flex: "1 1 150px" }}>
                          <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>PROJEKT</div>
                          <select disabled={disabledE} value={e.projectId} onChange={ev => updateEntry(e.id, { projectId: ev.target.value, phaseId: "", positionId: "" })} style={{ height: 34, fontSize: 12, width: "100%" }} autoFocus>
                            <option value="">—</option>
                            {data.projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
                          </select>
                        </div>
                        {hasPhaseE && (
                          <div style={{ flex: "1 1 120px" }}>
                            <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>PHASE</div>
                            <select disabled={disabledE} value={combinedValE} onChange={ev => onPhaseChangeE(ev.target.value)} style={{ height: 34, fontSize: 12, width: "100%" }}>
                              <option value="">—</option>
                              {customPhasesE.map(cp => <option key={cp.id} value={cp.id}>{cp.label}</option>)}
                              {enabledPhasesE.map(ph => <option key={ph.id} value={ph.id}>{ph.id} · {ph.label.split(" ").slice(1).join(" ")}</option>)}
                              {positionsE.flatMap(pos => (pos.enabledPhases||[]).map(id=>SIA_PHASES.find(ph=>ph.id===id)).filter(Boolean).map(ph => <option key={ph.id+"|"+pos.code} value={ph.id+"|"+pos.code}>{pos.code} · {ph.id}</option>))}
                            </select>
                          </div>
                        )}
                        <div style={{ flex: "2 1 180px" }}>
                          <div style={{ fontSize: 10, color: "#aaa", marginBottom: 3, letterSpacing: "0.06em" }}>TÄTIGKEIT</div>
                          <input disabled={disabledE} value={e.description || ""} onChange={ev => updateEntry(e.id, { description: ev.target.value })} placeholder="Beschreibung…" style={{ height: 34, fontSize: 12, width: "100%", boxSizing: "border-box" }} />
                        </div>
                        <div style={{ display: "flex", gap: 6 }}>
                          <button className="btn btn-danger" disabled={disabledE} onClick={() => { delEntry(e.id); setWeekEditForm(null); }} style={{ fontSize: 12, height: 34, opacity: disabledE ? 0.4 : 1 }}>Löschen</button>
                          <button className="btn btn-ghost" onClick={() => setWeekEditForm(null)} style={{ fontSize: 12, height: 34 }}><span className="material-icons" style={{fontSize:16,verticalAlign:"middle"}}>close</span></button>
                        </div>
                      </div>
                    </div>
                  );
                }
              })()}
            </div>
          )}
        </div>

        {/* Modal: Ferien-Übersicht */}
        {ferienUebersicht && selectedEmp && (
          <Modal title={`Ferien — ${selectedEmp.name}`} onClose={() => setFerienUebersicht(false)}>
            {(() => {
              const today = new Date().toISOString().slice(0, 10);
              const empFerien = (data.ferienEntries || []).filter(f => f.employeeId === selectedEmp.id).sort((a, b) => (b.dateFrom || "").localeCompare(a.dateFrom || ""));
              if (empFerien.length === 0) return <div style={{ fontSize: 13, color: "#aaa", padding: "12px 0" }}>Keine Ferieneinträge vorhanden.</div>;
              const groups = [
                { label: "Pendent", entries: empFerien.filter(f => f.status === "pending") },
                { label: "Bestätigt", entries: empFerien.filter(f => f.status !== "pending") },
              ];
              return groups.filter(g => g.entries.length > 0).map(g => (
                <div key={g.label} style={{ marginBottom: 16 }}>
                  <div style={{ fontSize: 10, letterSpacing: "0.1em", color: "#888", marginBottom: 8 }}>{g.label.toUpperCase()}</div>
                  {g.entries.map(f => {
                    const isPending = f.status === "pending";
                    const isEditPending = isPending && !!f.originalData;
                    const displayTo = isPending && f.originalData ? f.originalData.dateTo : f.dateTo;
                    const isPast = displayTo < today;
                    return (
                      <div key={f.id} style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 0", borderBottom: "1px solid #f0ede8" }}>
                        <div style={{ flex: 1 }}>
                          {isEditPending ? (
                            <>
                              <div style={{ fontSize: 11, color: "#aaa", textDecoration: "line-through" }}>{formatDate(f.originalData.dateFrom)} – {formatDate(f.originalData.dateTo)}</div>
                              <div style={{ fontSize: 13, color: "#222" }}>{formatDate(f.dateFrom)} – {formatDate(f.dateTo)} <span style={{ fontSize: 10, color: "#b5621e", fontWeight: 600 }}>neu</span></div>
                            </>
                          ) : (
                            <div style={{ fontSize: 13, color: "#222" }}>{formatDate(f.dateFrom)} – {formatDate(f.dateTo)}</div>
                          )}
                          {f.note && <div style={{ fontSize: 11, color: "#aaa", marginTop: 2 }}>{f.note}</div>}
                        </div>
                        <span style={{ fontSize: 10, padding: "2px 10px", borderRadius: 20, background: isPending ? "#fff8e8" : "#e8f5ee", color: isPending ? "#b5621e" : "#2d6a4f", fontWeight: 600, flexShrink: 0, whiteSpace: "nowrap" }}>
                          {isEditPending ? "Änderung" : isPending ? "pendent" : "bestätigt"}
                        </span>
                        {!isPast && !isPending && (
                          <button onClick={() => { setFerienUebersicht(false); editFerien(f); }} style={{ background: "none", border: "none", color: "#888", cursor: "pointer", fontSize: 13, padding: "0 3px", lineHeight: 1, flexShrink: 0 }} title="Bearbeiten"><span className="material-icons" style={{fontSize:16,verticalAlign:"middle"}}>edit</span></button>
                        )}
                        {isPast
                          ? <span style={{ fontSize: 11, color: "#ccc", flexShrink: 0, padding: "0 4px" }}>vergangen</span>
                          : isEditPending
                            ? <button onClick={() => cancelFerienEdit(f.id)} style={{ background: "none", border: "none", color: "#bbb", cursor: "pointer", fontSize: 13, padding: "0 2px", lineHeight: 1, flexShrink: 0 }} title="Änderung verwerfen"><span className="material-icons" style={{fontSize:16,verticalAlign:"middle"}}>close</span></button>
                            : <button className="btn btn-sm btn-danger" onClick={() => deleteFerien(f.id)} title="Zurücknehmen"><span className="material-icons" style={{fontSize:16,verticalAlign:"middle"}}>close</span></button>
                        }
                      </div>
                    );
                  })}
                </div>
              ));
            })()}
          </Modal>
        )}

        {/* Modal: Ferienantrag */}
        {antragModal === "ferien" && (
          <Modal title="Ferien beantragen" onClose={() => setAntragModal(null)} onSave={saveAntrag} overflow>
            {employees.length > 1 && (
              <FormField label="Mitarbeiter">
                <select value={antragForm.employeeId || ""} onChange={e => setAntragForm({ ...antragForm, employeeId: e.target.value })}>
                  <option value="">— wählen —</option>
                  {employees.map(e => <option key={e.id} value={e.id}>{e.name}</option>)}
                </select>
              </FormField>
            )}
            <div className="form-row">
              <FormField label="Von"><DatePicker value={antragForm.dateFrom || ""} onChange={e => setAntragForm({ ...antragForm, dateFrom: e.target.value })} /></FormField>
              <FormField label="Bis"><DatePicker value={antragForm.dateTo || ""} onChange={e => setAntragForm({ ...antragForm, dateTo: e.target.value })} /></FormField>
            </div>
            <FormField label="Notiz (optional)"><input value={antragForm.note || ""} onChange={e => setAntragForm({ ...antragForm, note: e.target.value })} /></FormField>
          </Modal>
        )}

        <div>
          {employees.length > 0 && (
            <div className="card" style={{ marginBottom: 12, padding: "10px 14px" }}>
              <div style={{ fontSize: 10, letterSpacing: "0.08em", color: "#888", marginBottom: 6 }}>MITARBEITER</div>
              {(() => {
                const canSwitchEmployee = isAdmin || (currentUser?.permissions || []).includes("mitarbeiter");
                if (canSwitchEmployee) {
                  return (
                    <select value={selectedEmpId} onChange={e => setSelectedEmpId(e.target.value)} style={{ width: "100%", fontSize: 13 }}>
                      {isAdmin && <option value="">— Alle / Kein Filter —</option>}
                      {employees.map(e => <option key={e.id} value={e.id}>{e.name} · {e.pensum||100}%</option>)}
                    </select>
                  );
                }
                return (
                  <div style={{ fontSize: 13, fontWeight: 500, color: "#1a1a18" }}>
                    {employees.find(e => e.id === myEmployeeId)?.name || "—"}
                  </div>
                );
              })()}
            </div>
          )}
          <div className="card">
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
            <NavArrows onPrev={() => { const d = new Date(viewDate); d.setMonth(d.getMonth() - 1); setCurrentDate(d.toISOString().slice(0, 10)); }} onNext={() => { const d = new Date(viewDate); d.setMonth(d.getMonth() + 1); setCurrentDate(d.toISOString().slice(0, 10)); }} />
            <div style={{ fontFamily: "'Playfair Display', serif", fontSize: 14, textAlign: "center" }}>{monthLabel}</div>
            <div style={{ width: 55 }} />
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 2, marginBottom: 6 }}>
            {["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"].map(d => (
              <div key={d} style={{ fontSize: 9, color: "#aaa", textAlign: "center", letterSpacing: "0.05em", padding: "4px 0" }}>{d}</div>
            ))}
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 2 }}>
            {monthCells.map((cell, i) => {
              if (!cell) return <div key={i}></div>;
              const isSelected = cell.dateStr === currentDate;
              const isCurToday = cell.dateStr === new Date().toISOString().slice(0, 10);
              const hasEntries = cell.mins > 0;
              const noWork = cell.isWeekend || cell.isFeiertag;
              const isVortag = cell.isVortag && !cell.isFeiertag;

              let bg = "transparent";
              if (isSelected) bg = "#1a1a18";
              else if (cell.isFerien) bg = "#e8f5ee";
              else if (cell.isPendingFerien) bg = "#fff8e8";
              else if (cell.isAbsenz) bg = "#f0f0f0";
              else if (cell.isPendingAbsenz) bg = "#f5f0f8";
              else if (hasEntries && !noWork) bg = "#faf3e4";
              else if (hasEntries && noWork) bg = "#f5f0e8";

              const textColor = isSelected ? "#e8e5df" : (noWork && !hasEntries) ? "#ccc" : "#1a1a18";

              return (
                <button key={i} onClick={() => setCurrentDate(cell.dateStr)} style={{
                  aspectRatio: "1", padding: 1,
                  background: bg, color: textColor,
                  border: isCurToday && !isSelected ? "1.5px solid var(--accent)" : "1px solid transparent",
                  borderRadius: 4, fontFamily: "inherit",
                  display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
                  cursor: "pointer", transition: "all 0.1s",
                }}>
                  <div style={{ fontSize: 11, fontWeight: noWork ? 400 : 500 }}>{cell.day}</div>
                  {!selectedEmp && hasEntries && (
                    <div style={{ fontSize: 7, color: isSelected ? "#e8e5df" : "#8a6a3a", marginTop: 1 }}>
                      {(cell.mins / 60).toFixed(1)}h
                    </div>
                  )}
                  {selectedEmp && hasEntries && (
                    <div style={{ fontSize: 7, color: isSelected ? "#e8e5df" : "#2d6a4f", fontWeight: 600, marginTop: 1 }}>
                      {(cell.mins / 60).toFixed(1)}h
                    </div>
                  )}
                  {(cell.isFerien || cell.isPendingFerien) && !isSelected && (
                    <div style={{ marginTop: 2, display: "flex", justifyContent: "center" }}>
                      {cell.isPendingFerien ? (
                        <div style={{ fontSize: 7, color: "#b5621e", lineHeight: 1 }}>?</div>
                      ) : (
                        <svg width="7" height="7" viewBox="0 0 12 12" fill="none">
                          <circle cx="6" cy="6" r="2.5" fill="#2d6a4f"/>
                          <line x1="6" y1="0" x2="6" y2="2" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                          <line x1="6" y1="10" x2="6" y2="12" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                          <line x1="0" y1="6" x2="2" y2="6" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                          <line x1="10" y1="6" x2="12" y2="6" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                          <line x1="1.8" y1="1.8" x2="3.2" y2="3.2" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                          <line x1="8.8" y1="8.8" x2="10.2" y2="10.2" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                          <line x1="10.2" y1="1.8" x2="8.8" y2="3.2" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                          <line x1="3.2" y1="8.8" x2="1.8" y2="10.2" stroke="#2d6a4f" strokeWidth="1.2" strokeLinecap="round"/>
                        </svg>
                      )}
                    </div>
                  )}
                  {(cell.isAbsenz || cell.isPendingAbsenz) && !isSelected && !cell.isFerien && !cell.isPendingFerien && (
                    <div style={{ fontSize: 7, color: cell.isPendingAbsenz ? "#7a5a9a" : "#888", marginTop: 1 }}>
                      {cell.isPendingAbsenz ? "?" : <span className="material-icons" style={{fontSize:8,verticalAlign:"middle"}}>close</span>}
                    </div>
                  )}
                  {isVortag && !hasEntries && !cell.isFerien && !cell.isAbsenz && (
                    <div style={{ fontSize: 7, color: "#bbb", marginTop: 1 }}>−1h</div>
                  )}
                </button>
              );
            })}
          </div>

          <div style={{ marginTop: 16, paddingTop: 14, borderTop: "1px solid #ece8e2", display: "flex", justifyContent: "space-between", fontSize: 11 }}>
            <span style={{ color: "#888" }}>Monatstotal</span>
            <strong>{formatHours(monthTotalMins)}</strong>
          </div>
        </div>

        {selectedEmp && (
          <div className="card" style={{ marginTop: 12, padding: "14px 16px" }}>
            <div style={{ paddingBottom: 8, marginBottom: 8, borderBottom: "1px solid #ece8e2" }}>
              <div className="section-label" style={{ marginBottom: 4 }}>Saldo seit Eintritt</div>
              {totalSaldo && [
                { label: "Soll bis heute", value: `${totalSaldo.soll}h` },
                { label: "IST", value: `${totalSaldo.ist}h` },
              ].map(r => (
                <div key={r.label} style={{ display: "flex", justifyContent: "space-between", padding: "1px 0", fontSize: 11, color: "#555" }}>
                  <span>{r.label}</span><span>{r.value}</span>
                </div>
              ))}
              {totalSaldo && (
                <div style={{ display: "flex", justifyContent: "space-between", marginTop: 4, paddingTop: 4, borderTop: "1px solid #e0dbd4", fontFamily: "'Playfair Display', serif", fontSize: 13 }}>
                  <span>Saldo</span>
                  <strong style={{ color: totalSaldo.saldo >= 0 ? "#2d6a4f" : "#8a1a1a" }}>
                    {totalSaldo.saldo === 0 ? "0h" : `${totalSaldo.saldo > 0 ? "+" : ""}${totalSaldo.saldo}h`}
                  </strong>
                </div>
              )}
            </div>
            <div style={{ paddingBottom: 8, marginBottom: 8, borderBottom: "1px solid #ece8e2" }}>
              <div className="section-label" style={{ marginBottom: 4 }}>Monatssaldo — {new Date(currentDate + "T12:00:00").toLocaleString("de-CH", { month: "long" })}</div>
              {monthSaldo && [
                { label: "Soll", value: `${monthSaldo.soll}h` },
                { label: "IST", value: `${monthSaldo.ist}h` },
              ].map(r => (
                <div key={r.label} style={{ display: "flex", justifyContent: "space-between", padding: "1px 0", fontSize: 11, color: "#555" }}>
                  <span>{r.label}</span><span>{r.value}</span>
                </div>
              ))}
              {monthSaldo && (
                <div style={{ display: "flex", justifyContent: "space-between", marginTop: 4, paddingTop: 4, borderTop: "1px solid #e0dbd4", fontFamily: "'Playfair Display', serif", fontSize: 13 }}>
                  <span>Saldo</span>
                  <strong style={{ color: monthSaldo.saldo >= 0 ? "#2d6a4f" : "#8a1a1a" }}>
                    {monthSaldo.saldo === 0 ? "0h" : `${monthSaldo.saldo > 0 ? "+" : ""}${monthSaldo.saldo}h`}
                  </strong>
                </div>
              )}
            </div>
            <div>
              <div className="section-label" style={{ marginBottom: 4 }}>Ferien {new Date().getFullYear()}</div>
              {ferienSaldo && [
                { label: "Anspruch", value: `${ferienSaldo.anspruch}h`, show: true },
                { label: "Übertrag", value: `${ferienSaldo.ubertrag}h`, show: ferienSaldo.ubertrag > 0 },
                { label: "Bezogen", value: `${ferienSaldo.bezogen}h`, show: ferienSaldo.bezogen > 0 },
                { label: "Geplant", value: `${ferienSaldo.geplant}h`, show: ferienSaldo.geplant > 0 },
              ].filter(r => r.show).map(r => (
                <div key={r.label} style={{ display: "flex", justifyContent: "space-between", padding: "1px 0", fontSize: 11, color: "#555" }}>
                  <span>{r.label}</span><span>{r.value}</span>
                </div>
              ))}
              {ferienSaldo && (
                <div style={{ display: "flex", justifyContent: "space-between", marginTop: 4, paddingTop: 4, borderTop: "1px solid #e0dbd4", fontFamily: "'Playfair Display', serif", fontSize: 13 }}>
                  <span>Rest</span>
                  <strong style={{ color: ferienSaldo.rest >= 0 ? "#2d6a4f" : "#8a1a1a" }}>
                    {ferienSaldo.rest === 0 ? "0h" : `${ferienSaldo.rest > 0 ? "+" : ""}${ferienSaldo.rest}h`}
                  </strong>
                </div>
              )}
            </div>
          </div>
        )}

        </div>
      </div>

      <ReceiptViewer expense={receiptView} onClose={() => setReceiptView(null)} />

      {contextMenu && (
        <>
          <div style={{ position: "fixed", inset: 0, zIndex: 999 }} onClick={() => setContextMenu(null)} onContextMenu={ev => { ev.preventDefault(); setContextMenu(null); }} />
          <div style={{ position: "fixed", top: contextMenu.y, left: contextMenu.x, zIndex: 1000, background: "#fff", border: "1px solid #e0dbd4", borderRadius: 6, boxShadow: "0 4px 16px rgba(0,0,0,0.12)", minWidth: 180, padding: "4px 0" }}>
            <button style={{ display: "block", width: "100%", textAlign: "left", padding: "8px 14px", fontSize: 12, background: "none", border: "none", cursor: "pointer", fontFamily: "inherit", color: "#1a1a18" }}
              onMouseEnter={ev => ev.currentTarget.style.background = "#faf8f5"}
              onMouseLeave={ev => ev.currentTarget.style.background = "none"}
              onClick={() => { duplicateToNextDay(contextMenu.entry); setContextMenu(null); }}>
              Auf morgen kopieren
            </button>
          </div>
        </>
      )}
    </div>
  );
}

