// Calendar — clean month view with range pills (inspired by user's screenshot)
// Plus add/edit/delete calendar events.
const { useState, useEffect } = React;

// ─────────────────────────────────────────────────────────────
// Date math helpers (Mon-first weeks)
// ─────────────────────────────────────────────────────────────
const firstDayCol = (y, m) => {
  // m: 0-11. Returns 0..6 with Mon=0
  const jsDay = new Date(y, m, 1).getDay(); // Sun=0..Sat=6
  return (jsDay + 6) % 7;
};
const daysInMonth = (y, m) => new Date(y, m + 1, 0).getDate();

// Inclusive day-difference using ISO strings
const dayDiff = (aISO, bISO) => Math.round((fromISO(bISO) - fromISO(aISO)) / 86400000);

// ─────────────────────────────────────────────────────────────
// Mini date-range picker — clean style from screenshot
// Single tap = single day. Second tap on later date = range end. Tapping on/before start resets.
// ─────────────────────────────────────────────────────────────
function MiniRangePicker({ start, end, onChange, anchorISO }) {
  const t = useTheme();
  const anchorDate = anchorISO ? fromISO(anchorISO) : fromISO(start || todayISO());
  const [view, setView] = useState({ y: anchorDate.getFullYear(), m: anchorDate.getMonth() });

  const startOffset = firstDayCol(view.y, view.m);
  const dim = daysInMonth(view.y, view.m);
  const totalCells = Math.ceil((startOffset + dim) / 7) * 7;
  const days = ['MO', 'DI', 'MI', 'DO', 'FR', 'SA', 'SO'];

  const cellISO = (d) => `${view.y}-${String(view.m + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`;

  const isInRange = (d) => start && end && cellISO(d) >= start && cellISO(d) <= end;
  const isStart = (d) => start && cellISO(d) === start;
  const isEnd = (d) => end && cellISO(d) === end;
  const isToday = (d) => cellISO(d) === todayISO();

  const onDayTap = (d) => {
    const iso = cellISO(d);
    if (!start || start && end && start !== end) {
      onChange(iso, iso);return;
    }
    if (iso < start) {onChange(iso, iso);return;}
    if (iso === start) {onChange(iso, iso);return;}
    onChange(start, iso);
  };

  const move = (delta) => {
    const d = new Date(view.y, view.m + delta, 1);
    setView({ y: d.getFullYear(), m: d.getMonth() });
  };

  return (
    <div>
      {/* Header: prev / month name / next */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16 }}>
        <Pressable onClick={() => move(-1)} scale={0.85}>
          <div style={{ width: 32, height: 32, borderRadius: 16, border: `1px solid ${t.sep}`, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <Icon d={icons.back} color={t.text} size={14} sw={2} />
          </div>
        </Pressable>
        <div style={{ fontSize: 17, fontWeight: 600, color: t.text, letterSpacing: -0.3 }}>{DE_MONTHS[view.m]} {view.y}</div>
        <Pressable onClick={() => move(1)} scale={0.85}>
          <div style={{ width: 32, height: 32, borderRadius: 16, border: `1px solid ${t.sep}`, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <Icon d={icons.chev} color={t.text} size={14} sw={2} />
          </div>
        </Pressable>
      </div>

      {/* Day labels */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', marginBottom: 8 }}>
        {days.map((d) =>
        <div key={d} style={{ textAlign: 'center', fontSize: 10, color: t.textSec, fontWeight: 600, letterSpacing: 0.8 }}>{d}</div>
        )}
      </div>

      {/* Grid with range overlay */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', position: 'relative', gap: 0 }}>
        {Array.from({ length: totalCells }, (_, i) => {
          const d = i - startOffset + 1;
          const valid = d >= 1 && d <= dim;
          if (!valid) return <div key={i} style={{ height: 40 }} />;

          const inR = isInRange(d),sS = isStart(d),sE = isEnd(d),today = isToday(d);
          const singleDay = start && end && start === end && sS;

          // Range pill background — connect cells horizontally
          const showLeftHalf = inR && !sS;
          const showRightHalf = inR && !sE;
          const isEdge = sS || sE;

          return (
            <Pressable key={i} onClick={() => onDayTap(d)} scale={0.92}>
              <div style={{
                height: 40, position: 'relative',
                display: 'flex', alignItems: 'center', justifyContent: 'center'
              }}>
                {/* Connection bar (light gray) */}
                {inR && !singleDay &&
                <div style={{
                  position: 'absolute', top: '50%', transform: 'translateY(-50%)',
                  left: showLeftHalf || isEdge ? sS ? '50%' : 0 : '50%',
                  right: showRightHalf || isEdge ? sE ? '50%' : 0 : '50%',
                  height: 30, background: t.chipStrong,
                  borderRadius: isEdge && !inR ? 15 : 0
                }} />
                }
                {/* Endpoint dark fill */}
                {isEdge && !singleDay &&
                <div style={{
                  position: 'absolute', top: '50%', left: '50%',
                  transform: 'translate(-50%, -50%)',
                  width: 30, height: 30, borderRadius: 15,
                  background: t.solidBg
                }} />
                }
                {/* Single-day filled */}
                {singleDay &&
                <div style={{
                  position: 'absolute', top: '50%', left: '50%',
                  transform: 'translate(-50%, -50%)',
                  width: 30, height: 30, borderRadius: 15,
                  background: t.solidBg
                }} />
                }
                {/* Today outline (only if not the start) */}
                {today && !singleDay && !isEdge &&
                <div style={{
                  position: 'absolute', top: '50%', left: '50%',
                  transform: 'translate(-50%, -50%)',
                  width: 30, height: 30, borderRadius: 15,
                  border: `1.5px solid ${t.text}`
                }} />
                }
                <span style={{
                  position: 'relative',
                  fontSize: 14, fontWeight: 500,
                  color: isEdge || singleDay ? '#fff' : today ? t.text : t.textSec
                }}>{d}</span>
              </div>
            </Pressable>);

        })}
      </div>

      {/* Legend */}
      <div style={{ marginTop: 14, display: 'flex', gap: 18, fontSize: 11, color: t.textSec }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <div style={{ width: 8, height: 8, borderRadius: 4, background: t.solidBg }} /> Ausgewählt
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <div style={{ width: 8, height: 8, borderRadius: 4, border: `1.5px solid ${t.text}` }} /> Heute
        </div>
      </div>
    </div>);

}

// ─────────────────────────────────────────────────────────────
// Month view — events shown as horizontal pills below day numbers.
// Pill WIDTH within a single-day event reflects the time portion of the day
// (e.g., 09:00–18:00 takes ~37% of the day cell width). All-day events get
// a dashed, lighter pill so they stand out from timed ones.
// Multi-day events span multiple day columns as before.
// Pills are draggable horizontally to shift days; edges to extend duration.
// ─────────────────────────────────────────────────────────────
function CalMonthView({ view, setView, events, onPickDay, onPickEvent, equipment, onChange, toast, labelMode = 'name', setLabelMode }) {
  const t = useTheme();
  // Long-press palette popover state
  const [palette, setPalette] = React.useState(null); // { ev, x, y } | null
  const longPressTimer = React.useRef(null);
  const longPressFired = React.useRef(false);
  const { y, m } = view;
  const startOffset = firstDayCol(y, m);
  const dim = daysInMonth(y, m);
  const totalCells = Math.ceil((startOffset + dim) / 7) * 7;
  const rows = totalCells / 7;
  const days = ['MO', 'DI', 'MI', 'DO', 'FR', 'SA', 'SO'];
  const MAX_LANES = 3;

  const cellISO = (d) => `${y}-${String(m + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
  const monthStart = cellISO(1);
  const monthEnd = cellISO(dim);
  const visibleEvents = events.filter((e) => !(e.end < monthStart || e.start > monthEnd));

  // helpers
  const parseTime = (s) => {
    if (!s || typeof s !== 'string' || !/^\d{1,2}:\d{2}$/.test(s)) return null;
    const [h, mi] = s.split(':').map(Number);
    return h + mi / 60;
  };

  // Lane-pack events into rows. Events beyond MAX_LANES are hidden and counted
  // as overflow per day-cell, then rendered as a single "+N" indicator.
  const segmentsByRow = Array.from({ length: rows }, () => []);
  const overflowByCell = Array.from({ length: rows }, () => Array.from({ length: 7 }, () => 0));
  const sorted = [...visibleEvents].sort((a, b) =>
    a.start.localeCompare(b.start) || daysBetween(b.start, b.end) - daysBetween(a.start, a.end));
  // Track occupancy per lane without a cap so overflow ordering stays stable.
  const occ = Array.from({ length: rows }, () => Array.from({ length: 7 }, () => []));

  for (const ev of sorted) {
    const evStart = ev.start < monthStart ? monthStart : ev.start;
    const evEnd = ev.end > monthEnd ? monthEnd : ev.end;
    const startIdx = startOffset + Number(evStart.slice(8)) - 1;
    const endIdx = startOffset + Number(evEnd.slice(8)) - 1;
    const startRow = Math.floor(startIdx / 7);
    const endRow = Math.floor(endIdx / 7);

    // Find the lowest lane (no cap) where the event fits across its full span.
    let lane = 0;
    while (true) {
      let free = true;
      for (let r = startRow; r <= endRow && free; r++) {
        const cs = r === startRow ? startIdx % 7 : 0;
        const ce = r === endRow ? endIdx % 7 : 6;
        for (let c = cs; c <= ce; c++) if (occ[r][c][lane]) { free = false; break; }
      }
      if (free) break;
      lane++;
      if (lane > 50) break; // safety guard
    }

    for (let r = startRow; r <= endRow; r++) {
      const colStart = r === startRow ? startIdx % 7 : 0;
      const colEnd = r === endRow ? endIdx % 7 : 6;
      for (let c = colStart; c <= colEnd; c++) occ[r][c][lane] = true;
      if (lane < MAX_LANES) {
        segmentsByRow[r].push({
          ev, lane, colStart, colEnd,
          roundLeft: r === startRow, roundRight: r === endRow,
          showLabel: r === startRow,
        });
      } else {
        for (let c = colStart; c <= colEnd; c++) overflowByCell[r][c]++;
      }
    }
  }

  const visibleLaneCount = segmentsByRow.map((segs) => Math.max(0, ...segs.map((s) => s.lane + 1)));
  const hasOverflow = overflowByCell.map((row) => row.some((n) => n > 0));
  const NUM_H = 26, LANE_H = 18, ROW_PAD = 6, OVERFLOW_H = 14;
  const rowHeights = visibleLaneCount.map((n, r) =>
    NUM_H + Math.max(1, n) * LANE_H + (hasOverflow[r] ? OVERFLOW_H + 2 : 0) + ROW_PAD);
  const today = todayISO();

  const tint = (hex) => t.dark ? hex + '40' : hex + '24';

  // Drag state — horizontal shift
  const [drag, setDrag] = React.useState(null);
  const containerRef = React.useRef(null);
  const shiftISO = (iso, days) => { const d = fromISO(iso); d.setDate(d.getDate() + days); return fmtDateISO(d); };

  const startDrag = (e, ev, mode) => {
    if (!onChange) return;
    e.preventDefault(); e.stopPropagation();
    try { e.currentTarget.setPointerCapture(e.pointerId); } catch {}
    longPressFired.current = false;
    if (longPressTimer.current) clearTimeout(longPressTimer.current);
    if (mode === 'move') {
      const rect = e.currentTarget.getBoundingClientRect();
      longPressTimer.current = setTimeout(() => {
        longPressFired.current = true;
        setPalette({ ev, x: rect.left + rect.width / 2, y: rect.top });
        setDrag(null);
      }, 550);
    }
    setDrag({ id: ev.id, mode, startX: e.clientX, dx: 0, origStart: ev.start, origEnd: ev.end, ev, moved: false });
  };
  const moveDrag = (e) => {
    if (!drag) return;
    const dx = e.clientX - drag.startX;
    if (Math.abs(dx) > 4 && longPressTimer.current) { clearTimeout(longPressTimer.current); longPressTimer.current = null; }
    setDrag(d => d ? { ...d, dx, moved: d.moved || Math.abs(dx) > 4 } : null);
  };
  const endDrag = (e, ev) => {
    if (longPressTimer.current) { clearTimeout(longPressTimer.current); longPressTimer.current = null; }
    if (longPressFired.current) { longPressFired.current = false; return; }
    if (!drag) return;
    const w = containerRef.current ? containerRef.current.getBoundingClientRect().width / 7 : 50;
    const shift = Math.round((drag.dx || 0) / w);
    if (drag.moved && shift !== 0) {
      let ns = drag.origStart, ne = drag.origEnd;
      if (drag.mode === 'move') { ns = shiftISO(drag.origStart, shift); ne = shiftISO(drag.origEnd, shift); }
      if (drag.mode === 'resize-left') { ns = shiftISO(drag.origStart, shift); if (ns > ne) ns = ne; }
      if (drag.mode === 'resize-right') { ne = shiftISO(drag.origEnd, shift); if (ne < ns) ne = ns; }
      onChange({ ...ev, start: ns, end: ne });
      if (toast) toast(`Verschoben auf ${fmtDateDE(ns)}${ns !== ne ? '–' + fmtDateDE(ne) : ''}`);
    } else if (!drag.moved) {
      onPickEvent(ev);
    }
    setDrag(null);
  };

  return (
    <div style={{ padding: '12px 12px 0' }}>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', marginBottom: t.studio ? 6 : 2, paddingBottom: t.studio ? 8 : 0, borderBottom: t.studio ? `1px solid ${t.sep}` : 'none' }}>
        {days.map((d, di) =>
          <div key={d} style={{ textAlign: 'center', fontSize: t.studio ? 10.5 : 10, color: t.studio && di >= 5 ? t.textTer : t.textSec, fontWeight: 700, letterSpacing: t.studio ? 1.2 : 0.6, paddingBottom: 4 }}>{d}</div>
        )}
      </div>

      <div ref={containerRef}>
        {Array.from({ length: rows }, (_, r) => {
          const rowH = rowHeights[r];
          return (
            <div key={r} style={{ position: 'relative', height: rowH, borderTop: t.studio ? (r === 0 ? 'none' : `0.5px solid ${t.sep}`) : `0.5px solid ${t.sep}` }}>
              {/* day-number cells */}
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', position: 'absolute', inset: 0 }}>
                {Array.from({ length: 7 }, (_, c) => {
                  const i = r * 7 + c;
                  const d = i - startOffset + 1;
                  const valid = d >= 1 && d <= dim;
                  const iso = valid ? cellISO(d) : null;
                  const isT = iso === today;
                  const weekend = c >= 5;
                  return (
                    <Pressable key={c} onClick={() => valid && onPickDay(iso)} scale={0.98} disabled={!valid} style={{ height: '100%' }}>
                      <div style={{ height: '100%', display: 'flex', justifyContent: 'center', paddingTop: 5, borderLeft: t.studio ? 'none' : (c === 0 ? 'none' : `0.5px solid ${t.dark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.03)'}`), background: (isT && !t.studio) ? (t.dark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.05)') : 'transparent' }}>
                        <div style={{
                          width: 22, height: 22, borderRadius: 11,
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                          fontSize: 13, fontWeight: isT ? 700 : 500,
                          background: (isT && t.studio) ? t.solidBg : 'transparent',
                          color: !valid ? 'transparent' : (isT && t.studio) ? t.solidFg : weekend ? t.textSec : t.text,
                        }}>{valid ? d : ''}</div>
                      </div>
                    </Pressable>);
                })}
              </div>

              {/* event pills */}
              <div style={{ position: 'absolute', top: NUM_H, left: 0, right: 0, bottom: 0 }}>
                {segmentsByRow[r].map((seg, j) => {
                  const ev = seg.ev;
                  const isDragging = drag && drag.id === ev.id;
                  const dx = isDragging ? drag.dx : 0;

                  // Time-aware pill geometry — same transparent tinted style for all bars.
                  // Single-day timed → narrower pill aligned to the time window.
                  // Multi-day / all-day → full-width tinted pill across columns.
                  const isMultiDay = ev.start !== ev.end;
                  const sHr = parseTime(ev.startTime);
                  const eHr = parseTime(ev.endTime);
                  const hasTime = sHr !== null && eHr !== null && eHr > sHr;
                  const singleDayTimed = !isMultiDay && hasTime;

                  let leftPct = seg.colStart / 7 * 100;
                  let widthPct = (seg.colEnd - seg.colStart + 1) / 7 * 100;

                  // Studio: always render single-day events as a full-day-width bar
                  // (narrow time-window pills are too small to read in the month grid).
                  if (singleDayTimed && !t.studio) {
                    const dayLeft = seg.colStart / 7 * 100;
                    const dayWidth = 1 / 7 * 100;
                    leftPct  = dayLeft + (sHr / 24) * dayWidth;
                    widthPct = ((eHr - sHr) / 24) * dayWidth;
                  }
                  const pillBg = tint(ev.color);
                  const pillTextColor = t.studio ? ev.color : (t.dark ? '#fff' : ev.color);

                  const canDrag = !!onChange;

                  return (
                    <div key={j}
                      onPointerDown={canDrag ? (e) => startDrag(e, ev, 'move') : undefined}
                      onPointerMove={canDrag ? moveDrag : undefined}
                      onPointerUp={canDrag ? (e) => endDrag(e, ev) : undefined}
                      onPointerCancel={canDrag ? () => setDrag(null) : undefined}
                      onClick={canDrag ? undefined : () => onPickEvent(ev)}
                      style={{
                        position: 'absolute',
                        top: seg.lane * LANE_H, height: LANE_H - 3,
                        left: `calc(${leftPct}% + 2px)`,
                        width: `calc(${widthPct}% - 4px)`,
                        transform: isDragging ? `translateX(${dx}px)` : 'none',
                        background: pillBg,
                        borderTopLeftRadius: seg.roundLeft ? 5 : 0, borderBottomLeftRadius: seg.roundLeft ? 5 : 0,
                        borderTopRightRadius: seg.roundRight ? 5 : 0, borderBottomRightRadius: seg.roundRight ? 5 : 0,
                        display: 'flex', alignItems: 'center',
                        padding: '0 4px', overflow: 'hidden',
                        cursor: canDrag ? (isDragging ? 'grabbing' : 'grab') : 'pointer',
                        touchAction: 'none', userSelect: 'none',
                        zIndex: isDragging ? 12 : 3,
                      }}
                      title={ev.title}>
                      {seg.showLabel && (() => {
                        const detail = labelMode === 'detail' && ev.fromRental;
                        if (!detail) {
                          return (
                            <span style={{
                              fontSize: 9.5, fontWeight: 700,
                              color: pillTextColor,
                              whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', lineHeight: 1,
                              position: 'relative', zIndex: 2,
                            }}>{ev.title}</span>
                          );
                        }
                        const eq = ev.equipmentLabel || ev.title;
                        const hasTimes = ev.pickupTime || ev.returnTime;
                        const times = hasTimes
                          ? `↑${ev.pickupTime || '—'} ↓${ev.returnTime || '—'}`
                          : '';
                        return (
                          <span style={{
                            fontSize: 8.5,
                            color: pillTextColor,
                            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', lineHeight: 1,
                            position: 'relative', zIndex: 2,
                            fontVariantNumeric: 'tabular-nums',
                          }}>
                            <span style={{ fontWeight: 800 }}>{eq}</span>
                            {times && <span style={{ fontWeight: 500, opacity: 0.85 }}>{' · ' + times}</span>}
                          </span>
                        );
                      })()}
                      {canDrag && seg.roundLeft && (
                        <div onPointerDown={(e) => startDrag(e, ev, 'resize-left')}
                          style={{ position: 'absolute', top: 0, bottom: 0, left: 0, width: 6, cursor: 'ew-resize', zIndex: 3 }}/>
                      )}
                      {canDrag && seg.roundRight && (
                        <div onPointerDown={(e) => startDrag(e, ev, 'resize-right')}
                          style={{ position: 'absolute', top: 0, bottom: 0, right: 0, width: 6, cursor: 'ew-resize', zIndex: 3 }}/>
                      )}
                    </div>);
                })}

                {/* "+N weitere" overflow indicators per day cell */}
                {hasOverflow[r] && (() => {
                  // Group consecutive cells with the same overflow count into one pill
                  // so multi-day clusters read as one chip rather than 5 tiny boxes.
                  const lane = visibleLaneCount[r];
                  const top = lane * LANE_H + 1;
                  const cells = overflowByCell[r];
                  const pills = [];
                  let c = 0;
                  while (c < 7) {
                    if (cells[c] > 0) {
                      let end = c;
                      while (end + 1 < 7 && cells[end + 1] === cells[c]) end++;
                      pills.push({ c, end, n: cells[c] });
                      c = end + 1;
                    } else c++;
                  }
                  return pills.map((p, k) => {
                    const left = p.c / 7 * 100;
                    const w = (p.end - p.c + 1) / 7 * 100;
                    const d = r * 7 + p.c - startOffset + 1;
                    const iso = d >= 1 && d <= dim ? cellISO(d) : null;
                    return (
                      <Pressable key={'ov-' + k} onClick={() => iso && onPickDay(iso)} scale={0.95}>
                        <div style={{
                          position: 'absolute', top, height: OVERFLOW_H,
                          left: `calc(${left}% + 2px)`, width: `calc(${w}% - 4px)`,
                          borderRadius: 4,
                          background: t.dark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.05)',
                          display: 'flex', alignItems: 'center', justifyContent: 'center',
                          fontSize: 9.5, fontWeight: 700, color: t.textSec,
                          letterSpacing: 0.2, cursor: 'pointer',
                        }}>
                          +{p.n} weitere
                        </div>
                      </Pressable>
                    );
                  });
                })()}
              </div>
            </div>);
        })}
        <div style={{ borderTop: `0.5px solid ${t.sep}` }}/>
      </div>
      {/* Label-Modus Umschalter */}
      {setLabelMode && (
        <div style={{ padding: '10px 4px 0', display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 8 }}>
          <div style={{ display: 'inline-flex', padding: 2, borderRadius: t.studio ? 999 : 8, background: t.studio ? 'transparent' : t.chipStrong, border: t.studio ? `0.5px solid ${t.sep}` : 'none', flexShrink: 0 }}>
            {[['name', 'Name'], ['detail', 'Gerät · Zeit']].map(([id, label]) => (
              <Pressable key={id} onClick={() => setLabelMode(id)} scale={0.95}>
                <div style={{
                  padding: '4px 9px', borderRadius: t.studio ? 999 : 6,
                  fontSize: 10.5, fontWeight: 600,
                  background: labelMode === id ? (t.studio ? t.solidBg : (t.dark ? '#636366' : '#fff')) : 'transparent',
                  color: labelMode === id ? (t.studio ? t.solidFg : t.text) : t.textSec,
                  boxShadow: labelMode === id && !t.studio ? '0 1px 2px rgba(0,0,0,0.1)' : 'none',
                }}>{label}</div>
              </Pressable>
            ))}
          </div>
        </div>
      )}

      {/* Long-press color palette */}
      {palette && (
        <div onClick={() => setPalette(null)} style={{
          position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.35)', zIndex: 999,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <div onClick={(e) => e.stopPropagation()} style={{
            background: t.card, borderRadius: 18, padding: '16px 18px', boxShadow: '0 16px 50px rgba(0,0,0,0.35)',
            maxWidth: 300, width: '88%',
          }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
              <div style={{ width: 14, height: 14, borderRadius: 7, background: palette.ev.color }}/>
              <div style={{ fontSize: 13, fontWeight: 700, color: t.text, flex: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{palette.ev.title}</div>
              <Pressable onClick={() => setPalette(null)} scale={0.85}>
                <div style={{ width: 22, height: 22, borderRadius: 11, background: t.chip, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <svg width="9" height="9" viewBox="0 0 12 12" fill="none" stroke={t.text} strokeWidth="2.2" strokeLinecap="round"><path d="M3 3l6 6M9 3l-6 6"/></svg>
                </div>
              </Pressable>
            </div>
            <div style={{ fontSize: 11, color: t.textSec, marginBottom: 10 }}>Kalenderfarbe ändern</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {EVENT_COLORS.map(c => (
                <Pressable key={c} onClick={() => { onChange({ ...palette.ev, color: c }); setPalette(null); toast && toast('Farbe geändert'); }} scale={0.88}>
                  <div style={{
                    width: 34, height: 34, borderRadius: 17, background: c,
                    border: palette.ev.color === c ? '3px solid #fff' : '0',
                    boxShadow: palette.ev.color === c ? `0 0 0 2px ${c}, 0 2px 6px rgba(0,0,0,0.15)` : '0 1px 2px rgba(0,0,0,0.1)',
                  }}/>
                </Pressable>
              ))}
              {/* Custom color via native picker */}
              <label style={{
                width: 34, height: 34, borderRadius: 17, cursor: 'pointer',
                background: `conic-gradient(from 0deg, #ff595e, #ffca3a, #8ac926, #1982c4, #6a4c93, #ff595e)`,
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                border: '2px solid ' + t.appBg, boxShadow: '0 1px 2px rgba(0,0,0,0.15)',
              }}>
                <input type="color" value={palette.ev.color || '#007AFF'}
                  onChange={(e) => { onChange({ ...palette.ev, color: e.target.value }); }}
                  onBlur={() => { setPalette(null); toast && toast('Farbe geändert'); }}
                  style={{ opacity: 0, width: 1, height: 1 }}/>
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3v3M12 18v3M5 5l2 2M17 17l2 2M3 12h3M18 12h3M5 19l2-2M17 7l2-2"/></svg>
              </label>
            </div>
          </div>
        </div>
      )}
    </div>);
}

// ─────────────────────────────────────────────────────────────
// Belegungsplan — equipment × time resource grid (timeline)
// ─────────────────────────────────────────────────────────────
function CalPlanView({ view, events, equipment, rentals = [], setRentals, onPickEvent, onPickDay, weekStartISO, onMoveWeek, toast }) {
  const t = useTheme();
  // 7-day window beginning weekStartISO (Monday)
  const start = fromISO(weekStartISO);
  const dayList = Array.from({ length: 7 }, (_, i) => {const d = new Date(start);d.setDate(d.getDate() + i);return fmtDateISO(d);});
  const winStart = dayList[0],winEnd = dayList[6];
  const today = todayISO();

  const LABEL_W = 116; // frozen left column
  const DAY_W = 96; // each day column
  const ROW_H = 56;
  const HEAD_H = 40;

  // Drag state: { id, kind, startX, dx, origStart, origEnd, didMove }
  const [drag, setDrag] = React.useState(null);
  const dragDayShift = drag ? Math.round(drag.dx / DAY_W) : 0;

  const shiftISO = (iso, days) => { const d = fromISO(iso); d.setDate(d.getDate() + days); return fmtDateISO(d); };

  const startDrag = (e, b) => {
    if (b.kind !== 'rental' || !setRentals) return;
    if (b.ref && b.ref.status === 'abgeschlossen') return;
    e.preventDefault();
    e.stopPropagation();
    try { e.currentTarget.setPointerCapture(e.pointerId); } catch {}
    setDrag({ id: b.id, kind: b.kind, startX: e.clientX, dx: 0, origStart: b.start, origEnd: b.end, didMove: false });
  };
  const moveDrag = (e) => {
    if (!drag) return;
    const dx = e.clientX - drag.startX;
    setDrag(d => d ? { ...d, dx, didMove: d.didMove || Math.abs(dx) > 4 } : null);
  };
  const endDrag = (e, b) => {
    if (!drag) return;
    const moved = drag.didMove;
    const shift = Math.round(drag.dx / DAY_W);
    if (moved && shift !== 0 && b.kind === 'rental') {
      const ns = shiftISO(drag.origStart, shift);
      const ne = shiftISO(drag.origEnd, shift);
      setRentals(prev => prev.map(r => r.id === drag.id ? { ...r, start: ns, end: ne } : r));
      if (toast) toast(`Verschoben auf ${fmtDateDE(ns)}`);
    } else if (!moved && b.kind === 'rental' && onPickDay) {
      onPickDay(b.start);
    }
    setDrag(null);
  };
  const cancelDrag = () => setDrag(null);

  // group equipment by category/kind
  const groups = {};
  equipment.forEach((e) => {const k = e.kind || 'Sonstige';(groups[k] = groups[k] || []).push(e);});
  const groupNames = Object.keys(groups);

  // Bookings = rentals (one per item) + events, normalized to { equipmentId, start, end, title, color }
  const bookings = [
  ...rentals.filter((r) => r.status !== 'abgeschlossen').flatMap((r) => {
    const items = getRentalItems(r);
    return items.map((it) => ({
      kind: 'rental', id: r.id, equipmentId: it.equipmentId,
      start: r.start, end: r.end,
      startTime: r.startTime, endTime: r.endTime,
      title: r.tenantName + (it.quantity > 1 ? ' ×' + it.quantity : ''),
      color: r.color || statusMeta(r.status).color, ref: r,
    }));
  }),
  ...events.map((e) => ({ kind: 'event', id: e.id, equipmentId: e.equipmentId, start: e.start, end: e.end, title: e.title, color: e.color, ref: e })),
  ].filter((b) => !(b.end < winStart || b.start > winEnd));

  const colIndex = (iso) => dayList.indexOf(iso);
  const todayCol = colIndex(today);

  const weekLabel = `${fromISO(winStart).getDate()}.–${fromISO(winEnd).getDate()}. ${DE_MONTHS_SHORT[fromISO(winEnd).getMonth()]}`;

  // Bar geometry + greedy lane assignment so overlapping bookings stack instead of covering each other
  const BAR_H = 30, BAR_GAP = 4, ROW_PAD = 8, BASE_ROW_H = 56;
  const assignLanes = (bks) => {
    const sorted = [...bks].sort((a, b) => a.start.localeCompare(b.start) || a.end.localeCompare(b.end));
    const laneEnds = []; // last end-ISO occupying each lane
    sorted.forEach((b) => {
      let lane = laneEnds.findIndex((end) => b.start > end);
      if (lane === -1) { lane = laneEnds.length; }
      laneEnds[lane] = b.end;
      b._lane = lane;
    });
    return { sorted, laneCount: Math.max(1, laneEnds.length) };
  };
  // Precompute per-equipment lanes + row heights
  const planByEq = {};
  equipment.forEach((eq) => {
    planByEq[eq.id] = assignLanes(bookings.filter((b) => b.equipmentId === eq.id));
  });
  const rowHeightFor = (eqId) => {
    const lc = (planByEq[eqId] || { laneCount: 1 }).laneCount;
    return Math.max(BASE_ROW_H, ROW_PAD * 2 + lc * BAR_H + (lc - 1) * BAR_GAP);
  };

  return (
    <div style={{ padding: '12px 0 0' }}>
      {/* week nav */}
      <div style={{ padding: '0 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: t.text }}>{weekLabel}</div>
        <div style={{ display: 'flex', gap: 6 }}>
          <Pressable onClick={() => onMoveWeek(-1)} scale={0.88}>
            <div style={{ width: 30, height: 30, borderRadius: 15, background: t.chip, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Icon d={icons.back} color={t.text} size={13} sw={2} />
            </div>
          </Pressable>
          <Pressable onClick={() => onMoveWeek(1)} scale={0.88}>
            <div style={{ width: 30, height: 30, borderRadius: 15, background: t.chip, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Icon d={icons.chev} color={t.text} size={13} sw={2} />
            </div>
          </Pressable>
        </div>
      </div>

      {/* Scroll container */}
      <div style={{ overflowX: 'auto', overflowY: 'hidden', WebkitOverflowScrolling: 'touch' }}>
        <div style={{ minWidth: LABEL_W + DAY_W * 7, position: 'relative' }}>
          {/* Header: day columns */}
          <div style={{ display: 'flex', position: 'sticky', top: 0, zIndex: 4 }}>
            <div style={{ width: LABEL_W, flexShrink: 0, height: HEAD_H, background: t.bg, position: 'sticky', left: 0, zIndex: 5, borderRight: `0.5px solid ${t.sep}` }} />
            {dayList.map((iso, i) => {
              const dd = fromISO(iso);
              const isT = iso === today;
              return (
                <Pressable key={iso} onClick={() => onPickDay(iso)} scale={0.97} style={{ width: DAY_W, flexShrink: 0 }}>
                  <div style={{ height: HEAD_H, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', borderRight: `0.5px solid ${t.sep}`, background: isT ? t.accentSoft : t.bg }}>
                    <div style={{ fontSize: 9, fontWeight: 700, color: isT ? t.accent : t.textSec, letterSpacing: 0.5 }}>{['MO', 'DI', 'MI', 'DO', 'FR', 'SA', 'SO'][i].toUpperCase()}</div>
                    <div style={{ fontSize: 14, fontWeight: 700, color: isT ? t.accent : t.text, marginTop: 1 }}>{dd.getDate()}</div>
                  </div>
                </Pressable>);

            })}
          </div>

          {/* Today vertical line */}
          {todayCol >= 0 &&
          <div style={{ position: 'absolute', top: HEAD_H, bottom: 0, left: LABEL_W + todayCol * DAY_W + DAY_W / 2, width: 2, background: t.red, opacity: 0.55, zIndex: 1, pointerEvents: 'none' }} />
          }

          {/* Rows grouped by category */}
          {groupNames.map((gn) =>
          <div key={gn}>
              {/* group header */}
              <div style={{ display: 'flex', position: 'sticky', left: 0, zIndex: 2 }}>
                <div style={{ width: LABEL_W + DAY_W * 7, background: t.dark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.035)', padding: '5px 12px', position: 'sticky', left: 0 }}>
                  <span style={{ fontSize: 10, fontWeight: 800, color: t.textSec, letterSpacing: 0.6, textTransform: 'uppercase' }}>{gn}</span>
                </div>
              </div>
              {groups[gn].map((eq) => {
              const { sorted: eqBookings } = planByEq[eq.id] || { sorted: [] };
              const rowH = rowHeightFor(eq.id);
              return (
                <div key={eq.id} style={{ position: 'relative', height: rowH, borderTop: `0.5px solid ${t.sep}` }}>
                    {/* day cells background grid (full row height) */}
                    <div style={{ position: 'absolute', top: 0, bottom: 0, left: LABEL_W, display: 'flex' }}>
                      {dayList.map((iso, i) =>
                    <div key={iso} style={{ width: DAY_W, flexShrink: 0, height: '100%', borderRight: `0.5px solid ${t.sep}`, background: iso === today ? t.accentSoft : t.dark ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.015)' }} />
                    )}
                    </div>

                    {/* booking bars (absolute, lane-stacked) */}
                    {eqBookings.map((b) => {
                    const bs = b.start < winStart ? winStart : b.start;
                    const be = b.end > winEnd ? winEnd : b.end;
                    const cStart = colIndex(bs),cEnd = colIndex(be);
                    if (cStart < 0 || cEnd < 0) return null;
                    const clipLeft = b.start < winStart, clipRight = b.end > winEnd;
                    const left = LABEL_W + cStart * DAY_W + 3;
                    const width = (cEnd - cStart + 1) * DAY_W - 6;
                    const top = ROW_PAD + b._lane * (BAR_H + BAR_GAP);
                    const isDragging = drag && drag.id === b.id;
                    const dragShift = isDragging ? drag.dx : 0;
                    const isDraggable = b.kind === 'rental' && b.ref && b.ref.status !== 'abgeschlossen' && !!setRentals;
                    return (
                      <div key={b.kind + b.id}
                        onPointerDown={isDraggable ? (e) => startDrag(e, b) : undefined}
                        onPointerMove={isDraggable ? moveDrag : undefined}
                        onPointerUp={isDraggable ? (e) => endDrag(e, b) : undefined}
                        onPointerCancel={isDraggable ? cancelDrag : undefined}
                        onClick={isDraggable ? undefined : () => (b.kind === 'event' ? onPickEvent(b.ref) : onPickDay(bs))}
                        style={{
                          position: 'absolute', top, height: BAR_H,
                          left, width, zIndex: isDragging ? 5 : 2,
                          cursor: isDraggable ? (isDragging ? 'grabbing' : 'grab') : 'pointer',
                          touchAction: 'none', userSelect: 'none',
                          background: t.dark ? b.color + '40' : b.color + '24',
                          borderTopLeftRadius: clipLeft ? 0 : 7, borderBottomLeftRadius: clipLeft ? 0 : 7,
                          borderTopRightRadius: clipRight ? 0 : 7, borderBottomRightRadius: clipRight ? 0 : 7,
                          padding: '0 8px',
                          display: 'flex', alignItems: 'center', gap: 6, overflow: 'hidden',
                          transform: isDragging ? `translateX(${dragShift}px)` : 'none',
                          boxShadow: isDragging ? '0 8px 22px rgba(0,0,0,0.25)' : 'none',
                          outline: isDragging ? `2px solid ${b.color}` : 'none',
                          transition: isDragging ? 'none' : 'transform 0.15s, box-shadow 0.15s',
                          opacity: isDragging ? 0.92 : 1,
                        }}>
                        <span style={{ fontSize: 11, fontWeight: 700, color: t.dark ? '#fff' : b.color, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', flexShrink: 1, minWidth: 0 }}>{b.title}</span>
                        {b.kind === 'rental' && (b.startTime || b.endTime) && (
                          <span style={{ fontSize: 9.5, fontWeight: 600, color: t.textSec, whiteSpace: 'nowrap', flexShrink: 0, fontFeatureSettings: '"tnum"' }}>
                            {b.startTime || '–'}–{b.endTime || '–'}
                          </span>
                        )}
                        <span style={{ fontSize: 9.5, color: t.textSec, whiteSpace: 'nowrap', flexShrink: 0 }}>
                          {isDragging && dragDayShift !== 0
                            ? `${dragDayShift > 0 ? '+' : ''}${dragDayShift} Tag${Math.abs(dragDayShift) === 1 ? '' : 'e'}`
                            : `${fromISO(b.start).getDate()}.–${fromISO(b.end).getDate()}.`}
                        </span>
                      </div>);
                  })}

                    {/* frozen equipment label */}
                    <div style={{ width: LABEL_W, flexShrink: 0, position: 'sticky', left: 0, zIndex: 3, background: t.bg, borderRight: `0.5px solid ${t.sep}`, height: '100%', display: 'flex', alignItems: 'center', gap: 8, padding: '0 8px' }}>
                      <EqAvatar item={eq} size={34} radius={8} />
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontSize: 12, fontWeight: 700, color: t.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{eq.name}</div>
                        <div style={{ fontSize: 9.5, color: t.textSec, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{eq.price} €/Tag</div>
                      </div>
                    </div>
                  </div>);

            })}
            </div>
          )}
          <div style={{ borderTop: `0.5px solid ${t.sep}` }} />
        </div>
      </div>

      {/* (legend removed — bar colors now follow the event/rental color directly) */}
      <div style={{ padding: '14px 16px 0', display: 'flex', justifyContent: 'flex-end' }}>
        <div style={{ fontSize: 10, color: t.textTer, fontStyle: 'italic' }}>↔ Pill ziehen zum Verschieben</div>
      </div>
    </div>);

}

// ─────────────────────────────────────────────────────────────
// Day-detail bottom sheet
// ─────────────────────────────────────────────────────────────
function DaySheet({ open, dayISO, onClose, events, equipment, onAdd, onEdit, onDelete }) {
  const t = useTheme();
  const dayEvents = events.filter((e) => dayISO && e.start <= dayISO && e.end >= dayISO && !e.synthetic);

  return (
    <Sheet open={open} onClose={onClose}>
      <div style={{ padding: '4px 24px 16px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
          <div>
            <div style={{ fontSize: 13, color: t.textSec, fontWeight: 500 }}>{dayISO === todayISO() ? 'Heute · ' : ''}{dayISO && DE_DAYS[fromISO(dayISO).getDay()]}</div>
            <div style={{ fontSize: 22, fontWeight: 700, color: t.text, letterSpacing: -0.5, marginTop: 2 }}>
              {dayISO && `${fromISO(dayISO).getDate()}. ${DE_MONTHS[fromISO(dayISO).getMonth()]}`}
            </div>
          </div>
          <Pressable onClick={onClose}>
            <div style={{ fontSize: 14, color: t.textSec, fontWeight: 500 }}>Schließen</div>
          </Pressable>
        </div>

        <div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 8 }}>
          {dayEvents.length === 0 &&
          <div style={{ padding: '24px 0', textAlign: 'center', color: t.textSec, fontSize: 13 }}>Keine Einträge an diesem Tag.</div>
          }
          {dayEvents.map((ev) => {
            const eq = equipment.find((e) => e.id === ev.equipmentId);
            return (
              <LongPressRow key={ev.id}
                onTap={() => onEdit(ev)}
                onEdit={() => onEdit(ev)}
                onDelete={ev.fromRental ? null : () => onDelete(ev)}>
              <div style={{ background: t.card, border: '0.5px solid ' + t.sep, borderRadius: 14, padding: 12, display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ width: 6, alignSelf: 'stretch', minHeight: 38, background: ev.color, borderRadius: 3 }} />
                <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 14, fontWeight: 600, color: t.text, display: 'flex', alignItems: 'center', gap: 6 }}>
                      {ev.title}
                      {ev.fromRental && <span style={{ fontSize: 9, fontWeight: 700, color: ev.color, background: ev.color + '22', padding: '1px 6px', borderRadius: 100, letterSpacing: 0.3 }}>MIETE</span>}
                    </div>
                    <div style={{ fontSize: 11, color: t.textSec, marginTop: 2 }}>
                      {eq ? eq.name : 'Allgemein'} · {fmtRange(ev.start, ev.end)}
                    </div>
                    {ev.fromRental && (ev.startTime || ev.endTime || ev.pickupTime || ev.returnTime) && (() => {
                      const pu = ev.pickupTime || ev.startTime;
                      const rt = ev.returnTime || ev.endTime;
                      const isStartDay = ev.start === dayISO;
                      const isEndDay   = ev.end === dayISO;
                      const sd = fromISO(ev.start), ed = fromISO(ev.end);
                      const sLbl = `${String(sd.getDate()).padStart(2,'0')}.${String(sd.getMonth()+1).padStart(2,'0')}.`;
                      const eLbl = `${String(ed.getDate()).padStart(2,'0')}.${String(ed.getMonth()+1).padStart(2,'0')}.`;
                      return (
                        <div style={{ fontSize: 11, color: t.text, fontWeight: 600, marginTop: 3, display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', fontFeatureSettings: '"tnum"' }}>
                          <span style={{
                            display: 'inline-flex', alignItems: 'center', gap: 4,
                            opacity: isStartDay || !isEndDay ? 1 : 0.45,
                          }}>↑ {outLabel(ev)} <span style={{ color: t.textSec, fontWeight: 500 }}>{sLbl}</span> <b>{pu ? pu + ' Uhr' : '–'}</b></span>
                          <span style={{ color: t.textTer }}>·</span>
                          <span style={{
                            display: 'inline-flex', alignItems: 'center', gap: 4,
                            opacity: isEndDay || !isStartDay ? 1 : 0.45,
                          }}>↓ Rückgabe <span style={{ color: t.textSec, fontWeight: 500 }}>{eLbl}</span> <b>{rt ? rt + ' Uhr' : '–'}</b></span>
                        </div>
                      );
                    })()}
                    {ev.note && <div style={{ fontSize: 11, color: t.textSec, marginTop: 2, fontStyle: 'italic' }}>{ev.note}</div>}
                  </div>
                <Icon d={icons.chev} color={t.textTer} size={14} sw={2}/>
              </div>
              </LongPressRow>);

          })}
        </div>

        <Pressable onClick={() => onAdd(dayISO)} scale={0.97} style={{ marginTop: 16 }}>
          <div style={{
            padding: '14px 0', textAlign: 'center', borderRadius: 14,
            background: t.solidBg, color: t.solidFg, fontSize: 15, fontWeight: 600,
            boxShadow: '0 6px 16px rgba(0,0,0,0.28)'
          }}>+ Eintrag erstellen</div>
        </Pressable>
      </div>
    </Sheet>);

}

// ─────────────────────────────────────────────────────────────
// Add / edit event sheet
// ─────────────────────────────────────────────────────────────
function EventSheet({ open, onClose, initial, defaultDate, equipment, onSave, onDelete }) {
  const t = useTheme();
  const isEdit = !!(initial && initial.id);
  const blank = {
    id: '', title: '', equipmentId: '',
    start: defaultDate || todayISO(), end: defaultDate || todayISO(),
    color: EVENT_COLORS[0], note: ''
  };
  const [form, setForm] = useState(blank);
  const [showPicker, setShowPicker] = useState(false);

  useEffect(() => {
    if (open) {
      setForm(initial ? { ...blank, ...initial } : { ...blank, start: defaultDate || todayISO(), end: defaultDate || todayISO() });
      setShowPicker(false);
    }
  }, [open, initial, defaultDate]);

  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
  const valid = form.title.trim() && form.start && form.end;

  const save = () => {
    if (!valid) return;
    const out = { ...form, id: form.id || 'ev-' + Date.now() };
    onSave(out);
    onClose();
  };

  const eq = equipment.find((e) => e.id === form.equipmentId);

  return (
    <Sheet open={open} onClose={onClose}>
      <div style={{ padding: '4px 24px 24px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
          <div>
            <div style={{ fontSize: 22, fontWeight: 700, color: t.text, letterSpacing: -0.5 }}>{isEdit ? 'Eintrag bearbeiten' : 'Neuer Eintrag'}</div>
            <div style={{ fontSize: 13, color: t.textSec, marginTop: 4 }}>Termin / Buchung im Kalender</div>
          </div>
          <Pressable onClick={onClose}><div style={{ fontSize: 14, color: t.textSec, fontWeight: 500 }}>Abbrechen</div></Pressable>
        </div>

        <div style={{ marginTop: 18 }}>
          <div style={{ fontSize: 11, color: t.textSec, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6 }}>Titel</div>
          <input value={form.title} onChange={(e) => set('title', e.target.value)} placeholder="z.B. Hochzeit Müller"
          style={{
            width: '100%', padding: '12px 14px', borderRadius: 12,
            border: `0.5px solid ${t.inputBorder}`, background: t.card,
            fontSize: 15, color: t.text, outline: 'none', fontFamily: 'inherit',
            boxSizing: 'border-box'
          }} />
        </div>

        <div style={{ marginTop: 14 }}>
          <div style={{ fontSize: 11, color: t.textSec, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6 }}>Equipment · optional</div>
          <div style={{ display: 'flex', gap: 6, overflowX: 'auto', paddingBottom: 4 }}>
            <Pressable onClick={() => set('equipmentId', '')} scale={0.95}>
              <div style={{
                padding: '8px 12px', borderRadius: 10, whiteSpace: 'nowrap',
                background: !form.equipmentId ? t.solidBg : t.chip,
                color: !form.equipmentId ? t.solidFg : t.text,
                fontSize: 12, fontWeight: 600
              }}>Allgemein</div>
            </Pressable>
            {equipment.map((e) =>
            <Pressable key={e.id} onClick={() => set('equipmentId', e.id)} scale={0.95}>
                <div style={{
                padding: '8px 12px', borderRadius: 10, whiteSpace: 'nowrap',
                background: form.equipmentId === e.id ? t.solidBg : t.chip,
                color: form.equipmentId === e.id ? t.solidFg : t.text,
                fontSize: 12, fontWeight: 600
              }}>{e.name}</div>
              </Pressable>
            )}
          </div>
        </div>

        <div style={{ marginTop: 14 }}>
          <div style={{ fontSize: 11, color: t.textSec, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6 }}>Zeitraum</div>
          <Pressable onClick={() => setShowPicker((s) => !s)} scale={0.99}>
            <div style={{
              padding: '12px 14px', borderRadius: 12,
              border: `0.5px solid ${t.inputBorder}`, background: t.card,
              display: 'flex', justifyContent: 'space-between', alignItems: 'center'
            }}>
              <div>
                <div style={{ fontSize: 14, color: t.text, fontWeight: 500 }}>{fmtRange(form.start, form.end)}</div>
                <div style={{ fontSize: 11, color: t.textSec, marginTop: 2 }}>{daysBetween(form.start, form.end)} Tag{daysBetween(form.start, form.end) > 1 ? 'e' : ''}</div>
              </div>
              <Icon d={showPicker ? 'M5 15l7-7 7 7' : 'M5 9l7 7 7-7'} color={t.textSec} size={16} sw={2} />
            </div>
          </Pressable>
          {showPicker &&
          <div style={{ marginTop: 10, padding: 16, background: t.card, borderRadius: 14, border: '0.5px solid ' + t.sep }}>
              <MiniRangePicker
              start={form.start} end={form.end}
              onChange={(s, e) => {set('start', s);set('end', e);}}
              anchorISO={form.start} />
            
            </div>
          }
        </div>

        {/* Uhrzeit von / bis */}
        <div style={{ marginTop: 14 }}>
          <div style={{ fontSize: 11, color: t.textSec, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6 }}>Uhrzeit</div>
          <div style={{ background: t.card, borderRadius: 14, overflow: 'hidden' }}>
            <div style={{ display: 'flex', padding: '11px 14px', alignItems: 'center', justifyContent: 'space-between' }}>
              <div>
                <div style={{ fontSize: 13, color: t.text, fontWeight: 600 }}>Ganzer Tag</div>
                <div style={{ fontSize: 10.5, color: t.textTer, marginTop: 1 }}>Ohne feste Uhrzeit</div>
              </div>
              {(() => {
                const allDay = !form.startTime && !form.endTime;
                const toggle = () => {
                  if (allDay) setForm(f => ({ ...f, startTime: '09:00', endTime: '11:00' }));
                  else setForm(f => ({ ...f, startTime: '', endTime: '' }));
                };
                return (
                  <Pressable onClick={toggle} scale={0.9}>
                    <div style={{
                      width: 42, height: 26, borderRadius: 13, padding: 2,
                      background: allDay ? t.solidBg : t.chip, display: 'flex',
                      justifyContent: allDay ? 'flex-end' : 'flex-start', alignItems: 'center',
                    }}>
                      <div style={{ width: 22, height: 22, borderRadius: 11, background: '#fff', boxShadow: '0 1px 3px rgba(0,0,0,0.2)' }}/>
                    </div>
                  </Pressable>
                );
              })()}
            </div>
            {(form.startTime || form.endTime) && <>
              <div style={{ height: 0.5, background: t.sep, marginLeft: 14 }}/>
              <div style={{ display: 'flex', padding: '11px 14px', alignItems: 'center', justifyContent: 'space-between' }}>
                <div style={{ fontSize: 12, color: t.textSec, fontWeight: 500 }}>Von</div>
                <input type="time" value={form.startTime || '09:00'} onChange={(e) => set('startTime', e.target.value)}
                  style={{ padding: '7px 10px', borderRadius: 9, border: `0.5px solid ${t.inputBorder}`, background: t.inputBg, fontSize: 15, color: t.text, fontFamily: 'inherit', fontWeight: 600, fontFeatureSettings: '"tnum"' }}/>
              </div>
              <div style={{ height: 0.5, background: t.sep, marginLeft: 14 }}/>
              <div style={{ display: 'flex', padding: '11px 14px', alignItems: 'center', justifyContent: 'space-between' }}>
                <div>
                  <div style={{ fontSize: 12, color: t.textSec, fontWeight: 500 }}>Bis</div>
                  {(() => {
                    const parseT = (s) => { if (!s) return null; const [h, m] = s.split(':').map(Number); return h + m / 60; };
                    const a = parseT(form.startTime), b = parseT(form.endTime);
                    if (a == null || b == null || b <= a) return null;
                    const dur = b - a;
                    const mins = Math.round(dur * 60);
                    const label = mins < 60 ? mins + ' Min' : (dur % 1 === 0 ? dur + ' h' : dur.toFixed(1).replace('.', ',') + ' h');
                    return <div style={{ fontSize: 10.5, color: t.textTer, marginTop: 1 }}>Dauer: {label}</div>;
                  })()}
                </div>
                <input type="time" value={form.endTime || '11:00'} onChange={(e) => set('endTime', e.target.value)}
                  style={{ padding: '7px 10px', borderRadius: 9, border: `0.5px solid ${t.inputBorder}`, background: t.inputBg, fontSize: 15, color: t.text, fontFamily: 'inherit', fontWeight: 600, fontFeatureSettings: '"tnum"' }}/>
              </div>
            </>}
          </div>
        </div>

        <div style={{ marginTop: 14 }}>
          <div style={{ fontSize: 11, color: t.textSec, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6 }}>Farbe</div>
          <div style={{ display: 'flex', gap: 8 }}>
            {EVENT_COLORS.map((c) =>
            <Pressable key={c} onClick={() => set('color', c)} scale={0.9}>
                <div style={{
                width: 30, height: 30, borderRadius: 15, background: c,
                border: form.color === c ? '2.5px solid #fff' : '0',
                boxShadow: form.color === c ? `0 0 0 2px ${c}` : '0 1px 2px rgba(0,0,0,0.1)'
              }} />
              </Pressable>
            )}
          </div>
        </div>

        <div style={{ marginTop: 14 }}>
          <div style={{ fontSize: 11, color: t.textSec, fontWeight: 600, textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 6 }}>Notiz · optional</div>
          <input value={form.note} onChange={(e) => set('note', e.target.value)} placeholder="Abholzeit, Ort, …"
          style={{
            width: '100%', padding: '12px 14px', borderRadius: 12,
            border: `0.5px solid ${t.inputBorder}`, background: t.card,
            fontSize: 15, color: t.text, outline: 'none', fontFamily: 'inherit',
            boxSizing: 'border-box'
          }} />
        </div>

        <div style={{ display: 'flex', gap: 8, marginTop: 22 }}>
          {isEdit &&
          <Pressable onClick={() => {onDelete(form);onClose();}} scale={0.96}>
              <div style={{ padding: '14px 18px', textAlign: 'center', borderRadius: 14, background: t.redSoft, color: t.red, fontSize: 14, fontWeight: 600 }}>Löschen</div>
            </Pressable>
          }
          <Pressable onClick={save} scale={0.97} disabled={!valid} style={{ flex: 1 }}>
            <div style={{
              padding: '15px 0', textAlign: 'center', borderRadius: 14,
              background: valid ? t.solidBg : t.chip,
              color: t.solidFg, fontSize: 15, fontWeight: 600
            }}>{isEdit ? 'Speichern' : 'Eintrag anlegen'}</div>
          </Pressable>
        </div>
      </div>
    </Sheet>);

}

// ─────────────────────────────────────────────────────────────
// Year view (kept tiny)
// ─────────────────────────────────────────────────────────────
function CalYear({ year, onSelect, events }) {
  const t = useTheme();
  return (
    <div style={{ padding: '14px 16px 0', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>
      {DE_MONTHS_SHORT.map((m, mi) => {
        const isCurrent = mi === 4 && year === 2026;
        const startOff = firstDayCol(year, mi);
        const dim = daysInMonth(year, mi);
        const cells = Math.ceil((startOff + dim) / 7) * 7;
        const monthEvents = events.filter((e) => {
          const es = fromISO(e.start),ee = fromISO(e.end);
          return !(ee.getFullYear() < year || es.getFullYear() > year) &&
          !(ee < new Date(year, mi, 1) || es > new Date(year, mi + 1, 0));
        });
        return (
          <Pressable key={m} onClick={() => onSelect(mi)} scale={0.96}>
            <div>
              <div style={{ fontSize: 13, fontWeight: 700, color: isCurrent ? t.accent : t.text, marginBottom: 4, letterSpacing: -0.2 }}>
                {m}{isCurrent && <span style={{ marginLeft: 4, fontSize: 9, color: t.accent }}>●</span>}
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 1 }}>
                {Array.from({ length: cells }, (_, i) => {
                  const d = i - startOff + 1;
                  const valid = d >= 1 && d <= dim;
                  const iso = valid ? `${year}-${String(mi + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}` : null;
                  const isToday = iso === todayISO();
                  const hasBooking = iso && monthEvents.some((e) => iso >= e.start && iso <= e.end);
                  return (
                    <div key={i} style={{
                      aspectRatio: '1',
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontSize: 7, fontWeight: 500,
                      color: isToday ? '#fff' : hasBooking ? t.accent : valid ? t.textSec : 'transparent',
                      background: isToday ? t.accent : 'transparent',
                      borderRadius: 4
                    }}>{valid ? d : ''}</div>);

                })}
              </div>
            </div>
          </Pressable>);

      })}
    </div>);

}

// ─── Day view — vertical timeline with pill cards (like the inspo).
// Two strands stacked:
//   1) HEUTE   — timed events for the current day, anchored to a
//                hour line on the left; pill cards float to the right.
//   2) MEHRTÄGIG — multi-day spans that cross the current day, as a
//                  separate strand below so they never block the day.
// Each event = a coloured icon-circle on the line + a rounded card
// to the right with title/time/equipment.
// ─────────────────────────────────────────────────────────────
function CalDay({ dayISO, events, equipment, onAdd, onEdit, onChange, toast }) {
  const t = useTheme();
  const day = dayISO;

  const parseTime = (s) => {
    if (!s || typeof s !== 'string' || !/^\d{1,2}:\d{2}$/.test(s)) return null;
    const [h, m] = s.split(':').map(Number);
    return h + m / 60;
  };
  const fmtTime = (hr) => {
    const h = Math.max(0, Math.min(23, Math.floor(hr)));
    const m = Math.round((hr - h) * 60);
    return String(h).padStart(2, '0') + ':' + (m === 60 ? '00' : String(m).padStart(2, '0'));
  };

  const dayEvents = events.filter((e) => e.start <= day && e.end >= day);
  const timed = [];
  const multi = [];
  dayEvents.forEach(ev => {
    const isFirst = ev.start === day;
    const isLast = ev.end === day;
    const sHr = isFirst ? parseTime(ev.startTime) : null;
    const eHr = isLast ? parseTime(ev.endTime) : null;
    const hasTime = sHr != null && eHr != null && eHr > sHr;
    const isSpan = ev.start !== ev.end;
    if (!isSpan && hasTime) timed.push({ ev, sHr, eHr });
    // Untimed single-day → treat as a banner (won't blanket the timed strand).
    else if (!isSpan && !hasTime) multi.push({ ev, isFirst: true, isLast: true, untimed: true });
    else multi.push({ ev, isFirst, isLast });
  });
  timed.sort((a, b) => a.sHr - b.sHr || b.eHr - a.eHr);

  // Lane-pack overlapping timed events so pills don't sit on top of each other.
  const laneEnds = []; // each lane stores the last assigned end-hour
  timed.forEach(s => {
    let lane = laneEnds.findIndex(end => s.sHr >= end - 0.001);
    if (lane === -1) { lane = laneEnds.length; laneEnds.push(s.eHr); }
    else laneEnds[lane] = s.eHr;
    s.lane = lane;
  });
  const LANE_COUNT = Math.max(1, laneEnds.length);

  // Geometry — pixels per hour, only render rows for hours that have content
  // (plus an "all-day" pseudo row for untimed). Actually we draw a continuous
  // strand spanning min..max hour for compactness.
  const minHr = timed.length ? Math.max(0, Math.floor(Math.min(...timed.map(x => x.sHr))) - 1) : 8;
  const maxHr = timed.length ? Math.min(24, Math.ceil(Math.max(...timed.map(x => x.eHr))) + 1) : 20;
  const HOUR_H = 56;
  const STRAND_H = (maxHr - minHr) * HOUR_H + 24;
  const LINE_X = 70; // x-position of the vertical strand
  const NODE_R = 26; // icon-circle radius

  const iconFor = (ev) => ev.fromRental ? icons.doc : icons.cal;

  const now = new Date();
  const isToday = day === todayISO();
  const nowFrac = isToday ? (now.getHours() + now.getMinutes() / 60) : -1;

  const TimedStrand = () => (
    <div style={{ position: 'relative', height: STRAND_H, marginTop: 8 }}>
      {/* Hour rail */}
      {Array.from({ length: maxHr - minHr + 1 }, (_, i) => {
        const h = minHr + i;
        const top = i * HOUR_H + 12;
        return (
          <div key={h} style={{ position: 'absolute', top, left: 0, right: 0, height: 0 }}>
            <div style={{ position: 'absolute', left: 6, top: -8, fontSize: 11, color: t.textSec, fontWeight: 600, fontVariantNumeric: 'tabular-nums' }}>
              {String(h).padStart(2, '0')}:00
            </div>
            <div style={{ position: 'absolute', left: LINE_X - 1, right: 16, top: 0, height: 0, borderTop: `0.5px dashed ${t.dark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)'}` }}/>
          </div>
        );
      })}

      {/* Central vertical strand */}
      <div style={{ position: 'absolute', left: LINE_X, top: 0, bottom: 0, width: 2, background: t.dark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)', borderRadius: 2 }}/>

      {/* Now-marker */}
      {nowFrac >= 0 && nowFrac >= minHr && nowFrac <= maxHr && (
        <div style={{ position: 'absolute', top: (nowFrac - minHr) * HOUR_H + 12, left: LINE_X - 14, height: 0, display: 'flex', alignItems: 'center', gap: 4 }}>
          <div style={{ width: 8, height: 8, borderRadius: 4, background: t.red }}/>
          <div style={{ width: 200, height: 2, background: t.red, opacity: 0.6 }}/>
        </div>
      )}

      {/* Event pills */}
      {timed.map((s, idx) => {
        const ev = s.ev;
        const top = (s.sHr - minHr) * HOUR_H + 12;
        const dur = s.eHr - s.sHr;
        const minutes = Math.round(dur * 60);
        const durLabel = minutes < 60 ? minutes + ' min' : (dur % 1 === 0 ? dur + ' h' : dur.toFixed(1).replace('.', ',') + ' h');
        const eq = equipment.find(e => e.id === ev.equipmentId);
        const eqLabel = ev.equipmentLabel || (eq && eq.name) || '';
        // Lane shrink — width split between overlapping events
        const lanePct = 100 / LANE_COUNT;
        const cardLeftBase = LINE_X + NODE_R + 6;
        // Pill grows with duration but never shorter than the content needs.
        const PILL_MIN_H = 66;
        const pillH = Math.max(dur * HOUR_H, PILL_MIN_H);
        return (
          <div key={ev.id} style={{ position: 'absolute', top: top - NODE_R, left: 0, right: 0 }}>
            {/* node — always on the strand */}
            <div style={{ position: 'absolute', left: LINE_X - NODE_R + 1, top: 0, width: NODE_R * 2 - 2, height: NODE_R * 2, borderRadius: NODE_R, background: ev.color, border: `3px solid ${t.appBg}`, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 2px 6px rgba(0,0,0,0.15)', zIndex: 5 }}>
              <Icon d={iconFor(ev)} color="#fff" size={20} sw={2}/>
            </div>
            {/* pill card — narrowed per lane to avoid overlap */}
            <Pressable onClick={() => onEdit && onEdit(ev)} scale={0.98} style={{
              position: 'absolute',
              left: `calc(${cardLeftBase}px + ${s.lane * lanePct}% - ${s.lane * (cardLeftBase + 12) / LANE_COUNT}px)`,
              width: `calc(${lanePct}% - ${(cardLeftBase + 12) / LANE_COUNT}px - 4px)`,
              top: 0, height: pillH,
            }}>
              <div style={{
                height: '100%', borderRadius: 18,
                background: t.dark ? ev.color + '38' : ev.color + '1a',
                border: `1.5px solid ${ev.color}55`,
                display: 'flex', flexDirection: 'column', justifyContent: 'center',
                padding: '8px 14px', gap: 3, overflow: 'hidden',
              }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
                  <span style={{ fontSize: 11, color: t.textSec, fontWeight: 600, fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', minWidth: 0 }}>
                    {fmtTime(s.sHr)}–{fmtTime(s.eHr)} · {durLabel}
                  </span>
                  {ev.fromRental && <span style={{ fontSize: 8, fontWeight: 700, color: ev.color, background: ev.color + '22', padding: '1px 5px', borderRadius: 100, letterSpacing: 0.3, flexShrink: 0 }}>MIETE</span>}
                </div>
                <div style={{ fontSize: 14.5, fontWeight: 700, color: t.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{ev.title}</div>
                {eqLabel && (
                  <div style={{ fontSize: 11, color: t.textSec, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{eqLabel}</div>
                )}
              </div>
            </Pressable>
          </div>
        );
      })}

      {timed.length === 0 && (
        <div style={{ position: 'absolute', top: 100, left: 0, right: 0, textAlign: 'center', color: t.textSec, fontSize: 13 }}>
          Keine Termine an diesem Tag.
        </div>
      )}
    </div>
  );

  const MultiStrand = () => {
    if (!multi.length) return null;
    const labelFor = (s) => s.untimed ? 'Ganztägig' : 'Mehrtägig';
    const heading = multi.every(s => s.untimed) ? 'Ganztägig' : multi.some(s => s.untimed) ? 'Mehrtägig & ganztägig' : 'Mehrtägig';
    return (
      <div style={{ marginTop: 18, padding: '0 16px' }}>
        <div style={{ fontSize: 11, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 8 }}>
          {heading} · {multi.length} {multi.length === 1 ? 'Eintrag' : 'Einträge'}
        </div>
        <div style={{ position: 'relative', paddingLeft: 36 }}>
          {/* vertical line */}
          <div style={{ position: 'absolute', left: 14, top: 4, bottom: 4, width: 2, background: t.dark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)', borderRadius: 2 }}/>
          {multi.map((s, i) => {
            const ev = s.ev;
            const eq = equipment.find(e => e.id === ev.equipmentId);
            const totalDays = daysBetween(ev.start, ev.end);
            const currentIdx = daysBetween(ev.start, day);
            return (
              <div key={ev.id} style={{ position: 'relative', marginBottom: 10 }}>
                <div style={{ position: 'absolute', left: -35, top: 12, width: 28, height: 28, borderRadius: 14, background: ev.color, border: `3px solid ${t.appBg}`, display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0 2px 6px rgba(0,0,0,0.15)' }}>
                  <Icon d={ev.fromRental ? icons.doc : icons.cal} color="#fff" size={14} sw={2.2}/>
                </div>
                <Pressable onClick={() => onEdit && onEdit(ev)} scale={0.99}>
                  <div style={{
                    borderRadius: 16,
                    background: t.dark ? ev.color + '32' : ev.color + '18',
                    border: `1.5px solid ${ev.color}44`,
                    padding: '11px 14px',
                  }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, color: t.textSec, fontWeight: 600 }}>
                      {s.untimed ? labelFor(s) : `Tag ${currentIdx + 1} / ${totalDays}`}
                      {ev.fromRental && <span style={{ fontSize: 8, fontWeight: 700, color: ev.color, background: ev.color + '22', padding: '1px 5px', borderRadius: 100, letterSpacing: 0.3 }}>MIETE</span>}
                    </div>
                    <div style={{ fontSize: 14.5, fontWeight: 700, color: t.text, marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{ev.title}</div>
                    <div style={{ fontSize: 11.5, color: t.textSec, marginTop: 2 }}>
                      {(ev.equipmentLabel || (eq && eq.name) || '')}{s.untimed ? '' : ' · ' + fmtRange(ev.start, ev.end)}
                    </div>
                  </div>
                </Pressable>
              </div>
            );
          })}
        </div>
      </div>
    );
  };

  return (
    <div style={{ padding: '8px 0 20px' }}>
      {/* Header within the day view (counts only — main header is upstream) */}
      <div style={{ padding: '0 18px', fontSize: 11, color: t.textSec, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 10 }}>
        <span>{dayEvents.length} {dayEvents.length === 1 ? 'Eintrag' : 'Einträge'}</span>
        {timed.length > 0 && <><span style={{ color: t.textTer }}>·</span><span>{timed.length} mit Uhrzeit</span></>}
        {multi.length > 0 && <><span style={{ color: t.textTer }}>·</span><span>{multi.length} mehrtägig</span></>}
      </div>

      {/* HEUTE strand */}
      {timed.length > 0 || dayEvents.length === 0 ? <TimedStrand/> : null}

      {/* MEHRTÄGIG strand */}
      <MultiStrand/>

      {/* Add button */}
      <div style={{ padding: '18px 16px 0' }}>
        <Pressable onClick={() => onAdd && onAdd(day)} scale={0.97}>
          <div style={{
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
            padding: '13px 0', borderRadius: 14,
            border: `1.5px dashed ${t.sep}`, background: 'transparent',
            color: t.textSec, fontSize: 13, fontWeight: 600,
          }}>
            <Icon d={icons.plus} color={t.textSec} size={15} sw={2.2}/>
            Eintrag an diesem Tag hinzufügen
          </div>
        </Pressable>
      </div>
    </div>);
}
// ─────────────────────────────────────────────────────────────
// Main Kalender screen
// ─────────────────────────────────────────────────────────────
// Monday of the week containing iso
function mondayOf(iso) {
  const d = fromISO(iso);
  const wd = (d.getDay() + 6) % 7; // 0 = Monday
  d.setDate(d.getDate() - wd);
  return fmtDateISO(d);
}

function ScreenKalender({ go, events, setEvents, equipment, setEquipment, accessories = [], rentals = [], setRentals, toast }) {
  const t = useTheme();
  const [mode, setMode] = useState('month'); // tag | month | plan | jahr
  // Studio design offers only Monat + Tag; force back if a hidden mode is active.
  useEffect(() => { if (t.studio && mode !== 'month' && mode !== 'tag') setMode('month'); }, [t.studio, mode]);
  const [labelMode, setLabelMode] = useLocal('rf-cal-label-mode', 'name'); // 'name' | 'detail'
  const [view, setView] = useState(() => { const d = new Date(); return { y: d.getFullYear(), m: d.getMonth() }; });
  const [weekStart, setWeekStart] = useState(mondayOf(todayISO()));
  const [selectedDay, setSelectedDay] = useState(null);
  const [daySheetOpen, setDaySheetOpen] = useState(false);
  const [eventSheet, setEventSheet] = useState({ open: false, initial: null, day: null });
  const [peek, setPeek] = useState(null); // read-only popup: the event being previewed
  const [confirmEv, setConfirmEv] = useState(null);
  const [dayViewDate, setDayViewDate] = useState(todayISO());
  const calDirRef = React.useRef(0); // -1/+1 slide direction for month/day transitions
  const [addChoice, setAddChoice] = useState({ open: false, day: null });

  const moveWeek = (delta) => {
    const d = fromISO(weekStart);d.setDate(d.getDate() + delta * 7);
    setWeekStart(fmtDateISO(d));
  };

  const goToToday = () => {
    const d = fromISO(todayISO());
    calDirRef.current = 0;
    setView({ y: d.getFullYear(), m: d.getMonth() });
    setDayViewDate(todayISO());
    setWeekStart(mondayOf(todayISO()));
    toast('Heute · ' + fmtDateDE(todayISO()));
  };

  const move = (delta) => {
    calDirRef.current = Math.sign(delta);
    const d = new Date(view.y, view.m + delta, 1);
    setView({ y: d.getFullYear(), m: d.getMonth() });
  };
  const changeDay = (delta) => {
    calDirRef.current = Math.sign(delta);
    const d = fromISO(dayViewDate); d.setDate(d.getDate() + delta);
    setDayViewDate(fmtDateISO(d));
  };
  // Horizontal swipe across the calendar body → month (Monat) / day (Tag) nav.
  const calBodyRef = React.useRef(null);
  const calSwipe = React.useRef({ x: 0, y: 0, active: false, allowed: false, dir: null });
  const calSwipeBlocked = (target) => {
    let el = target;
    while (el && el.nodeType === 1) {
      if (el === calBodyRef.current) return false; // reached our own swipe root → nothing blocked it
      const cs = window.getComputedStyle(el);
      if (cs.touchAction === 'none' || cs.touchAction === 'pan-y') return true;
      if ((cs.overflowX === 'auto' || cs.overflowX === 'scroll') && el.scrollWidth > el.clientWidth + 4) return true;
      el = el.parentElement;
    }
    return false;
  };
  const onCalDown = (e) => {
    if (e.pointerType === 'mouse') { calSwipe.current.active = false; return; }
    calSwipe.current = { x: e.clientX, y: e.clientY, active: true, allowed: !calSwipeBlocked(e.target), dir: null };
  };
  const onCalMove = (e) => {
    const s = calSwipe.current; if (!s.active || s.dir) return;
    const dx = e.clientX - s.x, dy = e.clientY - s.y;
    if (Math.abs(dx) > 12 || Math.abs(dy) > 12) s.dir = Math.abs(dx) > Math.abs(dy) ? 'h' : 'v';
  };
  const onCalUp = (e) => {
    const s = calSwipe.current; if (!s.active) return; s.active = false;
    if (!s.allowed || s.dir !== 'h') return;
    const dx = e.clientX - s.x, dy = e.clientY - s.y;
    if (Math.abs(dx) < 56 || Math.abs(dy) > 90) return;
    const dir = dx < 0 ? 1 : -1;
    if (mode === 'month') move(dir);
    else if (mode === 'tag') changeDay(dir);
    else if (mode === 'jahr') { calDirRef.current = dir; setView(v => ({ y: v.y + dir, m: v.m })); }
  };

  const onPickDay = (iso) => {setSelectedDay(iso);setDaySheetOpen(true);};
  // Read-only popup first; the user explicitly clicks "Bearbeiten" to edit.
  const onPickEvent = (ev) => { setDaySheetOpen(false); setPeek(ev); };
  const onAdd = (defaultDay) => {
    setDaySheetOpen(false);
    setAddChoice({ open: true, day: defaultDay || todayISO() });
  };
  const onPickAddType = (kind) => {
    const day = addChoice.day;
    setAddChoice({ open: false, day: null });
    if (kind === 'event') {
      setEventSheet({ open: true, initial: null, day });
    } else if (kind === 'rental') {
      go('doc', { newRental: true, origin: 'quick', defaultStart: day });
    }
  };
  const onEditEvent = (ev) => {
    // Used by the DaySheet's edit action — also routes through peek now.
    setDaySheetOpen(false);
    setPeek(ev);
  };
  const onSaveEvent = (ev) => {
    setEvents((prev) => {
      const exists = prev.find((p) => p.id === ev.id);
      if (exists) return prev.map((p) => p.id === ev.id ? ev : p);
      return [ev, ...prev];
    });
    toast(eventSheet.initial ? 'Eintrag gespeichert' : `„${ev.title}“ angelegt`);
  };
  const onDeleteEvent = (ev) => { if (ev.fromRental) return; setConfirmEv(ev); };
  const doDelete = () => {
    setEvents((prev) => prev.filter((p) => p.id !== confirmEv.id));
    toast('Eintrag gelöscht');
    setConfirmEv(null);
  };

  // Single-source change handler — routes back to rentals or events.
  const onEventChange = (ev) => {
    if (ev.fromRental && setRentals) {
      setRentals(prev => prev.map(r => r.id === ev.rentalId ? {
        ...r,
        start: ev.start, end: ev.end,
        startTime: ev.startTime || r.startTime,
        endTime: ev.endTime || r.endTime,
        color: ev.color || r.color,
      } : r));
    } else {
      setEvents(prev => prev.map(p => p.id === ev.id ? ev : p));
    }
  };

  // Rentals automatically surface as calendar entries (single source of truth).
  // Manual calendar events live in `events` and may have no equipment/tenant.
  // Rentals automatically surface as calendar entries (single source of truth).
  // ONE bar per rental, regardless of how many equipment items are attached
  // — a rental is a single booking even when it covers a whole rig.
  // Manual calendar events live in `events` and may have no equipment/tenant.
  // Helper: shift "HH:MM" by N minutes (clamped to 00:00–23:59).
  const shiftTime = (s, mins) => {
    const m = /^(\d{1,2}):(\d{2})$/.exec(s || '');
    if (!m) return '';
    const total = Math.max(0, Math.min(24 * 60 - 1, (+m[1]) * 60 + (+m[2]) + mins));
    const h = Math.floor(total / 60);
    const mm = total % 60;
    return String(h).padStart(2, '0') + ':' + String(mm).padStart(2, '0');
  };
  const rentalEvents = rentals.flatMap((r) => {
    const items = getRentalItems(r);
    const eqLabel = items.length === 0
      ? (r.equipmentName || '')
      : items.length === 1
        ? items[0].equipmentName + (items[0].quantity > 1 ? ' ×' + items[0].quantity : '')
        : items.length + ' Geräte';
    const base = {
      rentalId: r.id, fromRental: true,
      equipmentId: items[0] ? items[0].equipmentId : r.equipmentId,
      equipmentLabel: eqLabel,
      color: r.color || statusMeta(r.status).color,
      note: r.purpose || '', status: r.status,
      delivery: r.delivery,
      itemsCount: items.length || 1,
    };
    const isMulti = r.start !== r.end;
    // Multi-day rental with times → surface Abholung + Rückgabe as their own
    // timed events on the respective days, in addition to the multi-day banner.
    if (isMulti && r.startTime && r.endTime) {
      return [
        { ...base, id: 'r:' + r.id, title: r.tenantName,
          start: r.start, end: r.end, startTime: '', endTime: '',
          pickupTime: r.startTime, returnTime: r.endTime },
        { ...base, id: 'r:' + r.id + ':pickup', synthetic: 'pickup',
          title: outLabel(r) + ' · ' + r.tenantName,
          start: r.start, end: r.start,
          startTime: r.startTime, endTime: shiftTime(r.startTime, 30) },
        { ...base, id: 'r:' + r.id + ':return', synthetic: 'return',
          title: 'Rückgabe · ' + r.tenantName,
          start: r.end, end: r.end,
          startTime: shiftTime(r.endTime, -30), endTime: r.endTime },
      ];
    }
    // Single-day or timeless rental — keep as one event.
    return [{
      ...base, id: 'r:' + r.id, title: r.tenantName,
      start: r.start, end: r.end,
      startTime: r.startTime, endTime: r.endTime,
      pickupTime: r.startTime, returnTime: r.endTime,
    }];
  });
  const mergedEvents = [...rentalEvents, ...events];
  // Synthetic Abholung/Rückgabe pills are only useful for day-level route planning.
  // Everywhere else (month grid, year, upcoming list) we keep just the rental banner.
  const overviewEvents = mergedEvents.filter((e) => !e.synthetic);

  const monthLabel = `${DE_MONTHS[view.m]} ${view.y}`;

  // Delete a rental from the calendar peek: also rolls back the equipment
  // "uses" counter so best-seller/utilization stats stay correct.
  const onDeleteRentalFromCal = (r) => {
    if (!setRentals) return;
    setRentals((prev) => prev.filter((p) => p.id !== r.id));
    const its = getRentalItems(r);
    if (its.length && setEquipment) {
      const ids = new Set(its.map((it) => it.equipmentId));
      setEquipment((prev) => prev.map((e) => ids.has(e.id) ? { ...e, uses: Math.max(0, (Number(e.uses) || 0) - 1) } : e));
    }
    setPeek(null);
    toast && toast(`„${r.tenantName}“ gelöscht`);
  };

  return (
    <div style={{ paddingBottom: 100 }}>
      {/* Header */}
      <div style={{ padding: '18px 22px 0' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            {(mode === 'tag' ? DE_DAYS[fromISO(dayViewDate).getDay()] : mode === 'jahr' ? 'Übersicht' : mode === 'plan' ? 'Equipment × Zeit' : '') !== '' &&
            <div style={{ fontSize: 13, color: t.textSec, fontWeight: 500 }}>{mode === 'tag' ? DE_DAYS[fromISO(dayViewDate).getDay()] : mode === 'jahr' ? 'Übersicht' : mode === 'plan' ? 'Equipment × Zeit' : ''}</div>
            }
            <div style={{ fontSize: 34, fontWeight: 700, color: t.text, letterSpacing: -1, lineHeight: 1.05 }}>
              {mode === 'tag' ? fmtDateDE(dayViewDate) : mode === 'jahr' ? view.y : mode === 'plan' ? 'Belegungsplan' : monthLabel}
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
            <Pressable onClick={goToToday} scale={0.94}>
              <div style={{ height: 40, padding: '0 16px', borderRadius: 999, border: `0.5px solid ${t.sep}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 13, fontWeight: 600, color: t.text }}>Heute</div>
            </Pressable>
            <Pressable onClick={() => onAdd(mode === 'tag' ? dayViewDate : todayISO())} scale={0.9}>
              <div style={{ width: 40, height: 40, borderRadius: 999, background: t.solidBg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon d={icons.plus} color={t.solidFg} size={19} sw={2.4} />
              </div>
            </Pressable>
            <CompanyGear size={40} />
          </div>
        </div>
      </div>

      {/* Mode + month nav */}
      <div style={{ padding: '14px 16px 0', display: 'flex', gap: 6, alignItems: 'center' }}>
        <div style={{ display: 'flex', padding: 3, borderRadius: t.studio ? 999 : 10, background: t.studio ? 'transparent' : t.chipStrong, border: t.studio ? `0.5px solid ${t.sep}` : 'none', flex: 1 }}>
          {(t.studio ? [['tag', 'Tag'], ['month', 'Monat']] : [['tag', 'Tag'], ['month', 'Monat'], ['plan', 'Plan'], ['jahr', 'Jahr']]).map(([id, label]) =>
          <Pressable key={id} onClick={() => { calDirRef.current = 0; setMode(id); }} scale={0.96} style={{ flex: 1 }}>
              <div style={{
              padding: '6px 0', textAlign: 'center', borderRadius: t.studio ? 999 : 8,
              fontSize: 12, fontWeight: 600,
              background: mode === id ? (t.studio ? t.solidBg : (t.dark ? t.cardAlt : '#fff')) : 'transparent',
              color: mode === id ? (t.studio ? t.solidFg : t.text) : t.textSec,
              boxShadow: mode === id && !t.studio ? '0 1px 3px rgba(0,0,0,0.08)' : 'none'
            }}>{label}</div>
            </Pressable>
          )}
        </div>
        {mode === 'tag' &&
        <>
            <Pressable onClick={() => changeDay(-1)} scale={0.88}>
              <div style={{ width: 32, height: 32, borderRadius: 16, background: t.studio ? 'transparent' : t.chip, border: t.studio ? `0.5px solid ${t.sep}` : 'none', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon d={icons.back} color={t.text} size={14} sw={2} />
              </div>
            </Pressable>
            <Pressable onClick={() => changeDay(1)} scale={0.88}>
              <div style={{ width: 32, height: 32, borderRadius: 16, background: t.studio ? 'transparent' : t.chip, border: t.studio ? `0.5px solid ${t.sep}` : 'none', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon d={icons.chev} color={t.text} size={14} sw={2} />
              </div>
            </Pressable>
          </>
        }
        {mode === 'month' &&
        <>
            <Pressable onClick={() => move(-1)} scale={0.88}>
              <div style={{ width: 32, height: 32, borderRadius: 16, background: t.studio ? 'transparent' : t.chip, border: t.studio ? `0.5px solid ${t.sep}` : 'none', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon d={icons.back} color={t.text} size={14} sw={2} />
              </div>
            </Pressable>
            <Pressable onClick={() => move(1)} scale={0.88}>
              <div style={{ width: 32, height: 32, borderRadius: 16, background: t.studio ? 'transparent' : t.chip, border: t.studio ? `0.5px solid ${t.sep}` : 'none', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Icon d={icons.chev} color={t.text} size={14} sw={2} />
              </div>
            </Pressable>
          </>
        }
      </div>

      {/* Body — horizontal swipe = Monat/Tag-Navigation; touch-action: pan-y blockt zugleich den globalen Tab-Wisch hier im Kalender */}
      <div ref={calBodyRef} onPointerDown={onCalDown} onPointerMove={onCalMove} onPointerUp={onCalUp}
           onPointerCancel={() => { calSwipe.current.active = false; }}
           style={{ touchAction: 'pan-y' }}>
      <div key={mode + '-' + (mode === 'tag' ? dayViewDate : view.y + '-' + view.m)}
           style={{ animation: calDirRef.current > 0 ? 'rfSlideR 0.3s cubic-bezier(0.2,0.7,0.3,1)' : calDirRef.current < 0 ? 'rfSlideL 0.3s cubic-bezier(0.2,0.7,0.3,1)' : 'none' }}>
      {mode === 'tag' && <CalDay dayISO={dayViewDate} events={mergedEvents} equipment={equipment} onAdd={onAdd} onEdit={onEditEvent} onChange={onEventChange} toast={toast} />}
      {mode === 'month' &&
      <CalMonthView
        view={view} setView={setView} events={overviewEvents} equipment={equipment}
        labelMode={labelMode} setLabelMode={setLabelMode}
        onPickDay={onPickDay} onPickEvent={onPickEvent}
        onChange={onEventChange} toast={toast} />

      }
      {mode === 'plan' &&
      <CalPlanView
        view={view} events={events} equipment={equipment} rentals={rentals} setRentals={setRentals}
        weekStartISO={weekStart} onMoveWeek={moveWeek}
        onPickEvent={onPickEvent} onPickDay={onPickDay} toast={toast} />

      }
      {mode === 'jahr' &&
      <CalYear year={view.y} events={overviewEvents} onSelect={(mi) => {setView({ y: view.y, m: mi });setMode('month');}} />
      }

      {/* Heutige Termine (only in month) */}
      {mode === 'month' &&
      <>
          {(() => {
            const today = todayISO();
            const dObj = fromISO(today);
            const dateLabel = `${DE_DAYS[dObj.getDay()]}, ${dObj.getDate()}. ${DE_MONTHS[dObj.getMonth()]}`;
            return (
              <div style={{ padding: '24px 20px 8px', display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
                <div style={{ fontSize: 15, fontWeight: 600, color: t.text, letterSpacing: -0.3 }}>Termine heute</div>
                <div style={{ fontSize: 12, color: t.textSec }}>{dateLabel}</div>
              </div>
            );
          })()}
          <div style={{ padding: '0 16px' }}>
            <div style={{ background: t.card, borderRadius: 16, boxShadow: t.shadowCard, overflow: 'hidden' }}>
              {(() => {
                const today = todayISO();
                const list = overviewEvents
                  .filter((e) => e.start <= today && e.end >= today)
                  .sort((a, b) => (a.startTime || a.pickupTime || '99:99').localeCompare(b.startTime || b.pickupTime || '99:99'));
                if (list.length === 0) return (
                  <div style={{ padding: '24px 16px', textAlign: 'center', color: t.textSec, fontSize: 13 }}>Heute keine Einträge.</div>
                );
                return list.map((ev, i) => {
                  const eq = equipment.find(e => e.id === ev.equipmentId);
                  const isRental = !!ev.fromRental;
                  const pu = ev.pickupTime || ev.startTime;
                  const rt = ev.returnTime || ev.endTime;
                  const isStartDay = ev.start === today;
                  const isEndDay   = ev.end === today;
                  return (
                    <LongPressRow key={ev.id}
                      bg={t.card}
                      style={{ borderRadius: 0 }}
                      onTap={() => onPickEvent(ev)}
                      onEdit={() => onPickEvent(ev)}
                      onDelete={isRental ? null : () => onDeleteEvent(ev)}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px', borderBottom: i < list.length - 1 ? `0.5px solid ${t.sep}` : 'none' }}>
                        {isRental ? (
                          <div style={{ width: 38, height: 38, borderRadius: 10, background: ev.color + '22', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                            <Icon d={icons.doc} color={ev.color} size={18} sw={1.9}/>
                          </div>
                        ) : (
                          <div style={{ width: 38, height: 38, borderRadius: 10, background: ev.color + '22', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                            <div style={{ fontSize: 8, color: ev.color, fontWeight: 700, lineHeight: 1, textTransform: 'uppercase', letterSpacing: 0.3 }}>{DE_MONTHS_SHORT[fromISO(ev.start).getMonth()]}</div>
                            <div style={{ fontSize: 15, color: ev.color, fontWeight: 700, lineHeight: 1.1 }}>{fromISO(ev.start).getDate()}</div>
                          </div>
                        )}
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontSize: 14, fontWeight: 600, color: t.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', display: 'flex', alignItems: 'center', gap: 5 }}>
                            {ev.title}
                            {isRental && <span style={{ fontSize: 8, fontWeight: 700, color: ev.color, background: ev.color + '22', padding: '1px 5px', borderRadius: 100, letterSpacing: 0.3 }}>MIETE</span>}
                          </div>
                          <div style={{ fontSize: 11, color: t.textSec, marginTop: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                            {(ev.equipmentLabel || (eq && eq.name) || 'Allgemein')} · {fmtRange(ev.start, ev.end)}
                          </div>
                          {isRental && (pu || rt) && (() => {
                            const sd = fromISO(ev.start), ed = fromISO(ev.end);
                            const sLbl = `${String(sd.getDate()).padStart(2,'0')}.${String(sd.getMonth()+1).padStart(2,'0')}.`;
                            const eLbl = `${String(ed.getDate()).padStart(2,'0')}.${String(ed.getMonth()+1).padStart(2,'0')}.`;
                            return (
                              <div style={{ fontSize: 11, color: t.text, fontWeight: 600, marginTop: 3, display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', fontVariantNumeric: 'tabular-nums' }}>
                                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, opacity: isStartDay || !isEndDay ? 1 : 0.45 }}>↑ {outLabel(ev)} <span style={{ color: t.textSec, fontWeight: 500 }}>{sLbl}</span> <b>{pu ? pu + ' Uhr' : '–'}</b></span>
                                <span style={{ color: t.textTer }}>·</span>
                                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, opacity: isEndDay || !isStartDay ? 1 : 0.45 }}>↓ Rückgabe <span style={{ color: t.textSec, fontWeight: 500 }}>{eLbl}</span> <b>{rt ? rt + ' Uhr' : '–'}</b></span>
                              </div>
                            );
                          })()}
                        </div>
                        <Icon d={icons.chev} color={t.textTer} size={14} sw={2}/>
                      </div>
                    </LongPressRow>);
                });
              })()}
            </div>
          </div>
        </>
      }
      </div>
      </div>

      <DaySheet
        open={daySheetOpen} dayISO={selectedDay}
        onClose={() => setDaySheetOpen(false)}
        events={mergedEvents} equipment={equipment}
        onAdd={onAdd} onEdit={onEditEvent} onDelete={onDeleteEvent} />
      
      {/* Read-only quick-view bottom sheet */}
      <RentalEventPeekSheet
        peek={peek} onClose={() => setPeek(null)}
        equipment={equipment} accessories={accessories} rentals={rentals} go={go}
        onColorChange={(c) => { onEventChange({ ...peek, color: c }); setPeek({ ...peek, color: c }); }}
        onEditEvent={(ev) => setEventSheet({ open: true, initial: ev, day: null })}
        onDeleteRental={onDeleteRentalFromCal} />

      <EventSheet
        open={eventSheet.open}
        onClose={() => setEventSheet({ open: false, initial: null, day: null })}
        initial={eventSheet.initial}
        defaultDate={eventSheet.day}
        equipment={equipment}
        onSave={onSaveEvent}
        onDelete={onDeleteEvent} />
      
      <Confirm open={!!confirmEv} title="Eintrag löschen?"
      message={confirmEv ? `„${confirmEv.title}" wird entfernt.` : ''}
      onCancel={() => setConfirmEv(null)} onConfirm={doDelete} />

      {/* +Eintrag choice — Termin vs neue Miete */}
      <Sheet open={addChoice.open} onClose={() => setAddChoice({ open: false, day: null })}>
        <div style={{ padding: '4px 24px 24px' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
            <div>
              <div style={{ fontSize: 22, fontWeight: 700, color: t.text, letterSpacing: -0.5 }}>Was anlegen?</div>
              <div style={{ fontSize: 13, color: t.textSec, marginTop: 4 }}>{addChoice.day && fmtDateDE(addChoice.day)}</div>
            </div>
            <Pressable onClick={() => setAddChoice({ open: false, day: null })}><div style={{ fontSize: 14, color: t.textSec, fontWeight: 500 }}>Abbrechen</div></Pressable>
          </div>
          <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 10 }}>
            <Pressable onClick={() => onPickAddType('event')} scale={0.98}>
              <div style={{ background: t.card, borderRadius: 16, padding: '14px 16px', display: 'flex', alignItems: 'center', gap: 14 }}>
                <div style={{ width: 44, height: 44, borderRadius: 12, background: t.accentSoft, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <Icon d={icons.cal} color={t.accent} size={22} sw={1.8}/>
                </div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 15, fontWeight: 700, color: t.text }}>Allgemeiner Eintrag</div>
                  <div style={{ fontSize: 11.5, color: t.textSec, marginTop: 1 }}>Termin · Erinnerung · Notiz</div>
                </div>
                <Icon d={icons.chev} color={t.textTer} size={15} sw={2.4}/>
              </div>
            </Pressable>
            <Pressable onClick={() => onPickAddType('rental')} scale={0.98}>
              <div style={{ background: t.solidBg, color: t.solidFg, borderRadius: 16, padding: '14px 16px', display: 'flex', alignItems: 'center', gap: 14, boxShadow: '0 6px 16px rgba(0,0,0,0.28)' }}>
                <div style={{ width: 44, height: 44, borderRadius: 12, background: 'rgba(255,255,255,0.18)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <Icon d={icons.doc} color={t.solidFg} size={22} sw={1.8}/>
                </div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 15, fontWeight: 700 }}>Neue Miete</div>
                  <div style={{ fontSize: 11.5, opacity: 0.85, marginTop: 1 }}>Mietvertrag mit Equipment + Mieter</div>
                </div>
                <Icon d={icons.chev} color={t.solidFg} size={15} sw={2.4}/>
              </div>
            </Pressable>
          </div>
        </div>
      </Sheet>
    </div>);

}


// Read-only quick-view of a rental/event — reused by Kalender + Dashboard.
function RentalEventPeekSheet({ peek, onClose, equipment = [], accessories = [], rentals = [], go, onColorChange, onEditEvent, onEditRental, onDeleteRental }) {
  const t = useTheme();
  const [confirmDel, setConfirmDel] = useState(false);
  const [showPeekContact, setShowPeekContact] = useState(false);
  return (
        <Sheet open={!!peek} onClose={onClose} maxHeight="86%">
          {peek && (() => {
            const ev = peek;
            const rental = ev.fromRental ? rentals.find(r => r.id === ev.rentalId) : null;
            const its = rental ? getRentalItems(rental) : (ev.equipmentId ? [{ equipmentId: ev.equipmentId, equipmentName: (equipment.find(e => e.id === ev.equipmentId) || {}).name, quantity: 1, dailyRate: (equipment.find(e => e.id === ev.equipmentId) || {}).price }] : []);
            const days = rental ? billableDays(rental) : daysBetween(ev.start, ev.end);
            const sm = rental ? statusMeta(rental.status) : (ev.status ? statusMeta(ev.status) : null);
            const subtotal = rental ? (window.rentalSubtotal ? window.rentalSubtotal(rental) : 0) : 0;
            const total = rental ? (window.rentalTotal ? window.rentalTotal(rental) : 0) : 0;
            const deliveryFee = rental && window.rentalDeliveryFee ? window.rentalDeliveryFee(rental) : 0;
            const accItems = rental ? (rental.accessoryItems || []) : [];
  
            const onEdit = () => {
              onClose();
              if (ev.fromRental) {
                if (onEditRental) { onEditRental(rental || ev); }
                else { go('doc', { rentalId: ev.rentalId, origin: 'cal' }); }
              }
              else if (onEditEvent) { onEditEvent(ev); }
            };
  
            return (
              <div style={{ padding: '4px 4px 24px' }}>
                {/* Header */}
                <div style={{ padding: '0 20px', display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12 }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <div style={{ width: 28, height: 28, borderRadius: 14, background: ev.color, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                        <Icon d={ev.fromRental ? icons.doc : icons.cal} color="#fff" size={15} sw={2}/>
                      </div>
                      <div style={{ fontSize: 12, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.7 }}>
                        {ev.fromRental ? 'Mietvertrag' : 'Termin'}
                      </div>
                      {sm && (
                        <span style={{ fontSize: 10, fontWeight: 700, color: sm.color, background: sm.color + '22', padding: '3px 8px', borderRadius: 100, letterSpacing: 0.4 }}>● {sm.label}</span>
                      )}
                    </div>
                    <div style={{ fontSize: 24, fontWeight: 700, color: t.text, letterSpacing: -0.6, marginTop: 8 }}>{ev.title}</div>
                  </div>
                  <Pressable onClick={onClose} scale={0.85}>
                    <div style={{ width: 32, height: 32, borderRadius: 16, background: t.chip, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                      <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke={t.text} strokeWidth="2.2" strokeLinecap="round"><path d="M3 3l6 6M9 3l-6 6"/></svg>
                    </div>
                  </Pressable>
                </div>
  
                {/* Quick stats row — period + total */}
                <div style={{ padding: '16px 16px 0', display: 'grid', gridTemplateColumns: rental ? '1fr 1fr' : '1fr', gap: 8 }}>
                  <div style={{ background: t.card, borderRadius: 14, padding: '12px 14px' }}>
                    <div style={{ fontSize: 10, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5 }}>Zeitraum</div>
                    <div style={{ fontSize: 15, fontWeight: 700, color: t.text, marginTop: 3, fontVariantNumeric: 'tabular-nums' }}>{fmtRange(ev.start, ev.end)}</div>
                    <div style={{ fontSize: 11, color: t.textSec, marginTop: 1 }}>{days} Tag{days > 1 ? 'e' : ''}{(ev.startTime || ev.endTime) ? ` · ${ev.startTime || '–'}–${ev.endTime || '–'}` : ''}</div>
                  </div>
                  {rental && (
                    <div style={{ background: t.card, borderRadius: 14, padding: '12px 14px' }}>
                      <div style={{ fontSize: 10, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5 }}>Gesamtpreis</div>
                      <div style={{ fontSize: 18, fontWeight: 700, color: t.text, marginTop: 3, fontVariantNumeric: 'tabular-nums' }}>{total} €</div>
                      <div style={{ fontSize: 11, color: t.textSec, marginTop: 1 }}>
                        {rental.paymentStatus === 'bezahlt' ? <span style={{ color: t.green, fontWeight: 700 }}>● Bezahlt</span> : <span style={{ color: t.orange, fontWeight: 700 }}>● Offen</span>}
                      </div>
                    </div>
                  )}
                </div>
  
                {/* Abholung / Rückgabe (bzw. Liefer-/Abholzeitpunkt) — immer bei Mieten */}
                {rental && (
                  <div style={{ padding: '10px 16px 0' }}>
                    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                      {(() => {
                        const sd = fromISO(rental.start), ed = fromISO(rental.end);
                        const sLbl = `${sd.getDate()}. ${DE_MONTHS_SHORT[sd.getMonth()]}`;
                        const eLbl = `${ed.getDate()}. ${DE_MONTHS_SHORT[ed.getMonth()]}`;
                        const Tile = ({ arrow, color, label, dateLbl, time }) => (
                          <div style={{ background: t.card, borderRadius: 14, padding: '12px 14px' }}>
                            <div style={{ fontSize: 10, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5, display: 'flex', alignItems: 'center', gap: 5 }}>
                              <span style={{ color, fontSize: 12 }}>{arrow}</span>
                              {label}
                            </div>
                            <div style={{ fontSize: 16, fontWeight: 700, color: t.text, marginTop: 4, fontVariantNumeric: 'tabular-nums' }}>{time || '–'}{time ? ' Uhr' : ''}</div>
                            <div style={{ fontSize: 11.5, color: t.textSec, marginTop: 1 }}>{dateLbl}</div>
                          </div>
                        );
                        return (
                          <>
                            <Tile arrow="↑" color={t.green || '#34C759'} label={outLabel(rental, true)} dateLbl={sLbl} time={rental.startTime}/>
                            <Tile arrow="↓" color={t.red   || '#FF3B30'} label="Rückgabe" dateLbl={eLbl} time={rental.endTime}/>
                          </>
                        );
                      })()}
                    </div>
                  </div>
                )}
  
                {/* Tenant block — name always; contact collapsible. Address lives in delivery section when delivering. */}
                {rental && (() => {
                  const showAddr = rental.address && !(rental.delivery && rental.delivery.enabled);
                  const hasContact = rental.phone || rental.email || showAddr;
                  return (
                  <>
                    <div style={{ padding: '20px 20px 6px', fontSize: 11, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>Mieter</div>
                    <div style={{ padding: '0 16px' }}>
                      <div style={{ background: t.card, borderRadius: 14, padding: '12px 14px' }}>
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
                          <div style={{ fontSize: 14, fontWeight: 700, color: t.text }}>{rental.tenantName}</div>
                          {rental.delivery && rental.delivery.enabled && (rental.delivery.address || rental.address) ? null : hasContact && (
                            <Pressable onClick={() => setShowPeekContact((s) => !s)} scale={0.9}>
                              <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 12, color: t.accent, fontWeight: 600 }}>
                                {showPeekContact ? 'Weniger' : 'Kontakt'}
                                <Icon d={showPeekContact ? 'M5 15l7-7 7 7' : 'M5 9l7 7 7-7'} color={t.accent} size={13} sw={2.2} />
                              </div>
                            </Pressable>
                          )}
                        </div>
                        {rental.delivery && rental.delivery.enabled && (rental.delivery.address || rental.address) && (
                          <div style={{ display: 'flex', alignItems: 'flex-start', gap: 6, marginTop: 8, paddingTop: 8, borderTop: `0.5px solid ${t.sep}` }}>
                            <Icon d="M12 21s-6-5.7-6-10a6 6 0 0112 0c0 4.3-6 10-6 10z" color={t.textSec} size={13} sw={1.8} />
                            <div style={{ fontSize: 12, color: t.textSec, whiteSpace: 'pre-line' }}>{rental.delivery.address || rental.address}</div>
                          </div>
                        )}
                        {showPeekContact && hasContact && (
                          <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginTop: 8, paddingTop: 8, borderTop: `0.5px solid ${t.sep}` }}>
                            {rental.phone && <div style={{ fontSize: 12, color: t.textSec }}>{rental.phone}</div>}
                            {rental.email && <div style={{ fontSize: 12, color: t.textSec }}>{rental.email}</div>}
                            {showAddr && <div style={{ fontSize: 12, color: t.textSec, whiteSpace: 'pre-line' }}>{rental.address}</div>}
                          </div>
                        )}
                      </div>
                    </div>
                  </>
                  );
                })()}
  
                {/* Equipment list */}
                {its.length > 0 && (
                  <>
                    <div style={{ padding: '16px 20px 6px', fontSize: 11, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>
                      Equipment · {its.length}{its.length > 1 ? ' Positionen' : ' Position'}
                    </div>
                    <div style={{ padding: '0 16px' }}>
                      <div style={{ background: t.card, borderRadius: 14, overflow: 'hidden' }}>
                        {its.map((it, idx) => {
                          const eq = equipment.find(e => e.id === it.equipmentId);
                          const perPiece = Number(it.dailyRate) || 0;
                          const sum = days * perPiece * Math.max(1, Number(it.quantity) || 1);
                          return (
                            <div key={idx} style={{ padding: '12px 14px', display: 'flex', gap: 12, alignItems: 'center', borderBottom: idx < its.length - 1 ? `0.5px solid ${t.sep}` : 'none' }}>
                              {eq && <EqAvatar item={eq} size={42} radius={10}/>}
                              <div style={{ flex: 1, minWidth: 0 }}>
                                <div style={{ fontSize: 14, fontWeight: 600, color: t.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                                  {it.equipmentName || (eq && eq.name)}
                                </div>
                                <div style={{ fontSize: 11, color: t.textSec, marginTop: 1, fontVariantNumeric: 'tabular-nums' }}>
                                  {it.quantity} Stück · {perPiece} €/Tag
                                </div>
                              </div>
                              <div style={{ fontSize: 14, fontWeight: 700, color: t.text, fontVariantNumeric: 'tabular-nums' }}>{sum} €</div>
                            </div>
                          );
                        })}
                        {rental && (
                          <div style={{ padding: '11px 14px', background: t.cardAlt, display: 'flex', justifyContent: 'space-between', borderTop: `0.5px solid ${t.sep}` }}>
                            <div style={{ fontSize: 12, color: t.textSec, fontWeight: 500 }}>Summe · {days} Tag{days > 1 ? 'e' : ''}{deliveryFee > 0 ? ` · inkl. ${deliveryFee.toFixed(2).replace('.', ',')} € Lieferung` : ''}</div>
                            <div style={{ fontSize: 14, fontWeight: 700, color: t.text }}>{subtotal} €</div>
                          </div>
                        )}
                      </div>
                    </div>
                  </>
                )}
  
                {/* Accessories — what's out with this rental */}
                {accItems.length > 0 && (
                  <>
                    <div style={{ padding: '16px 20px 6px', fontSize: 11, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>
                      Zubehör unterwegs · {accItems.length}
                    </div>
                    <div style={{ padding: '0 16px' }}>
                      <div style={{ background: t.card, borderRadius: 14, padding: '6px 14px' }}>
                        {accItems.map((a, idx) => (
                          <div key={idx} style={{ padding: '8px 0', display: 'flex', alignItems: 'center', gap: 8, borderBottom: idx < accItems.length - 1 ? `0.5px solid ${t.sep}` : 'none' }}>
                            <span style={{ fontSize: 15 }}>{window.accCatEmoji ? window.accCatEmoji((accessories.find(x => x.id === a.accessoryId) || {}).cat) : '📦'}</span>
                            <div style={{ flex: 1, fontSize: 13, fontWeight: 600, color: t.text }}>{a.name}</div>
                            <div style={{ fontSize: 12.5, color: t.textSec, fontVariantNumeric: 'tabular-nums' }}>{a.quantity}×</div>
                          </div>
                        ))}
                      </div>
                    </div>
                  </>
                )}

                {/* Delivery info — for rentals */}
                {rental && rental.delivery && rental.delivery.enabled && (
                  <>
                    <div style={{ padding: '16px 20px 6px', fontSize: 11, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>Lieferung</div>
                    <div style={{ padding: '0 16px' }}>
                      <div style={{ background: t.card, borderRadius: 14, padding: '12px 14px' }}>
                        <div style={{ fontSize: 13, color: t.text, fontWeight: 500, whiteSpace: 'pre-line' }}>{rental.delivery.address || rental.address || '—'}</div>
                        <div style={{ fontSize: 11, color: t.textSec, marginTop: 4, fontVariantNumeric: 'tabular-nums' }}>{rental.delivery.km || 0} km einfach · Pauschale + km × Satz</div>
                      </div>
                    </div>
                  </>
                )}
  
                {/* Notes */}
                {(ev.note || (rental && (rental.note || rental.purpose))) && (
                  <>
                    <div style={{ padding: '16px 20px 6px', fontSize: 11, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>Notiz</div>
                    <div style={{ padding: '0 16px' }}>
                      <div style={{ background: t.card, borderRadius: 14, padding: '12px 14px', fontSize: 13, color: t.text, fontWeight: 500, whiteSpace: 'pre-line' }}>
                        {ev.note || (rental && rental.note) || (rental && rental.purpose)}
                      </div>
                    </div>
                  </>
                )}
  
                {/* Calendar color — editable inline */}
                {onColorChange && <>
                <div style={{ padding: '16px 20px 6px', fontSize: 11, color: t.textSec, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>Farbe im Kalender</div>
                <div style={{ padding: '0 16px' }}>
                  <div style={{ background: t.card, borderRadius: 14, padding: '12px 14px', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
                    {EVENT_COLORS.map(c => {
                      const sel = ev.color === c;
                      return (
                        <Pressable key={c} onClick={() => onColorChange(c)} scale={0.88}>
                          <div style={{
                            width: 30, height: 30, borderRadius: 15, background: c,
                            border: sel ? '3px solid #fff' : '0',
                            boxShadow: sel ? `0 0 0 2px ${c}, 0 1px 4px rgba(0,0,0,0.15)` : '0 1px 2px rgba(0,0,0,0.1)',
                          }}/>
                        </Pressable>
                      );
                    })}
                    <label title="Eigene Farbe" style={{
                      width: 30, height: 30, borderRadius: 15, cursor: 'pointer',
                      background: `conic-gradient(from 0deg, #ff595e, #ffca3a, #8ac926, #1982c4, #6a4c93, #ff595e)`,
                      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                      border: '2px solid ' + t.appBg, boxShadow: '0 1px 2px rgba(0,0,0,0.15)',
                    }}>
                      <input type="color" value={ev.color || '#007AFF'}
                        onChange={(e) => onColorChange(e.target.value)}
                        style={{ opacity: 0, width: 1, height: 1, pointerEvents: 'none' }}/>
                      <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3v3M12 18v3M5 5l2 2M17 17l2 2M3 12h3M18 12h3M5 19l2-2M17 7l2-2"/></svg>
                    </label>
                  </div>
                </div>
  
                </>}
  
                {/* Action bar */}
                <div style={{ padding: '20px 16px 0', display: 'flex', gap: 10 }}>
                  {rental && onDeleteRental && (
                    <Pressable onClick={() => setConfirmDel(true)} scale={0.88}>
                      <div style={{ width: 48, height: 48, borderRadius: 14, background: t.redSoft, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <Icon d="M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14" color={t.red} size={18} sw={2}/>
                      </div>
                    </Pressable>
                  )}
                  <Pressable onClick={onClose} scale={0.97} style={{ flex: 1 }}>
                    <div style={{ padding: '14px 0', textAlign: 'center', borderRadius: 14, background: t.chip, color: t.text, fontSize: 15, fontWeight: 600 }}>Schließen</div>
                  </Pressable>
                  <Pressable onClick={onEdit} scale={0.97} style={{ flex: 1.4 }}>
                    <div style={{ padding: '14px 0', textAlign: 'center', borderRadius: 14, background: t.solidBg, color: t.solidFg, fontSize: 15, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, boxShadow: '0 6px 18px rgba(0,0,0,0.25)' }}>
                      <Icon d="M12 20h9M16.5 3.5a2.121 2.121 0 113 3L7 19l-4 1 1-4L16.5 3.5z" color={t.solidFg} size={15} sw={2}/>
                      Bearbeiten
                    </div>
                  </Pressable>
                </div>

                <Confirm open={confirmDel} title="Miete löschen?"
                  message={`„${rental ? rental.tenantName : ''}“ wird dauerhaft gelöscht. Equipment-Statistiken werden entsprechend zurückgerechnet.`}
                  onCancel={() => setConfirmDel(false)}
                  onConfirm={() => { setConfirmDel(false); onDeleteRental && rental && onDeleteRental(rental); }} />
              </div>
            );
          })()}
        </Sheet>
  );
}
Object.assign(window, { RentalEventPeekSheet });

Object.assign(window, { ScreenKalender, MiniRangePicker, CalPlanView, CalMonthView });