// ===== Kapazitätsplanung: rollierende Jahreskapazität =====
const { useState: useCS } = React;

const WD_NAMES = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'];
const ROW_ORDER = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'];
const MONTHS = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
const ymd = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;

function defaultCapacity() {
  return {
    shiftsPerDay: 3, hoursPerShift: 8, breaks: 30, oee: 85, systems: 1,
    weekdays: { Mo: true, Di: true, Mi: true, Do: true, Fr: true, Sa: false, So: false },
    overrides: {},
    overtime: {},
  };
}

// Heatmap-Start: Montag der laufenden Woche (deterministisch ab 01.06.2026)
function startMonday() {
  const d = new Date(2026, 5, 1);
  const off = (d.getDay() + 6) % 7;
  d.setDate(d.getDate() - off);
  return d;
}

const SHIFT_COLORS = ['var(--surface-2)',
  'color-mix(in oklch, var(--accent-ink) 22%, var(--surface))',
  'color-mix(in oklch, var(--accent-ink) 52%, var(--surface))',
  'var(--accent-ink)'];
const SHIFT_BORDERS = ['var(--line)',
  'color-mix(in oklch, var(--accent-ink) 40%, var(--line))',
  'color-mix(in oklch, var(--accent-ink) 70%, var(--line))',
  'var(--accent-ink)'];

function CapacityPlanner({ capBySystem, setCapFor, currentSystem }) {
  const sysIds = Object.keys(capBySystem);
  const [sys, setSys] = useCS(currentSystem && capBySystem[currentSystem] ? currentSystem : sysIds[0]);
  const cap = capBySystem[sys];
  const setCap = (fn) => setCapFor(sys, fn);
  const [range, setRange] = useCS({ von: '', bis: '', shifts: 1 });

  const netShiftH = Math.max(0, ((cap.hoursPerShift || 8) * 60 - cap.breaks) / 60);
  const regelTag = cap.systems * cap.shiftsPerDay * netShiftH * cap.oee / 100;
  const operatingDays = ROW_ORDER.filter(w => cap.weekdays[w]).length;
  const regelWoche = regelTag * operatingDays;

  const dayShifts = (date) => {
    const key = ymd(date);
    if (cap.overrides[key] != null) return cap.overrides[key];
    const wd = WD_NAMES[date.getDay()];
    return cap.weekdays[wd] ? cap.shiftsPerDay : 0;
  };
  const dayHours = (s) => cap.systems * s * netShiftH * cap.oee / 100;

  const cycle = (date) => {
    const key = ymd(date);
    const cur = dayShifts(date);
    const nxt = (cur + 1) % 4;
    setCap(c => ({ ...c, overrides: { ...c.overrides, [key]: nxt } }));
  };

  const applyRange = () => {
    if (!range.von || !range.bis) return;
    const a = new Date(range.von), b = new Date(range.bis);
    if (b < a) return;
    setCap(c => {
      const ov = { ...c.overrides };
      for (let d = new Date(a); d <= b; d.setDate(d.getDate() + 1)) ov[ymd(d)] = range.shifts;
      return { ...c, overrides: ov };
    });
  };
  const clearOverrides = () => setCap(c => ({ ...c, overrides: {} }));

  // Überstunden
  const [ot, setOt] = useCS({ von: '', bis: '', hours: 4 });
  const dayOvertime = (date) => (cap.overtime && cap.overtime[ymd(date)]) || 0;
  const OT_STEPS = [0, 2, 4, 8];
  const cycleOvertime = (date) => {
    const key = ymd(date);
    const cur = (cap.overtime && cap.overtime[key]) || 0;
    const i = OT_STEPS.indexOf(cur);
    const nxt = OT_STEPS[(i + 1) % OT_STEPS.length];
    setCap(c => {
      const o = { ...(c.overtime || {}) };
      if (nxt > 0) o[key] = nxt; else delete o[key];
      return { ...c, overtime: o };
    });
  };
  const applyOvertime = () => {
    if (!ot.von || !ot.bis) return;
    const a = new Date(ot.von), b = new Date(ot.bis);
    if (b < a) return;
    setCap(c => {
      const o = { ...(c.overtime || {}) };
      for (let d = new Date(a); d <= b; d.setDate(d.getDate() + 1)) { if (ot.hours > 0) o[ymd(d)] = ot.hours; else delete o[ymd(d)]; }
      return { ...c, overtime: o };
    });
  };
  const clearOvertime = () => setCap(c => ({ ...c, overtime: {} }));
  const overtimeTotal = Object.values(cap.overtime || {}).reduce((a, b) => a + b, 0);

  // Wochen aufbauen
  const start = startMonday();
  const weeks = [];
  for (let w = 0; w < 53; w++) {
    const col = [];
    for (let r = 0; r < 7; r++) {
      const d = new Date(start); d.setDate(start.getDate() + w * 7 + r);
      col.push(d);
    }
    weeks.push(col);
  }
  // Monatslabels: wo der Monat in der Mo-Zeile wechselt
  const monthLabels = weeks.map((col, w) => {
    const m = col[0].getMonth();
    const prev = w > 0 ? weeks[w - 1][0].getMonth() : -1;
    return m !== prev ? MONTHS[m] : null;
  });

  const CELL = 13, GAP = 2.5;

  return (
    <div>
      {/* Arbeitssystem-Auswahl */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 16, paddingBottom: 14, borderBottom: '1px solid var(--line)' }}>
        <L>Arbeitssystem</L>
        <select value={sys} onChange={e => setSys(e.target.value)}
          style={{ fontFamily: 'var(--sans)', fontSize: 13, fontWeight: 500, padding: '7px 10px', borderRadius: 6, border: '1px solid var(--line-2)', background: 'var(--surface)', color: 'var(--ink)', minWidth: 240 }}>
          {window.WORK_SYSTEMS.map(w => <option key={w.id} value={w.id}>{w.name}</option>)}
        </select>
        <span className="num" style={{ fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--muted)' }}>{sys}</span>
        <span style={{ fontSize: 11.5, color: 'var(--muted)', marginLeft: 'auto' }}>Kapazität wird je Arbeitssystem gepflegt</span>
      </div>

      {/* Parameter */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))', gap: 14, marginBottom: 18 }}>
        <Param label="Schichten / Tag">
          <Seg options={[1, 2, 3]} value={cap.shiftsPerDay} onChange={v => setCap(c => ({ ...c, shiftsPerDay: v }))} />
        </Param>
        <Param label="Stunden / Schicht">
          <NumBox value={cap.hoursPerShift || 8} min={1} max={12} step={0.5} suffix="h" onChange={v => setCap(c => ({ ...c, hoursPerShift: v }))} />
        </Param>
        <Param label="Anzahl Systeme">
          <NumBox value={cap.systems} min={1} max={12} suffix="" onChange={v => setCap(c => ({ ...c, systems: v }))} />
        </Param>
        <Param label="Pausen / Schicht">
          <NumBox value={cap.breaks} min={0} max={120} step={5} suffix="min" onChange={v => setCap(c => ({ ...c, breaks: v }))} />
        </Param>
        <Param label="OEE">
          <NumBox value={cap.oee} min={1} max={100} suffix="%" onChange={v => setCap(c => ({ ...c, oee: v }))} />
        </Param>
      </div>

      {/* Betriebstage */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 16, flexWrap: 'wrap' }}>
        <L>Betriebstage</L>
        <div style={{ display: 'flex', gap: 4 }}>
          {ROW_ORDER.map(w => (
            <button key={w} onClick={() => setCap(c => ({ ...c, weekdays: { ...c.weekdays, [w]: !c.weekdays[w] } }))}
              style={{ width: 38, height: 30, borderRadius: 6, border: '1px solid', fontFamily: 'var(--mono)', fontSize: 11.5, fontWeight: 600,
                borderColor: cap.weekdays[w] ? 'var(--accent-ink)' : 'var(--line-2)',
                background: cap.weekdays[w] ? 'var(--accent)' : 'var(--surface)',
                color: cap.weekdays[w] ? 'var(--accent-ink)' : 'var(--muted)' }}>{w}</button>
          ))}
        </div>
        <span style={{ fontSize: 11.5, color: 'var(--muted)' }}>z.B. Sa/So abschalten</span>
      </div>

      {/* Kennzahlen */}
      <div style={{ display: 'flex', gap: 26, padding: '12px 16px', background: 'var(--surface-2)', border: '1px solid var(--line)', borderRadius: 8, marginBottom: 18, flexWrap: 'wrap' }}>
        <Kpi label="Netto-Schicht" val={netShiftH.toFixed(2).replace('.', ',') + ' h'} />
        <Kpi label="Regelkapazität / Tag" val={regelTag.toFixed(1).replace('.', ',') + ' h'} accent />
        <Kpi label="Betriebstage / Woche" val={operatingDays} />
        <Kpi label="Regelkapazität / Woche" val={regelWoche.toFixed(1).replace('.', ',') + ' h'} accent />
        {overtimeTotal > 0 && <Kpi label="Überstunden geplant" val={'+' + overtimeTotal + ' h'} />}
      </div>

      {/* Zeitraum-Werkzeug */}
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 12, marginBottom: 18, flexWrap: 'wrap' }}>
        <div>
          <L>Zeitraum von</L>
          <div><input type="date" value={range.von} onChange={e => setRange(r => ({ ...r, von: e.target.value }))} style={dateInput} /></div>
        </div>
        <div>
          <L>bis</L>
          <div><input type="date" value={range.bis} onChange={e => setRange(r => ({ ...r, bis: e.target.value }))} style={dateInput} /></div>
        </div>
        <div>
          <L>Schichten</L>
          <div style={{ marginTop: 4 }}><Seg options={[0, 1, 2, 3]} value={range.shifts} onChange={v => setRange(r => ({ ...r, shifts: v }))} /></div>
        </div>
        <button onClick={applyRange} style={{ height: 32, padding: '0 14px', borderRadius: 6, border: '1px solid var(--accent-ink)', background: 'var(--accent-ink)', color: '#fff', fontSize: 12.5, fontWeight: 500 }}>Zeitraum anwenden</button>
        <button onClick={clearOverrides} style={{ height: 32, padding: '0 12px', borderRadius: 6, border: '1px solid var(--line-2)', background: 'var(--surface)', color: 'var(--ink-2)', fontSize: 12.5 }}>Ausnahmen zurücksetzen</button>
      </div>

      {/* Überstunden-Werkzeug */}
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 12, marginBottom: 18, flexWrap: 'wrap', padding: '12px 14px', background: 'color-mix(in oklch, var(--good) 22%, var(--surface-2))', border: '1px solid color-mix(in oklch, var(--good) 50%, var(--line))', borderRadius: 8 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginRight: 4 }}>
          <Icon name="clock" size={15} style={{ color: 'var(--good-ink)' }} />
          <span style={{ fontWeight: 600, fontSize: 13 }}>Überstunden</span>
        </div>
        <div>
          <L>von</L>
          <div><input type="date" value={ot.von} onChange={e => setOt(r => ({ ...r, von: e.target.value }))} style={dateInput} /></div>
        </div>
        <div>
          <L>bis</L>
          <div><input type="date" value={ot.bis} onChange={e => setOt(r => ({ ...r, bis: e.target.value }))} style={dateInput} /></div>
        </div>
        <div>
          <L>Zusatzstunden / Tag</L>
          <div style={{ marginTop: 4 }}><NumBox value={ot.hours} min={0} max={16} step={1} suffix="h" onChange={v => setOt(r => ({ ...r, hours: v }))} /></div>
        </div>
        <button onClick={applyOvertime} style={{ height: 32, padding: '0 14px', borderRadius: 6, border: '1px solid var(--good-ink)', background: 'var(--good-ink)', color: '#fff', fontSize: 12.5, fontWeight: 500 }}>Überstunden anwenden</button>
        <button onClick={clearOvertime} style={{ height: 32, padding: '0 12px', borderRadius: 6, border: '1px solid var(--line-2)', background: 'var(--surface)', color: 'var(--ink-2)', fontSize: 12.5 }}>zurücksetzen</button>
        <span style={{ fontSize: 11, color: 'var(--muted)', marginLeft: 'auto' }}>oder Rechtsklick auf einen Tag (+2/+4/+8 h)</span>
      </div>

      {/* Jahres-Heatmap */}
      <div style={{ marginBottom: 6 }}>
        <L>Rollierendes Jahr · Klick auf einen Tag schaltet 0→1→2→3 Schichten</L>
      </div>
      <div style={{ overflowX: 'auto', paddingBottom: 6 }}>
        <div style={{ display: 'inline-block' }}>
          {/* Monatslabels */}
          <div style={{ display: 'flex', marginLeft: 26, height: 14 }}>
            {monthLabels.map((m, w) => (
              <div key={w} style={{ width: CELL + GAP, position: 'relative' }}>
                {m && <span style={{ position: 'absolute', left: 0, fontSize: 10, color: 'var(--muted)', fontFamily: 'var(--mono)', whiteSpace: 'nowrap' }}>{m}</span>}
              </div>
            ))}
          </div>
          <div style={{ display: 'flex' }}>
            {/* Wochentag-Labels */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: GAP, marginRight: 4, width: 22 }}>
              {ROW_ORDER.map(w => <div key={w} style={{ height: CELL, fontSize: 9, color: 'var(--muted)', fontFamily: 'var(--mono)', lineHeight: CELL + 'px' }}>{w}</div>)}
            </div>
            {/* Wochen */}
            <div style={{ display: 'flex', gap: GAP }}>
              {weeks.map((col, w) => (
                <div key={w} style={{ display: 'flex', flexDirection: 'column', gap: GAP }}>
                  {col.map((d, r) => {
                    const s = dayShifts(d);
                    const isOverride = cap.overrides[ymd(d)] != null;
                    const otH = dayOvertime(d);
                    return (
                      <div key={r} onClick={() => cycle(d)} onContextMenu={(e) => { e.preventDefault(); cycleOvertime(d); }}
                        title={`${WD_NAMES[d.getDay()]} ${ymd(d)} · ${s} Schicht${s !== 1 ? 'en' : ''} · ${dayHours(s).toFixed(1)} h${otH ? ` · +${otH} h Überstunden` : ''}`}
                        style={{ position: 'relative', width: CELL, height: CELL, borderRadius: 2.5, cursor: 'pointer',
                          background: SHIFT_COLORS[s],
                          border: '1px solid ' + SHIFT_BORDERS[s],
                          boxShadow: isOverride ? 'inset 0 0 0 1.5px var(--warn-ink)' : 'none' }}>
                        {otH > 0 && <span style={{ position: 'absolute', top: -1, right: -1, width: 0, height: 0, borderStyle: 'solid', borderWidth: '0 5px 5px 0', borderColor: `transparent var(--good-ink) transparent transparent` }} />}
                      </div>
                    );
                  })}
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>

      {/* Legende */}
      <div style={{ display: 'flex', gap: 16, alignItems: 'center', marginTop: 12, flexWrap: 'wrap' }}>
        {[0, 1, 2, 3].map(s => (
          <span key={s} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, color: 'var(--muted)' }}>
            <span style={{ width: 12, height: 12, borderRadius: 2.5, background: SHIFT_COLORS[s], border: '1px solid ' + SHIFT_BORDERS[s] }} />
            {s === 0 ? 'geschlossen' : s + ' Schicht' + (s > 1 ? 'en' : '')}
          </span>
        ))}
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, color: 'var(--muted)' }}>
          <span style={{ width: 12, height: 12, borderRadius: 2.5, background: 'var(--surface)', boxShadow: 'inset 0 0 0 1.5px var(--warn-ink)' }} /> manuelle Ausnahme
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, color: 'var(--muted)' }}>
          <span style={{ position: 'relative', width: 12, height: 12, borderRadius: 2.5, background: 'var(--surface-2)', border: '1px solid var(--line-2)' }}><span style={{ position: 'absolute', top: -1, right: -1, width: 0, height: 0, borderStyle: 'solid', borderWidth: '0 5px 5px 0', borderColor: 'transparent var(--good-ink) transparent transparent' }} /></span> Überstunden
        </span>
      </div>
    </div>
  );
}

function Param({ label, children }) {
  return (
    <div>
      <L>{label}</L>
      <div style={{ marginTop: 5 }}>{children}</div>
    </div>
  );
}
function Kpi({ label, val, accent }) {
  return (
    <div>
      <div className="num" style={{ fontFamily: 'var(--mono)', fontSize: 17, fontWeight: 600, color: accent ? 'var(--accent-ink)' : 'var(--ink)' }}>{val}</div>
      <div className="label" style={{ fontSize: 9, marginTop: 2 }}>{label}</div>
    </div>
  );
}
function Seg({ options, value, onChange }) {
  return (
    <div style={{ display: 'inline-flex', border: '1px solid var(--line-2)', borderRadius: 6, overflow: 'hidden' }}>
      {options.map((o, i) => (
        <button key={o} onClick={() => onChange(o)}
          style={{ width: 36, height: 30, border: 'none', borderLeft: i ? '1px solid var(--line-2)' : 'none',
            background: value === o ? 'var(--accent)' : 'var(--surface)', color: value === o ? 'var(--accent-ink)' : 'var(--ink-2)',
            fontFamily: 'var(--mono)', fontSize: 12.5, fontWeight: 600 }}>{o}</button>
      ))}
    </div>
  );
}
function NumBox({ value, min, max, step = 1, suffix, onChange }) {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
      <input type="number" value={value} min={min} max={max} step={step}
        onChange={e => { let v = +e.target.value; if (isNaN(v)) v = min; v = Math.max(min, Math.min(max, v)); onChange(v); }}
        style={{ width: 66, height: 30, textAlign: 'center', fontFamily: 'var(--mono)', fontSize: 13, border: '1px solid var(--line-2)', borderRadius: 6, background: 'var(--surface)', color: 'var(--ink)' }} />
      {suffix && <span style={{ fontSize: 11.5, color: 'var(--muted)' }}>{suffix}</span>}
    </span>
  );
}
const dateInput = { height: 32, marginTop: 4, padding: '0 8px', fontFamily: 'var(--mono)', fontSize: 12.5, border: '1px solid var(--line-2)', borderRadius: 6, background: 'var(--surface)', color: 'var(--ink)' };

Object.assign(window, { CapacityPlanner, defaultCapacity, buildCapDayHours, buildOpenShifts, TransitionTimes });

// ===== Übergangszeiten & Auftragsreichweite je Arbeitssystem =====
function netDayCapacity(cap) {
  const netShiftH = Math.max(0, ((cap.hoursPerShift || 8) * 60 - cap.breaks) / 60);
  return cap.systems * cap.shiftsPerDay * netShiftH * cap.oee / 100;
}
function TransitionTimes({ transitionBySystem, setTransition, capBySystem, currentSystem }) {
  const fmt1 = (x) => x.toFixed(1).replace('.', ',');
  return (
    <div>
      <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13, maxWidth: 920 }}>
        <thead>
          <tr>
            {['Arbeitssystem', 'Min-Übergangszeit', 'Aktuelle Reichweite', 'Ø Reichweite (4 Wo.)', 'Trend'].map((h, i) => (
              <th key={i} style={{ ...window.thStyle, textAlign: i === 0 ? 'left' : i === 4 ? 'center' : 'right', padding: '8px 14px' }}>{h}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {window.WORK_SYSTEMS.map(w => {
            const cap = capBySystem[w.id];
            const netDay = netDayCapacity(cap) || 1;
            const active = w.id === currentSystem;
            const backlogH = active ? window.liveBacklogH(w.id) : (window.STATIC_BACKLOG_H[w.id] || 0);
            const reachDays = backlogH / netDay;
            const hist = window.REACH_HISTORY[w.id] || [reachDays];
            const avg = hist.reduce((a, b) => a + b, 0) / hist.length;
            const delta = reachDays - avg;
            const trendUp = delta > 0.15, trendDn = delta < -0.15;
            const tone = Math.abs(delta) <= 0.15 ? 'var(--muted)' : trendUp ? 'var(--bad-ink)' : 'var(--good-ink)';
            return (
              <tr key={w.id} style={{ borderBottom: '1px solid var(--line)', background: active ? 'color-mix(in oklch, var(--accent) 14%, transparent)' : 'transparent' }}>
                <td style={{ padding: '11px 14px' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ fontWeight: 600 }}>{w.name}</span>
                    {active && <span style={{ fontSize: 9.5, fontFamily: 'var(--mono)', textTransform: 'uppercase', letterSpacing: '.08em', color: 'var(--accent-ink)', padding: '1px 6px', borderRadius: 99, background: 'var(--accent)', border: '1px solid var(--accent-line)' }}>aktiv</span>}
                  </div>
                  <div className="num" style={{ fontFamily: 'var(--mono)', fontSize: 10.5, color: 'var(--muted)', marginTop: 2 }}>{w.id} · {fmt1(netDay)} h/Tag netto</div>
                </td>
                <td style={{ padding: '11px 14px', textAlign: 'right' }}>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, justifyContent: 'flex-end' }}>
                    <input type="number" min="0" max="120" step="1" value={transitionBySystem[w.id] ?? 0}
                      onChange={e => { let v = +e.target.value; if (isNaN(v)) v = 0; v = Math.max(0, Math.min(120, v)); setTransition(w.id, v); }}
                      style={{ width: 72, height: 32, textAlign: 'center', fontFamily: 'var(--mono)', fontSize: 13.5, fontWeight: 600, border: '1px solid var(--line-2)', borderRadius: 6, background: 'var(--surface)', color: 'var(--ink)' }} />
                    <span style={{ fontSize: 11.5, color: 'var(--muted)', width: 46, textAlign: 'left' }}>h<br /><span style={{ fontSize: 9.5 }}>≈ {fmt1((transitionBySystem[w.id] ?? 0) / window.SHIFT_HOURS)} Sch.</span></span>
                  </span>
                </td>
                <td style={{ padding: '11px 14px', textAlign: 'right' }}>
                  <div className="num" style={{ fontFamily: 'var(--mono)', fontSize: 15, fontWeight: 600, color: 'var(--ink)' }}>{fmt1(reachDays)} Tage</div>
                  <div className="num" style={{ fontFamily: 'var(--mono)', fontSize: 10.5, color: 'var(--muted)', marginTop: 1 }}>{Math.round(backlogH)} h Bestand{active ? ' · live' : ''}</div>
                </td>
                <td style={{ padding: '11px 14px', textAlign: 'right' }}>
                  <div className="num" style={{ fontFamily: 'var(--mono)', fontSize: 14, color: 'var(--ink-2)' }}>{fmt1(avg)} Tage</div>
                  <div style={{ display: 'flex', gap: 2, justifyContent: 'flex-end', marginTop: 4 }}>
                    {hist.map((h, i) => (
                      <span key={i} title={`KW-${hist.length - i}: ${fmt1(h)} Tage`} style={{ width: 6, borderRadius: 1, background: i === hist.length - 1 ? 'var(--accent-ink)' : 'var(--line-2)', height: Math.max(4, h / Math.max(...hist) * 22) }} />
                    ))}
                  </div>
                </td>
                <td style={{ padding: '11px 14px', textAlign: 'center' }}>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontFamily: 'var(--mono)', fontSize: 12, fontWeight: 600, color: tone }}>
                    <span style={{ fontSize: 13 }}>{trendUp ? '▲' : trendDn ? '▼' : '–'}</span>
                    {delta >= 0 ? '+' : ''}{fmt1(delta)}
                  </span>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
      <div style={{ marginTop: 14, fontSize: 11.5, color: 'var(--muted)', maxWidth: 760, lineHeight: 1.55 }}>
        <b style={{ color: 'var(--ink-2)' }}>Reichweite</b> = Auftragsbestand (Arbeitsinhalt) ÷ Netto-Tageskapazität. Sie zeigt, für wie viele Tage Arbeit am System eingelastet ist. Die <b style={{ color: 'var(--ink-2)' }}>Min-Übergangszeit</b> ist die minimale Liege-/Transportzeit bis zum nächsten Arbeitssystem und fließt als Vorlaufzeit in die Zulaufprognose ein.
      </div>
    </div>
  );
}

// Offene Schichten je Tag (ganzzahlig) — für Raster, Frozen Zone, geschlossene Schichten.
function openShiftsForDay(model, d) {
  const date = window.dateFromH(d * 24);
  const key = ymd(date);
  return model.overrides[key] != null
    ? model.overrides[key]
    : (model.weekdays[WD_NAMES[date.getDay()]] ? model.shiftsPerDay : 0);
}
function buildOpenShifts(model) { return (d) => openShiftsForDay(model, d); }

// EFFEKTIVE Arbeitsstunden je Tag für den Scheduler: offene Schichten × Netto-Schichtdauer × OEE × Systeme.
// Berücksichtigt Stunden/Schicht, Pausen, OEE und parallele Systeme. Overtime ebenfalls netto/OEE-reduziert.
function buildCapDayHours(model) {
  const hps = model.hoursPerShift || 8;
  const netPerShift = Math.max(0, hps - (model.breaks || 0) / 60); // h netto je Schicht (Pausen ab)
  const eff = (model.oee || 100) / 100;
  const sys = model.systems || 1;
  return (d) => {
    const date = window.dateFromH(d * 24);
    const key = ymd(date);
    const shifts = openShiftsForDay(model, d);
    const overtime = (model.overtime && model.overtime[key]) || 0;
    return (shifts * netPerShift + overtime) * eff * sys;
  };
}
