.ldkp-wrap{max-width:1300px;margin:0 auto;font-family:Inter,Arial,sans-serif}.ldkp-card{background:#fff;border:1px solid #e5e7eb;border-radius:18px;padding:18px;box-shadow:0 8px 24px rgba(0,0,0,.04);margin-bottom:18px}.ldkp-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}.ldkp-col-3{grid-column:span 3}.ldkp-col-4{grid-column:span 4}.ldkp-col-6{grid-column:span 6}.ldkp-col-8{grid-column:span 8}.ldkp-col-12{grid-column:span 12}.ldkp-title{font-size:24px;font-weight:800;margin:0 0 6px}.ldkp-subtitle{font-size:20px;font-weight:800;margin:0 0 14px}.ldkp-muted{color:#6b7280}.ldkp-select,.ldkp-input,.ldkp-textarea{width:100%;box-sizing:border-box;padding:12px 14px;border:1px solid #d1d5db;border-radius:12px;background:#fff;font-size:16px;line-height:1.4;color:#111827}.ldkp-select{min-height:48px;font-weight:600}.ldkp-textarea{min-height:120px}.ldkp-btn{display:inline-flex;align-items:center;justify-content:center;padding:10px 14px;border-radius:12px;background:#111827;color:#fff;text-decoration:none;border:0;cursor:pointer;font-weight:600}.ldkp-btn.light{background:#f3f4f6;color:#111827;border:1px solid #d1d5db}.ldkp-btn.green{background:#15803d}.ldkp-btn.red{background:#b91c1c}.ldkp-row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}.ldkp-thread{display:flex;flex-direction:column;gap:10px}.ldkp-bubble{padding:12px 14px;border-radius:14px;border:1px solid #e5e7eb;max-width:100%}.ldkp-bubble.employee{background:#f9fafb}.ldkp-bubble.manager{background:#eef6ff;border-color:#bfdbfe}.ldkp-pill{display:inline-block;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:700}.ldkp-pill.open{background:#fef3c7;color:#92400e}.ldkp-pill.approved{background:#dcfce7;color:#166534}.ldkp-pill.rejected{background:#fee2e2;color:#991b1b}.ldkp-pill.work{background:#ecfdf5;color:#166534}.ldkp-pill.free{background:#fef3c7;color:#92400e}.ldkp-nav{display:flex;gap:10px;align-items:center;justify-content:space-between;flex-wrap:wrap}.ldkp-main-menu{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-top:14px}.ldkp-main-menu .ldkp-btn{min-height:46px}.ldkp-main-menu .ldkp-btn.light{background:#f8fafc}.ldkp-main-menu .ldkp-btn.light.active{background:#111827;color:#fff;border-color:#111827}.ldkp-main-menu .ldkp-btn.light.has-count{position:relative}.ldkp-section{display:none}.ldkp-section.active{display:block}.ldkp-kpi{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.ldkp-kpi-item{background:#f9fafb;border:1px solid #e5e7eb;border-radius:14px;padding:12px}.ldkp-kpi-value{font-size:24px;font-weight:800}.ldkp-table-wrap{overflow:auto}.ldkp-table{width:100%;border-collapse:separate;border-spacing:0;border:1px solid #e5e7eb;border-radius:14px;background:#fff;overflow:hidden}.ldkp-table th,.ldkp-table td{padding:10px 12px;text-align:left;border-bottom:1px solid #edf0f2;vertical-align:top}.ldkp-table th{background:#f9fafb;font-size:14px}.ldkp-table tr:last-child td{border-bottom:none}.ldkp-mobile-item{display:none}.ldkp-status{padding:12px 14px;border-radius:12px;background:#f9fafb;border:1px solid #e5e7eb;margin:14px 0}.ldkp-success{background:#ecfdf5;border-color:#86efac}.ldkp-danger{background:#fef2f2;border-color:#fca5a5}.ldkp-tag{display:inline-flex;align-items:center;background:#eef2ff;color:#3730a3;border-radius:999px;padding:4px 10px;font-size:12px;font-weight:700;margin-left:6px}.ldkp-userline{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.ldkp-userline strong{margin-right:2px}.ldkp-noti-chip{display:inline-flex;align-items:center;gap:8px;background:#111827;color:#fff;border-radius:999px;padding:8px 12px;font-size:13px;font-weight:700;cursor:pointer;border:0}.ldkp-noti-count{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;border-radius:999px;background:#ef4444;color:#fff;font-size:12px;padding:0 6px}.ldkp-notice{border:1px solid #e5e7eb;border-radius:14px;padding:14px;margin-top:12px}.ldkp-notice.unread{border-color:#f59e0b;background:#fffbeb}.ldkp-day{margin-bottom:22px}.ldkp-date{font-size:24px;font-weight:800;margin:0 0 12px}.ldkp-area{font-size:18px;font-weight:800;margin:16px 0 8px}.ldkp-today-box{padding:16px;border-radius:16px;background:linear-gradient(135deg,#f8fafc,#eef2ff);border:1px solid #dbeafe}.ldkp-actions-col{display:flex;gap:8px;flex-wrap:wrap}.ldkp-hidden{display:none}.ldkp-admin-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}.ldkp-toolbar{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-bottom:12px}.ldkp-editor-table input,.ldkp-editor-table select{width:100%;box-sizing:border-box;padding:8px 9px;border:1px solid #d1d5db;border-radius:10px;background:#fff}.ldkp-small{font-size:12px;color:#6b7280}.ldkp-print-zone.print-all .ldkp-section{display:block !important}.ldkp-filter-count{font-size:13px;color:#6b7280}.ldkp-login{max-width:520px}.ldkp-sticky-tools{position:sticky;top:20px}.ldkp-split{display:grid;grid-template-columns:1fr 1fr;gap:18px}.ldkp-inline-label{display:block;font-weight:700;margin-bottom:6px}.ldkp-empty{padding:14px;border-radius:12px;background:#fff8e5;border:1px solid #ead9a3}.ldkp-month-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.ldkp-month-card{border:1px solid #e5e7eb;border-radius:14px;padding:12px}.ldkp-day-header{display:flex;justify-content:space-between;align-items:center;gap:10px}.ldkp-filterbar{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-bottom:14px}.ldkp-head-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}.ldkp-area-group{margin-top:16px}.ldkp-area-head{font-size:13px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:#6b7280;margin:0 0 8px}.ldkp-line-table{border:1px solid #e5e7eb;border-radius:14px;background:#fff;overflow:hidden}.ldkp-line-row{display:grid;grid-template-columns:2fr 1fr 1fr 1.2fr;gap:12px;padding:11px 14px;border-top:1px solid #edf0f2;align-items:center}.ldkp-line-row:first-child{border-top:0}.ldkp-line-head{background:#f9fafb;font-size:12px;font-weight:800;color:#6b7280;text-transform:uppercase}.ldkp-line-row.replacement{grid-template-columns:1.6fr 1fr 1.2fr 1.6fr;background:#fff}.ldkp-manage-table input,.ldkp-manage-table select,.ldkp-manage-table .ldkp-btn{width:100%;box-sizing:border-box}.ldkp-manage-table .ldkp-actions-col{min-width:210px}.ldkp-employee-create{margin-top:16px;padding:22px;border:1px solid #dbe4f0;border-radius:22px;background:linear-gradient(180deg,#ffffff,#f8fbff);box-shadow:0 12px 30px rgba(15,23,42,.05)}.ldkp-employee-create-head{display:flex;justify-content:space-between;gap:16px;align-items:flex-start;flex-wrap:wrap;margin-bottom:18px}.ldkp-employee-create-title{font-size:20px;font-weight:800;margin:0 0 4px}.ldkp-employee-create-sub{color:#6b7280;max-width:760px}.ldkp-employee-create .ldkp-btn{min-height:52px;padding-inline:22px}.ldkp-employee-create .ldkp-inline-label{font-size:13px;text-transform:uppercase;letter-spacing:.04em;color:#475569}.ldkp-field-hint{display:block;margin-top:6px;font-size:12px;color:#6b7280}.ldkp-section-chip{display:inline-flex;align-items:center;padding:7px 12px;border-radius:999px;background:#eef2ff;color:#3730a3;font-size:12px;font-weight:800}.ldkp-employee-divider{height:1px;background:#e5e7eb;margin:6px 0 2px}.ldkp-employee-table td{vertical-align:top}.ldkp-employee-name{font-weight:800;font-size:18px;line-height:1.35}.ldkp-employee-meta{display:flex;flex-direction:column;gap:4px}.ldkp-contact-stack,.ldkp-area-stack{display:flex;flex-direction:column;gap:6px}.ldkp-label-small{font-size:12px;font-weight:800;color:#6b7280;text-transform:uppercase;letter-spacing:.04em}.ldkp-edit-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;min-width:420px}.ldkp-edit-grid .ldkp-span-2{grid-column:span 2}.ldkp-edit-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.ldkp-mobile-employee-list{display:none}.ldkp-employee-mobile-card{border:1px solid #e5e7eb;border-radius:16px;padding:14px;background:#fff;margin-top:12px}.ldkp-employee-mobile-top{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;margin-bottom:12px}.ldkp-employee-mobile-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}.ldkp-employee-mobile-grid .ldkp-span-2{grid-column:span 2}.ldkp-checkline{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:600}.ldkp-checkline input{width:auto}.ldkp-inline-stack{display:flex;flex-direction:column;gap:4px}.ldkp-employee-filterbar{margin-top:16px;padding:16px;border:1px solid #e5e7eb;border-radius:18px;background:#f8fafc}.ldkp-employee-list{display:flex;flex-direction:column;gap:14px;margin-top:16px}.ldkp-employee-manage-card{border:1px solid #e5e7eb;border-radius:18px;background:#fff;padding:18px;box-shadow:0 6px 18px rgba(15,23,42,.04)}.ldkp-employee-manage-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;flex-wrap:wrap;margin-bottom:14px}.ldkp-employee-manage-badges{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.ldkp-soft-pill{display:inline-flex;align-items:center;padding:7px 12px;border-radius:999px;background:#f3f4f6;color:#111827;font-size:12px;font-weight:800}.ldkp-employee-info-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-bottom:14px}.ldkp-info-box{border:1px solid #e5e7eb;border-radius:14px;padding:12px;background:#f9fafb}.ldkp-info-value{font-weight:600;color:#111827}.ldkp-employee-form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.ldkp-employee-form-grid .ldkp-span-2{grid-column:span 2}.ldkp-employee-delete-form{margin:0}.ldkp-employee-actions-row{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-top:2px}.ldkp-emergency-board{display:flex;flex-direction:column;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:16px;background:linear-gradient(90deg,#fff1f2,#fff7ed);border:1px solid #fecaca;margin:0 0 14px}.ldkp-emergency-label{display:block;width:100%;font-weight:800;color:#991b1b;white-space:nowrap}.ldkp-emergency-item-wrap{display:block;width:100%;padding:10px 12px;border-radius:12px;background:#fff;border:1px solid #fecaca}.ldkp-emergency-message{display:block;font-weight:800;color:#991b1b;line-height:1.45}.ldkp-emergency-meta{display:block;margin-top:6px;font-weight:600;color:#9a3412;font-size:13px;line-height:1.4}.ldkp-approvals-table .ldkp-actions-col{display:grid;grid-template-columns:1fr;gap:8px;min-width:130px}.ldkp-approvals-table .ldkp-actions-col form{margin:0}.ldkp-approvals-table td{vertical-align:top}.ldkp-doc-chip{display:inline-flex;align-items:center;justify-content:center;min-width:110px;padding:10px 12px;border-radius:14px;border:1px solid #d1d5db;background:#f8fafc;font-weight:700;line-height:1.5;text-decoration:none;color:#111827}.ldkp-doc-chip:hover{background:#eef2ff}.ldkp-approval-type{font-weight:800}.ldkp-approval-sub{margin-top:4px;color:#6b7280;font-size:13px}.ldkp-summary-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-bottom:14px}.ldkp-summary-card{border:1px solid #e5e7eb;border-radius:16px;background:#fff;padding:14px}.ldkp-summary-card .ldkp-small{display:block;margin-bottom:6px}.ldkp-summary-card strong{font-size:22px}.ldkp-sick-overview{display:grid;grid-template-columns:1.1fr 1.9fr;gap:16px;margin-top:14px}.ldkp-sick-stat-card{border:1px solid #e5e7eb;border-radius:18px;background:#fff;padding:16px}.ldkp-sick-stat-total{font-size:28px;font-weight:800}.ldkp-sick-days-table .ldkp-table th,.ldkp-sick-days-table .ldkp-table td{font-size:14px}.ldkp-replacement-assigned{padding:10px 12px;border-radius:12px;background:#ecfdf5;border:1px solid #86efac;font-weight:700;color:#166534}.ldkp-requested-note{margin-top:10px}.ldkp-hidden-inline{display:none!important} @media (max-width:760px){.ldkp-line-row,.ldkp-line-row.replacement{grid-template-columns:1fr 1fr}.ldkp-line-head{display:none}.ldkp-userline{align-items:flex-start}.ldkp-noti-chip{width:100%;justify-content:center}.ldkp-manage-table thead{display:none}.ldkp-manage-table,.ldkp-manage-table tbody,.ldkp-manage-table tr,.ldkp-manage-table td{display:block;width:100%}.ldkp-manage-table tr{border-top:1px solid #edf0f2;padding:12px 0}.ldkp-manage-table tr:first-child{border-top:0}.ldkp-manage-table td{border:0!important;padding:6px 0!important}.ldkp-manage-table td:before{content:attr(data-label);display:block;font-size:12px;font-weight:800;color:#6b7280;text-transform:uppercase;margin-bottom:4px}.ldkp-manage-table .ldkp-actions-col{display:grid;grid-template-columns:1fr 1fr;gap:8px}.ldkp-manage-table .ldkp-actions-col .ldkp-btn{min-height:44px}.ldkp-edit-grid{grid-template-columns:1fr;min-width:0}.ldkp-edit-grid .ldkp-span-2{grid-column:span 1}.ldkp-mobile-employee-list{display:block}.ldkp-employee-table-wrap{display:none}}@media (max-width:1100px){.ldkp-admin-grid,.ldkp-split,.ldkp-toolbar,.ldkp-filterbar,.ldkp-month-grid{grid-template-columns:1fr 1fr}.ldkp-col-3,.ldkp-col-4,.ldkp-col-6,.ldkp-col-8,.ldkp-col-12{grid-column:span 12}}@media (max-width:760px){.ldkp-admin-grid,.ldkp-split,.ldkp-toolbar,.ldkp-filterbar,.ldkp-month-grid,.ldkp-kpi,.ldkp-summary-grid,.ldkp-sick-overview{grid-template-columns:1fr}.ldkp-card{padding:14px;border-radius:14px}.ldkp-table-wrap{display:none}.ldkp-mobile-item{display:block;border:1px solid #e5e7eb;border-radius:14px;padding:12px;margin-bottom:10px;background:#fff}.ldkp-date{font-size:22px}.ldkp-employee-create{padding:14px}.ldkp-employee-mobile-top,.ldkp-edit-actions{flex-direction:column;align-items:stretch}.ldkp-employee-mobile-grid{grid-template-columns:1fr}.ldkp-employee-mobile-grid .ldkp-span-2{grid-column:span 1}.ldkp-approvals-table .ldkp-actions-col{grid-template-columns:1fr 1fr}.ldkp-main-menu{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;align-items:stretch}.ldkp-main-menu .ldkp-btn{width:100%;min-height:52px;padding:10px 8px;font-size:13px;line-height:1.2;text-align:center}.ldkp-emergency-board{padding:10px 12px;gap:8px}.ldkp-emergency-label,.ldkp-emergency-message,.ldkp-emergency-meta{font-size:13px}.ldkp-emergency-item-wrap{padding:9px 10px}}';
    }

    private function js() {
        return <<<'JS'
document.addEventListener('DOMContentLoaded', function(){
  document.addEventListener('click', function(e){
    var btn = e.target.closest('[data-request-subtab-open]');
    if(!btn) return;
    var wrap = btn.closest('[data-section="request_form"]') || document;
    var target = btn.getAttribute('data-request-subtab-open');
    wrap.querySelectorAll('[data-request-subtab-open]').forEach(function(b){ b.classList.toggle('active', b===btn); });
    wrap.querySelectorAll('[data-request-subtab]').forEach(function(p){ p.classList.toggle('ldkp-hidden', p.getAttribute('data-request-subtab') !== target); });
  });
  var ajax = window.LDKP_AJAX || null;

  function showSection(val){
    document.querySelectorAll('.ldkp-section').forEach(function(s){
      s.classList.toggle('active', s.getAttribute('data-section') === val);
    });
    var notifier = document.querySelector('[data-open-notifications]');
    if(notifier){
      notifier.classList.toggle('is-active', val === 'notifications');
    }
    document.querySelectorAll('[data-open-section]').forEach(function(btn){
      btn.classList.toggle('active', btn.getAttribute('data-open-section') === val);
    });
  }

  function markNoticeRead(id){
    if(!ajax || !id) return Promise.resolve();
    var body = new URLSearchParams();
    body.append('action','ldk_portal_mark_notice_read');
    body.append('nonce',ajax.nonce);
    body.append('notice_id',id);
    return fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
      .then(function(r){ return r.json(); })
      .catch(function(){ return null; });
  }

  var menu = document.getElementById('ldkp-menu');
  if(menu){
    showSection(menu.value);
    menu.addEventListener('change', function(){ showSection(menu.value); });
  } else {
    showSection('today');
  }

  document.querySelectorAll('[data-open-notifications]').forEach(function(btn){
    btn.addEventListener('click', function(){
      if(menu){
        menu.value = 'notifications';
      }
      showSection('notifications');
      var targetNav = document.querySelector('.ldkp-main-menu') || document.querySelector('.ldkp-status');
      if(targetNav){
        try { targetNav.scrollIntoView({behavior:'smooth', block:'center'}); } catch(e) {}
      }
    });
  });

  document.querySelectorAll('[data-open-section]').forEach(function(btn){
    btn.addEventListener('click', function(){
      var section = btn.getAttribute('data-open-section');
      if(section){
        if(menu){
          menu.value = section;
        }
        showSection(section);
        var targetNav = document.querySelector('.ldkp-main-menu') || document.querySelector('.ldkp-status');
        if(targetNav){
          try { targetNav.scrollIntoView({behavior:'smooth', block:'center'}); } catch(e) {}
        }
      }
    });
  });

  function showSickTab(target){
    document.querySelectorAll('[data-sick-tab]').forEach(function(box){
      box.classList.toggle('ldkp-hidden', box.getAttribute('data-sick-tab') !== target);
    });
    document.querySelectorAll('[data-sick-tab-target]').forEach(function(btn){
      var active = btn.getAttribute('data-sick-tab-target') === target;
      btn.classList.toggle('light', !active);
      btn.classList.toggle('red', active);
    });
  }
  document.querySelectorAll('[data-sick-tab-target]').forEach(function(btn){
    btn.addEventListener('click', function(){
      showSickTab(btn.getAttribute('data-sick-tab-target'));
    });
  });
  if(document.querySelector('[data-sick-tab]')){
    showSickTab('form');
  }

  var type = document.getElementById('ldkp-request-type');
  var vacation = document.getElementById('ldkp-vacation-box');
  function syncVac(){
    if(type && vacation){
      vacation.classList.toggle('ldkp-hidden', type.value !== 'urlaub');
    }
  }
  if(type){
    syncVac();
    type.addEventListener('change', syncVac);
  }

  function downloadBase64(filename, base64){
    var a = document.createElement('a');
    a.href = 'data:text/csv;base64,' + base64;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    a.remove();
  }

  function setNoticeCounter(count){
    document.querySelectorAll('[data-notification-count]').forEach(function(el){
      el.setAttribute('data-count', String(count));
      el.textContent = String(count);
      var wrap = el.closest('[data-notification-wrap]');
      if(wrap){ wrap.classList.toggle('ldkp-hidden', count === 0); }
    });
    var menuOpt = document.querySelector('#ldkp-menu option[value="notifications"]');
    if(menuOpt){ menuOpt.textContent = count > 0 ? 'Benachrichtigungen (' + count + ')' : 'Benachrichtigungen'; }
    document.querySelectorAll('[data-open-section="notifications"]').forEach(function(btn){
      btn.textContent = count > 0 ? 'Benachrichtigungen (' + count + ')' : 'Benachrichtigungen';
    });
  }

  function bindNoticeOpenButtons(scope){
    (scope || document).querySelectorAll('[data-open-notice-target]').forEach(function(btn){
      if(btn.dataset.boundNoticeOpen === '1') return;
      btn.dataset.boundNoticeOpen = '1';
      btn.addEventListener('click', function(){
        var section = btn.getAttribute('data-open-notice-target');
        var sickTab = btn.getAttribute('data-sick-open-tab');
        var noticeId = btn.getAttribute('data-notice-id');
        if(section){
          if(menu){
            menu.value = section;
          }
          showSection(section);
          if(section === 'sick_leave' && sickTab){
            showSickTab(sickTab);
          }
          var targetNav = menu || document.querySelector('.ldkp-main-menu') || document.querySelector('.ldkp-status');
          if(targetNav){
            try { targetNav.scrollIntoView({behavior:'smooth', block:'center'}); } catch(e) {}
          }
        }
        markNoticeRead(noticeId).then(function(){
          refreshNotifications();
        syncLiveUpdates(true);
        });
      });
    });
  }

  function bindNoticeButtons(scope){
    (scope || document).querySelectorAll('[data-notice-read]').forEach(function(btn){
      if(btn.dataset.boundNotice === '1') return;
      btn.dataset.boundNotice = '1';
      btn.addEventListener('click', function(){
        var id = btn.getAttribute('data-notice-read');
        markNoticeRead(id).then(function(j){
          if(j && j.success){ refreshNotifications(); }
        });
      });
    });
    (scope || document).querySelectorAll('[data-notice-read-all]').forEach(function(btn){
      if(btn.dataset.boundNoticeAll === '1') return;
      btn.dataset.boundNoticeAll = '1';
      btn.addEventListener('click', function(){
        var body = new URLSearchParams();
        body.append('action','ldk_portal_mark_all_notices_read');
        body.append('nonce',ajax.nonce);
        fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
          .then(function(r){ return r.json(); })
          .then(function(j){
            if(j && j.success){ refreshNotifications(); }
          });
      });
    });
  }

  function refreshNotifications(){
    if(!ajax) return;
    var body = new URLSearchParams();
    body.append('action','ldk_portal_get_notifications');
    body.append('nonce',ajax.nonce);
    fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
      .then(function(r){ return r.json(); })
      .then(function(j){
        if(!j || !j.success) return;
        var count = parseInt(j.data.count || 0, 10) || 0;
        setNoticeCounter(count);
        var list = document.querySelector('[data-notices-list]');
        if(list){
          list.querySelectorAll('.ldkp-notice').forEach(function(n){ n.remove(); });
          var empty = list.querySelector('[data-notice-empty]');
          if(count === 0){ if(empty) empty.classList.remove('ldkp-hidden'); }
          else {
            if(empty) empty.classList.add('ldkp-hidden');
            list.insertAdjacentHTML('afterbegin', j.data.html || '');
            bindNoticeButtons(list);
            bindNoticeOpenButtons(list);
            bindReplacementButtons(list);
          }
        }
      });
  }


  function bindReplacementButtons(scope){
    (scope || document).querySelectorAll('[data-replacement-response]').forEach(function(btn){
      if(btn.dataset.boundReplacement === '1') return;
      btn.dataset.boundReplacement = '1';
      btn.addEventListener('click', function(){
        var body = new URLSearchParams();
        btn.disabled = true;
        body.append('action','ldk_portal_respond_replacement_request');
        body.append('nonce',ajax.nonce);
        body.append('replacement_id', btn.getAttribute('data-replacement-id'));
        body.append('response', btn.getAttribute('data-replacement-response'));
        fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
          .then(parseAjaxResponse)
          .then(function(j){
            if(!j || !j.success){ btn.disabled = false; alert((j && j.data && j.data.message) ? j.data.message : 'Antwort konnte nicht gespeichert werden.'); return; }
            if(j.data && j.data.sections){
              replacePortalSections(j.data.sections);
              rebindPortalUi();
            }
            var card = btn.closest('.ldkp-notice');
            if(card){
              card.style.opacity = '0.45';
              setTimeout(function(){
                if(card.parentNode){ card.parentNode.removeChild(card); }
                refreshNotifications();
                syncLiveUpdates(true);
              }, 180);
            } else {
              refreshNotifications();
              syncLiveUpdates(true);
            }
          })
          .catch(function(){ btn.disabled = false; alert('Antwort konnte nicht gespeichert werden.'); });
      });
    });
    (scope || document).querySelectorAll('[data-assign-replacement]').forEach(function(btn){
      if(btn.dataset.boundAssign === '1') return;
      btn.dataset.boundAssign = '1';
      btn.addEventListener('click', function(){
        var body = new URLSearchParams();
        body.append('action','ldk_portal_assign_replacement');
        body.append('nonce',ajax.nonce);
        body.append('replacement_id', btn.getAttribute('data-assign-replacement'));
        body.append('candidate_key', btn.getAttribute('data-candidate-key'));
        fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
          .then(parseAjaxResponse)
          .then(function(j){
            if(!j || !j.success){ alert((j && j.data && j.data.message) ? j.data.message : 'Ersatz konnte nicht Ã¼bernommen werden.'); return; }
            if(j.data && j.data.sections){
              replacePortalSections(j.data.sections);
              rebindPortalUi();
            }
            var row = btn.closest('[data-replacement-response-row]');
            if(row){
              row.querySelectorAll('[data-assign-replacement]').forEach(function(b){ b.remove(); });
              var badge = document.createElement('div');
              badge.className = 'ldkp-replacement-assigned';
              badge.textContent = (j.data && j.data.assigned_name) ? ('AusgewÃ¤hlt: ' + j.data.assigned_name) : 'Ersatz Ã¼bernommen';
              row.appendChild(badge);
            }
            document.querySelectorAll('[data-requested-note]').forEach(function(el){
              if(el.getAttribute('data-replacement-id') === btn.getAttribute('data-assign-replacement')){
                el.textContent = (j.data && j.data.assigned_name) ? ('Ersatz Ã¼bernommen von ' + j.data.assigned_name) : 'Ersatz Ã¼bernommen';
                el.classList.add('ldkp-success');
              }
            });
            refreshNotifications();
            syncLiveUpdates(true);
          })
          .catch(function(){ alert('Ersatz konnte nicht Ã¼bernommen werden.'); });
      });
    });
    (scope || document).querySelectorAll('[data-employee-search]').forEach(function(input){
      if(input.dataset.boundSearch === '1') return;
      input.dataset.boundSearch = '1';
      input.addEventListener('input', function(){
        var wrap = input.closest('[data-manual-request-wrap]');
        if(!wrap) return;
        var select = wrap.querySelector('[data-manual-request-select]');
        if(!select) return;
        var q = (input.value || '').toLowerCase().trim();
        Array.prototype.forEach.call(select.options, function(opt){
          if(!opt.value) return;
          var text = (opt.textContent || '').toLowerCase();
          opt.hidden = q && text.indexOf(q) === -1;
        });
      });
    });
  }


  var employeeFiltersInitialized = false;

  function initEmployeeFilters(){
    if(employeeFiltersInitialized) return;
    employeeFiltersInitialized = true;
    bindEmployeeFilters();
  }

  function bindEmployeeFilters(){
    var wrap = document.querySelector('[data-employee-filter-wrap]');
    if(!wrap) return;
    var search = wrap.querySelector('[data-employee-filter-search]');
    var role = wrap.querySelector('[data-employee-filter-role]');
    var area = wrap.querySelector('[data-employee-filter-area]');
    var reset = wrap.querySelector('[data-employee-filter-reset]');
    var count = wrap.querySelector('[data-employee-filter-count]');
    var cards = Array.prototype.slice.call(document.querySelectorAll('[data-employee-card]'));
    function apply(){
      var q = ((search && search.value) || '').toLowerCase().trim();
      var roleVal = ((role && role.value) || '').toLowerCase().trim();
      var areaVal = ((area && area.value) || '').toLowerCase().trim();
      var visible = 0;
      cards.forEach(function(card){
        var hay = (card.getAttribute('data-employee-search') || '').toLowerCase();
        var roleText = (card.getAttribute('data-employee-role') || '').toLowerCase();
        var areaText = (card.getAttribute('data-employee-area') || '').toLowerCase();
        var ok = true;
        if(q && hay.indexOf(q) === -1) ok = false;
        if(roleVal && roleText !== roleVal) ok = false;
        if(areaVal && areaText.indexOf(areaVal) === -1) ok = false;
        card.style.display = ok ? '' : 'none';
        if(ok) visible++;
      });
      if(count){
        count.textContent = visible + ' von ' + cards.length + ' Mitarbeitern sichtbar';
      }
    }
    [search, role, area].forEach(function(el){
      if(!el) return;
      el.addEventListener('input', apply);
      el.addEventListener('change', apply);
    });
    if(reset){
      reset.addEventListener('click', function(){
        if(search) search.value = '';
        if(role) role.value = '';
        if(area) area.value = '';
        apply();
      });
    }
    apply();
  }


  function updateApprovalSummary(summary){
    if(!summary) return;
    var map = {open:'Offen',approved:'Genehmigt',rejected:'Nicht genehmigt',closed:'Erledigt'};
    Object.keys(map).forEach(function(key){
      var card = document.querySelector('[data-approval-summary="'+key+'"] strong');
      if(card && typeof summary[key] !== 'undefined') card.textContent = summary[key];
    });
  }

  function bindApprovalButtons(scope){
    (scope || document).querySelectorAll('[data-request-status]').forEach(function(btn){
      if(btn.dataset.boundApproval === '1') return;
      btn.dataset.boundApproval = '1';
      btn.addEventListener('click', function(){
        var req = btn.getAttribute('data-request-id');
        var status = btn.getAttribute('data-request-status');
        var body = new URLSearchParams();
        body.append('action','ldk_portal_update_request_status');
        body.append('nonce',ajax.nonce);
        body.append('request_id',req);
        body.append('status',status);
        btn.disabled = true;
        fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
          .then(function(r){ return r.json(); })
          .then(function(j){
            btn.disabled = false;
            if(!j || !j.success){
              alert(j && j.data && j.data.message ? j.data.message : 'Status konnte nicht geÃ¤ndert werden.');
              return;
            }
            var wrap = btn.closest('[data-request-row]');
            if(wrap){
              var pill = wrap.querySelector('[data-role="status-pill"]');
              if(pill){
                pill.textContent = j.data.label;
                pill.className = 'ldkp-pill ' + j.data.status;
              }
              wrap.querySelectorAll('[data-request-status]').forEach(function(b){ b.classList.remove('is-active'); b.disabled=false; });
              btn.classList.add('is-active');
            }
            updateApprovalSummary(j.data.summary || null);
            refreshNotifications();
          })
          .catch(function(){ btn.disabled = false; alert('Status konnte nicht geÃ¤ndert werden.'); });
      });
    });
    (scope || document).querySelectorAll('[data-request-delete]').forEach(function(btn){
      if(btn.dataset.boundDelete === '1') return;
      btn.dataset.boundDelete = '1';
      btn.addEventListener('click', function(){
        if(!confirm('Antrag wirklich lÃ¶schen?')) return;
        var req = btn.getAttribute('data-request-delete');
        var body = new URLSearchParams();
        body.append('action','ldk_portal_delete_request_ajax');
        body.append('nonce',ajax.nonce);
        body.append('request_id',req);
        btn.disabled = true;
        fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
          .then(function(r){ return r.json(); })
          .then(function(j){
            if(!j || !j.success){
              btn.disabled = false;
              alert(j && j.data && j.data.message ? j.data.message : 'Eintrag konnte nicht gelÃ¶scht werden.');
              return;
            }
            var wrap = btn.closest('[data-request-row]');
            if(wrap){ wrap.remove(); }
            updateApprovalSummary(j.data.summary || null);
            refreshNotifications();
          })
          .catch(function(){ btn.disabled = false; alert('Eintrag konnte nicht gelÃ¶scht werden.'); });
      });
    });
  }

  function showAjaxStatus(target, message, isError){
    if(!target) return;
    var box = document.createElement('div');
    box.className = 'ldkp-status ' + (isError ? 'ldkp-danger' : 'ldkp-success');
    box.textContent = message || (isError ? 'Es ist ein Fehler aufgetreten.' : 'Gespeichert.');
    if(target.firstElementChild){ target.insertBefore(box, target.firstElementChild); }
    else { target.appendChild(box); }
    try { box.scrollIntoView({behavior:'smooth', block:'center'}); } catch(e) {}
  }

  function replacePortalSections(sections){
    if(!sections) return;
    Object.keys(sections).forEach(function(key){
      var section = document.querySelector('.ldkp-section[data-section="' + key + '"]');
      if(section){ section.innerHTML = sections[key] || ''; }
    });
  }

  function rebindPortalUi(){
    bindNoticeOpenButtons(document);
    bindApprovalButtons(document);
    bindNoticeButtons(document);
    bindReplacementButtons(document);
    bindEmployeeFilters();
    if(document.querySelector('[data-sick-tab]')){
      showSickTab('form');
    }
  }

  function parseAjaxResponse(response){
    return response.text().then(function(txt){
      try { return JSON.parse(txt); }
      catch(e){ return {success:false, data:{message:'Die Website hat keine gÃ¼ltige Antwort zurÃ¼ckgegeben. Bitte prÃ¼fe PHP-Fehler oder Hosting-Limits.'}}; }
    });
  }

  var liveState = {versions:{}, pending:false};

  function collectSectionState(section){
    if(!section) return null;
    var state = {};
    section.querySelectorAll('input[name], select[name], textarea[name]').forEach(function(el){
      if(!el.name) return;
      if(el.type === 'file') return;
      if(el.type === 'checkbox' || el.type === 'radio'){ state[el.name] = !!el.checked; }
      else { state[el.name] = el.value; }
    });
    return state;
  }

  function restoreSectionState(section, state){
    if(!section || !state) return;
    section.querySelectorAll('input[name], select[name], textarea[name]').forEach(function(el){
      if(!el.name || !(el.name in state)) return;
      if(el.type === 'file') return;
      if(el.type === 'checkbox' || el.type === 'radio'){ el.checked = !!state[el.name]; }
      else { el.value = state[el.name]; }
      try { el.dispatchEvent(new Event('change', {bubbles:true})); } catch(e) {}
      try { el.dispatchEvent(new Event('input', {bubbles:true})); } catch(e) {}
    });
  }

  function isPortalBusy(){
    if(document.querySelector('form.is-loading')) return true;
    var active = document.activeElement;
    if(active && (active.matches('input, textarea, select') || active.closest('form'))) return true;
    return false;
  }

  function collectVisibleSections(){
    var out = [];
    document.querySelectorAll('.ldkp-section[data-section]').forEach(function(section){ out.push(section.getAttribute('data-section')); });
    return out;
  }

  function syncLiveUpdates(force){
    if(!ajax) return;
    if(liveState.pending) return;
    if(!force && document.hidden) return;
    if(!force && isPortalBusy()) return;
    var body = new URLSearchParams();
    body.append('action','ldk_portal_live_sync');
    body.append('nonce',ajax.nonce);
    body.append('versions', JSON.stringify(liveState.versions || {}));
    body.append('visible_sections', JSON.stringify(collectVisibleSections()));
    body.append('active_section', menu ? (menu.value || '') : '');
    liveState.pending = true;
    fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
      .then(parseAjaxResponse)
      .then(function(j){
        liveState.pending = false;
        if(!j || !j.success || !j.data) return;
        var data = j.data || {};
        if(data.sections && Object.keys(data.sections).length){
          var states = {};
          Object.keys(data.sections).forEach(function(key){
            var section = document.querySelector('.ldkp-section[data-section="' + key + '"]');
            states[key] = collectSectionState(section);
          });
          replacePortalSections(data.sections);
          Object.keys(states).forEach(function(key){
            var section = document.querySelector('.ldkp-section[data-section="' + key + '"]');
            restoreSectionState(section, states[key]);
          });
          rebindPortalUi();
        }
        if(data.versions){ liveState.versions = data.versions; }
      })
      .catch(function(){ liveState.pending = false; });
  }

  function handlePortalAjaxForm(form){
    if(!ajax || !form) return false;
    var actionInput = form.querySelector('input[name="action"]');
    if(!actionInput) return false;
    var actionMap = {
      'ldk_portal_create_employee': 'ldk_portal_create_employee_ajax',
      'ldk_portal_save_request': 'ldk_portal_save_request_ajax',
      'ldk_portal_upload_request_document': 'ldk_portal_upload_request_document_ajax',
      'ldk_portal_send_head_message': 'ldk_portal_send_head_message_ajax',
      'ldk_portal_reply_head_message': 'ldk_portal_reply_head_message_ajax',
      'ldk_portal_save_employee': 'ldk_portal_save_employee_ajax',
      'ldk_portal_delete_employee': 'ldk_portal_delete_employee_ajax',
      'ldk_portal_send_replacement_request': 'ldk_portal_send_replacement_request_ajax',
      'ldk_portal_save_emergency_message': 'ldk_portal_save_emergency_message_ajax',
      'ldk_portal_delete_emergency_message': 'ldk_portal_delete_emergency_message_ajax'
    };
    var ajaxAction = actionMap[actionInput.value || ''];
    if(!ajaxAction) return false;
    var body = new FormData(form);
    body.set('action', ajaxAction);
    body.set('nonce', ajax.nonce);
    form.querySelectorAll('.ldkp-status').forEach(function(el){ el.remove(); });
    form.classList.add('is-loading');
    form.querySelectorAll('button, input[type="submit"]').forEach(function(btn){ btn.disabled = true; });
    fetch(ajax.url, {method:'POST', credentials:'same-origin', body:body})
      .then(parseAjaxResponse)
      .then(function(j){
        form.classList.remove('is-loading');
        form.querySelectorAll('button, input[type="submit"]').forEach(function(btn){ btn.disabled = false; });
        if(!j || !j.success){
          showAjaxStatus(form, (j && j.data && j.data.message) ? j.data.message : 'Speichern fehlgeschlagen.', true);
          return;
        }
        if(j.data && j.data.sections){
          replacePortalSections(j.data.sections);
          if(j.data.open_section){
            if(menu){ menu.value = j.data.open_section; }
            showSection(j.data.open_section);
          }
          rebindPortalUi();
          if(j.data.sick_tab){ showSickTab(j.data.sick_tab); }
          var targetSection = j.data.open_section ? document.querySelector('.ldkp-section[data-section="' + j.data.open_section + '"]') : form.closest('.ldkp-section');
          showAjaxStatus(targetSection || form, j.data.message || 'Gespeichert.', false);
        } else {
          if(form.reset){ form.reset(); }
          showAjaxStatus(form, j.data && j.data.message ? j.data.message : 'Gespeichert.', false);
        }
        if(actionInput.value === 'ldk_portal_create_employee' && form.reset){ form.reset(); }
        refreshNotifications();
      })
      .catch(function(){
        form.classList.remove('is-loading');
        form.querySelectorAll('button, input[type="submit"]').forEach(function(btn){ btn.disabled = false; });
        showAjaxStatus(form, 'Senden fehlgeschlagen. Bitte prÃ¼fe DateigrÃ¶Ãe, Serverlimits oder Sicherheitsregeln.', true);
      });
    return true;
  }

  document.addEventListener('submit', function(e){
    var form = e.target;
    if(handlePortalAjaxForm(form)){
      e.preventDefault();
    }
  }, true);

  initEmployeeFilters();

  if(ajax){
    bindNoticeOpenButtons(document);
    bindApprovalButtons(document);
    bindNoticeButtons(document);
    bindReplacementButtons(document);
    refreshNotifications();
    syncLiveUpdates(true);
    window.setInterval(function(){ syncLiveUpdates(false); }, (ajax.liveInterval || 8000));
    document.addEventListener('visibilitychange', function(){
      if(!document.hidden){ refreshNotifications(); syncLiveUpdates(true); }
    });
  }

  function filterPlanSection(root){
    if(!root) return;
    var dateFromSel = root.querySelector('[data-plan-date-from]');
    var dateToSel = root.querySelector('[data-plan-date-to]');
    var monthSel = root.querySelector('[data-plan-month]');
    var employeeSel = root.querySelector('[data-plan-employee]');
    var search = root.querySelector('[data-plan-search]');
    var mode = root.querySelector('[data-plan-mode]');
    var activeFrom = dateFromSel ? dateFromSel.value : '';
    var activeTo = dateToSel ? dateToSel.value : '';
    var activeMonth = monthSel ? monthSel.value : '';
    var activeEmp = employeeSel ? employeeSel.value : '';
    var q = search ? search.value.toLowerCase().trim() : '';
    var dayBlocks = root.querySelectorAll('[data-plan-day]');
    var visibleDays = 0;

    function normalizeDate(value){
      if(!value) return '';
      var parts = value.split('.');
      if(parts.length !== 3) return value;
      return parts[2] + '-' + parts[1].padStart(2, '0') + '-' + parts[0].padStart(2, '0');
    }

    var fromKey = normalizeDate(activeFrom);
    var toKey = normalizeDate(activeTo);
    if(fromKey && toKey && fromKey > toKey){
      var tmp = fromKey; fromKey = toKey; toKey = tmp;
      if(dateFromSel && dateToSel){
        var tmpValue = dateFromSel.value;
        dateFromSel.value = dateToSel.value;
        dateToSel.value = tmpValue;
      }
    }

    dayBlocks.forEach(function(day){
      var d = day.getAttribute('data-date') || '';
      var m = day.getAttribute('data-month') || '';
      var dayKey = normalizeDate(d);
      var showDay = true;
      if(mode && mode.value === 'day'){
        if(fromKey && dayKey < fromKey) showDay = false;
        if(toKey && dayKey > toKey) showDay = false;
      }
      if(mode && mode.value === 'month' && activeMonth && m !== activeMonth) showDay = false;
      var visibleRows = 0;
      day.querySelectorAll('[data-plan-row]').forEach(function(row){
        var show = true;
        var name = row.getAttribute('data-name') || '';
        if(activeEmp && name !== activeEmp) show = false;
        if(q && (row.textContent || '').toLowerCase().indexOf(q) === -1) show = false;
        row.style.display = show ? '' : 'none';
        if(show) visibleRows++;
      });
      day.style.display = (showDay && visibleRows > 0) ? '' : 'none';
      if(showDay && visibleRows > 0) visibleDays++;
    });

    var count = root.querySelector('[data-filter-count]');
    if(count){
      if(mode && mode.value === 'day' && (activeFrom || activeTo)){
        var labelFrom = activeFrom || 'Anfang';
        var labelTo = activeTo || 'Ende';
        count.textContent = visibleDays + ' sichtbare Tage (' + labelFrom + ' bis ' + labelTo + ')';
      } else {
        count.textContent = visibleDays + ' sichtbare Tage';
      }
    }
  }

  document.querySelectorAll('[data-plan-filters]').forEach(function(root){
    root.querySelectorAll('select,input').forEach(function(el){
      el.addEventListener('change', function(){ filterPlanSection(root); });
      el.addEventListener('input', function(){ filterPlanSection(root); });
    });
    filterPlanSection(root);
    var printBtn = root.querySelector('[data-plan-print]');
    if(printBtn){
      printBtn.addEventListener('click', function(){
        var render = root.querySelector('[data-plan-render]');
        if(!render) return;
        var clone = render.cloneNode(true);
        clone.querySelectorAll('.ldkp-filterbar,.ldkp-head-actions,[data-plan-print]').forEach(function(el){ el.remove(); });
        clone.querySelectorAll('[data-plan-day]').forEach(function(day){
          if(day.style.display === 'none'){ day.remove(); return; }
          day.querySelectorAll('[data-plan-row]').forEach(function(row){ if(row.style.display === 'none'){ row.remove(); } });
        });
        var css = '<style>@page{margin:12mm} html,body{background:#fff!important} body{font-family:Arial,sans-serif;padding:0;margin:0;color:#111} body>*{display:none!important} #ldkp-print-root{display:block!important} #ldkp-print-root .ldkp-day{margin-bottom:24px} #ldkp-print-root .ldkp-date{font-size:24px;font-weight:700;margin:0 0 10px} #ldkp-print-root .ldkp-area{font-size:18px;font-weight:700;margin:14px 0 8px} #ldkp-print-root table{width:100%;border-collapse:collapse} #ldkp-print-root th,#ldkp-print-root td{border:1px solid #d1d5db;padding:8px 10px;text-align:left} #ldkp-print-root th{background:#f3f4f6} #ldkp-print-root .ldkp-mobile-item{display:none!important} #ldkp-print-root .ldkp-pill{display:inline-block;padding:3px 8px;border-radius:999px;border:1px solid #ccc;font-size:12px}</style>';
        var w = window.open('', '_blank');
        if(!w) return;
        w.document.open();
        w.document.write('<!doctype html><html><head><meta charset="utf-8"><title>Dienstplan</title>' + css + '</head><body><div id="ldkp-print-root">' + clone.innerHTML + '</div></body></html>');
        w.document.close();
        w.focus();
        setTimeout(function(){ w.print(); }, 300);
      });
    }
  });

  bindEmployeeFilters();

  var editorWrap = document.getElementById('ldkp-editor-wrap');
  if(editorWrap && ajax){
    var rows = [];

    function esc(val){
      return String(val == null ? '' : val)
        .replace(/&/g,'&amp;')
        .replace(/</g,'&lt;')
        .replace(/>/g,'&gt;')
        .replace(/"/g,'&quot;');
    }

    function renderEditor(){
      var tbody = editorWrap.querySelector('tbody');
      if(!tbody) return;
      tbody.innerHTML = '';
      rows.forEach(function(r, idx){
        var tr = document.createElement('tr');
        tr.innerHTML = '<td><input data-k="date" value="' + esc(r.date || '') + '"></td>' +
          '<td><input data-k="name" value="' + esc(r.name || '') + '"></td>' +
          '<td><select data-k="status"><option value="ARBEIT">Arbeit</option><option value="FR">Frei</option><option value="FE">Ferien</option><option value="FI">Feier</option></select></td>' +
          '<td><input data-k="area" value="' + esc(r.area || '') + '"></td>' +
          '<td><input data-k="shift" value="' + esc(r.shift || '') + '"></td>' +
          '<td><input data-k="start" value="' + esc(r.start || '') + '"></td>' +
          '<td><input data-k="end" value="' + esc(r.end || '') + '"></td>' +
          '<td><input data-k="break" value="' + esc(r.break || '') + '"></td>' +
          '<td><button type="button" class="ldkp-btn light" data-row-add="' + idx + '">+ oben</button> <button type="button" class="ldkp-btn light" data-row-add-below="' + idx + '">+ unten</button> <button type="button" class="ldkp-btn red" data-row-del="' + idx + '">LÃ¶schen</button></td>';
        tr.querySelector('[data-k="status"]').value = r.status || 'ARBEIT';
        tr.querySelectorAll('[data-k]').forEach(function(inp){
          inp.addEventListener('input', function(){ rows[idx][inp.getAttribute('data-k')] = inp.value; });
          inp.addEventListener('change', function(){ rows[idx][inp.getAttribute('data-k')] = inp.value; });
        });
        tbody.appendChild(tr);
      });

      tbody.querySelectorAll('[data-row-del]').forEach(function(btn){
        btn.addEventListener('click', function(){
          rows.splice(parseInt(btn.getAttribute('data-row-del'),10), 1);
          renderEditor();
        });
      });

      tbody.querySelectorAll('[data-row-add]').forEach(function(btn){
        btn.addEventListener('click', function(){
          rows.splice(parseInt(btn.getAttribute('data-row-add'),10), 0, {date:'',name:'',status:'ARBEIT',area:'',shift:'',start:'',end:'',break:''});
          renderEditor();
        });
      });

      tbody.querySelectorAll('[data-row-add-below]').forEach(function(btn){
        btn.addEventListener('click', function(){
          rows.splice(parseInt(btn.getAttribute('data-row-add-below'),10) + 1, 0, {date:'',name:'',status:'ARBEIT',area:'',shift:'',start:'',end:'',break:''});
          renderEditor();
        });
      });
    }

    function request(action, payload, cb){
      var body = new URLSearchParams();
      body.append('action', action);
      body.append('nonce', ajax.nonce);
      Object.keys(payload || {}).forEach(function(k){ body.append(k, payload[k]); });
      fetch(ajax.url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},body:body.toString()})
        .then(function(r){ return r.json(); })
        .then(function(j){
          if(!j || !j.success){
            alert(j && j.data && j.data.message ? j.data.message : 'Aktion fehlgeschlagen.');
            return;
          }
          if(cb) cb(j.data || {});
        })
        .catch(function(){ alert('Aktion fehlgeschlagen.'); });
    }

    request('ldk_portal_get_plan_editor_data', {}, function(data){
      rows = Array.isArray(data.rows) ? data.rows : [];
      renderEditor();
      var st = editorWrap.querySelector('[data-editor-status]');
      if(st){ st.textContent = 'Status: ' + (data.status || 'empty'); }
    });

    var addRow = editorWrap.querySelector('[data-editor-add]');
    if(addRow){ addRow.addEventListener('click', function(){ rows.push({date:'',name:'',status:'ARBEIT',area:'',shift:'',start:'',end:'',break:''}); renderEditor(); }); }
    var saveBtn = editorWrap.querySelector('[data-editor-save]');
    if(saveBtn){ saveBtn.addEventListener('click', function(){ request('ldk_portal_save_plan_rows', {rows:JSON.stringify(rows)}, function(d){ alert(d.message || 'Gespeichert.'); }); }); }
    var pubBtn = editorWrap.querySelector('[data-editor-publish]');
    if(pubBtn){ pubBtn.addEventListener('click', function(){ request('ldk_portal_publish_plan_rows', {rows:JSON.stringify(rows)}, function(d){ status.textContent = d.message || 'Freigegeben.'; if(d.sections){ replacePortalSections(d.sections); rebindPortalUi(); } syncLiveUpdates(true); }); }); }
    var delBtn = editorWrap.querySelector('[data-editor-delete]');
    if(delBtn){ delBtn.addEventListener('click', function(){ if(!confirm('Plan wirklich lÃ¶schen?')) return; request('ldk_portal_delete_plan', {}, function(d){ status.textContent = d.message || 'GelÃ¶scht.'; tbody.innerHTML = ''; if(d.sections){ replacePortalSections(d.sections); rebindPortalUi(); } syncLiveUpdates(true); }); }); }
    var expBtn = editorWrap.querySelector('[data-editor-export]');
    if(expBtn){ expBtn.addEventListener('click', function(){ request('ldk_portal_export_csv', {rows:JSON.stringify(rows)}, function(d){ downloadBase64(d.filename,d.content); }); }); }
    function pad(n){ return String(n).padStart(2,'0'); }
    function normalizeTime(v){
      var s = String(v == null ? '' : v).trim();
      if(!s || s === '-') return 'â';
      return s.split(/\s*\/\s*/).map(function(part){
        var m = part.trim().match(/^(\d{1,2}):(\d{1,2})$/);
        return m ? (pad(m[1]) + ':' + pad(m[2])) : part.trim();
      }).filter(Boolean).join(' / ') || 'â';
    }
    function excelDateToGerman(v){
      if (v == null || v === '') return '';
      if (typeof v === 'number' && typeof XLSX !== 'undefined' && XLSX.SSF) {
        var d = XLSX.SSF.parse_date_code(v);
        if (d && d.y && d.m && d.d) return pad(d.d) + '.' + pad(d.m) + '.' + d.y;
      }
      if (v instanceof Date) return pad(v.getDate()) + '.' + pad(v.getMonth()+1) + '.' + v.getFullYear();
      var s = String(v).trim();
      var m = s.match(/^(\d{1,2})[.\-/](\d{1,2})[.\-/](\d{2,4})$/);
      if (m) { var y = m[3]; if (y.length === 2) y = '20' + y; return pad(m[1]) + '.' + pad(m[2]) + '.' + y; }
      return s;
    }
    function normalizeCell(v){ if (v == null) return ''; return String(v).replace(//g,'').trim(); }
    function isDateLike(v){ if (v instanceof Date) return true; if (typeof v === 'number') return true; var s = normalizeCell(v); return /^\d{1,2}[.\-/]\d{1,2}[.\-/]\d{2,4}$/.test(s); }
    function isAreaRow(name){ return ['Management','Shift Leader','Service','Bar','KÃ¼che'].indexOf(name) !== -1; }
    function parseLegend(wb){
      var legend = {};
      wb.SheetNames.forEach(function(sheetName){
        var ws = wb.Sheets[sheetName];
        var arr = XLSX.utils.sheet_to_json(ws, {header:1, raw:true, defval:''});
        arr.forEach(function(row){
          var code = normalizeCell(row[1]).toUpperCase();
          if (!/^(M1|M2|M3|M7|SPM|S2|S3|S5|S8|S9|S10|S11|B1|B2|KK1|KK4)$/.test(code)) return;
          var area = normalizeCell(row[0]);
          var source = normalizeCell(row[2]) + ' ' + normalizeCell(row[3]);
          var times = Array.from(source.matchAll(/(\d{1,2}:\d{2})\s*-\s*(\d{1,2}:\d{2})/g));
          var starts = times.map(function(t){ return normalizeTime(t[1]); });
          var ends = times.map(function(t){ return normalizeTime(t[2]); });
          var pause = 'â';
          var pauseMatch = source.match(/Pause:\s*(\d{1,2}:\d{2})\s*-\s*(\d{1,2}:\d{2})/i);
          if (pauseMatch) pause = normalizeTime(pauseMatch[1]) + ' / ' + normalizeTime(pauseMatch[2]).replace(' / ', '');
          if (area.indexOf('Service / Bar') !== -1 && /^B\d+$/.test(code)) area = 'Bar';
          else if (area.indexOf('Service / Bar') !== -1) area = 'Service';
          legend[code] = { area: area || '', start: starts.join(' / ') || 'â', end: ends.join(' / ') || 'â', break: pause || 'â' };
        });
      });
      return legend;
    }
    function extractStatusAndShift(cell){
      var text = normalizeCell(cell).replace(/\*/g,'');
      if (!text) return null;
      if (/^FE$/i.test(text)) return {status:'FE'};
      if (/^FI$/i.test(text)) return {status:'FI'};
      if (/^FR$/i.test(text)) return {status:'FR'};
      var codeMatch = text.match(/(M1|M2|M3|M7|SPM|S2|S3|S5|S8|S9|S10|S11|B1|B2|KK1|KK4)/i);
      if (!codeMatch) return null;
      var shift = codeMatch[1].toUpperCase();
      var times = Array.from(text.matchAll(/(\d{1,2}:\d{2})\s*-\s*(\d{1,2}:\d{2})/g));
      var starts = times.map(function(m){ return normalizeTime(m[1]); });
      var ends = times.map(function(m){ return normalizeTime(m[2]); });
      return { status:'ARBEIT', shift:shift, start: starts.join(' / ') || '', end: ends.join(' / ') || '' };
    }
    function parseMatrixArray(arr, legend){
      if (!arr || !arr.length) return [];
      var header = arr[0] || [];
      var dateCols = [];
      for (var c = 1; c < header.length; c++) if (isDateLike(header[c])) dateCols.push({col:c, date:excelDateToGerman(header[c])});
      if (!dateCols.length) return [];
      var currentArea = ''; var out = [];
      for (var r = 2; r < arr.length; r++) {
        var row = arr[r] || []; var first = normalizeCell(row[0]); if (!first) continue;
        if (isAreaRow(first)) { currentArea = first; continue; }
        dateCols.forEach(function(dc){
          var parsed = extractStatusAndShift(row[dc.col]); if (!parsed) return;
          if (parsed.status !== 'ARBEIT') { out.push({date:dc.date,name:first,status:parsed.status,area:currentArea || '',shift:parsed.status,start:'â',end:'â',break:'â'}); return; }
          var lg = legend[parsed.shift] || {};
          var area = lg.area || currentArea || '';
          if (area === 'Service / Bar' && /^B\d+$/.test(parsed.shift)) area = 'Bar'; else if (area === 'Service / Bar') area = 'Service';
          out.push({ date:dc.date, name:first, status:'ARBEIT', area:area || '', shift:parsed.shift, start:normalizeTime(parsed.start || lg.start || 'â'), end:normalizeTime(parsed.end || lg.end || 'â'), break:normalizeTime(lg.break || 'â') });
        });
      }
      return out;
    }
    function parseDelimitedText(text){
      var lines = text.split(/?
/).filter(Boolean); if (!lines.length) return [];
      var first = lines[0]; var delim = first.indexOf(';') !== -1 ? ';' : ',';
      return lines.map(function(line){
        var out = []; var cur = ''; var inQuotes = false;
        for (var i = 0; i < line.length; i++) {
          var ch = line[i];
          if (ch === '"') { if (inQuotes && line[i+1] === '"') { cur += '"'; i++; } else inQuotes = !inQuotes; }
          else if (ch === delim && !inQuotes) { out.push(cur); cur = ''; }
          else cur += ch;
        }
        out.push(cur); return out;
      });
    }
    async function parseWorkbook(file){
      var ext = (file.name.split('.').pop() || '').toLowerCase();
      if (ext === 'csv') { var text = await file.text(); return parseMatrixArray(parseDelimitedText(text), {}); }
      if (typeof XLSX === 'undefined') throw new Error('XLSX fehlt');
      var data = await file.arrayBuffer();
      var wb = XLSX.read(data, {type:'array', cellDates:true, raw:true});
      var legend = parseLegend(wb);
      var allRows = [];
      wb.SheetNames.forEach(function(name){
        var ws = wb.Sheets[name];
        var arr = XLSX.utils.sheet_to_json(ws, {header:1, raw:true, defval:''});
        var parsed = parseMatrixArray(arr, legend);
        if (parsed.length) allRows = allRows.concat(parsed);
      });
      return allRows;
    }

    var fileInput = editorWrap.querySelector('[data-editor-file]');
    if(fileInput){
      fileInput.addEventListener('change', async function(){
        var file = fileInput.files && fileInput.files[0];
        if(!file) return;
        try {
          var next = await parseWorkbook(file);
          if(!next.length){ alert('Datei konnte nicht sinnvoll gelesen werden.'); return; }
          rows = next;
          renderEditor();
        } catch(err) {
          console.error(err);
          alert('Datei konnte nicht gelesen werden.');
        }
      });
    }
  }
});
JS;
    }

    private function weekday_label_de($date) {
        $date = trim((string) $date);
        if ($date === '') return '';
        $dt = DateTime::createFromFormat('d.m.Y', $date);
        if (!$dt) {
            $ts = strtotime($date);
            if (!$ts) return '';
            $dt = new DateTime('@' . $ts);
            $dt->setTimezone(wp_timezone());
        }
        $days = array('So','Mo','Di','Mi','Do','Fr','Sa');
        $idx = intval($dt->format('w'));
        return isset($days[$idx]) ? $days[$idx] : '';
    }

    private function format_date_with_weekday($date) {
        $date = trim((string) $date);
        if ($date === '') return '';
        $weekday = $this->weekday_label_de($date);
        return $weekday !== '' ? ($date . ' (' . $weekday . ')') : $date;
    }

    private function group_days($rows) {
        $days = array();
        foreach ($rows as $row) {
            if (empty($row['date'])) continue;
            $days[$row['date']][] = $row;
        }
        uksort($days, function($a, $b){
            $da = DateTime::createFromFormat('d.m.Y', $a);
            $db = DateTime::createFromFormat('d.m.Y', $b);
            if ($da && $db) return $da <=> $db;
            return strcmp($a, $b);
        });
        return $days;
    }

    private function get_month_key($date) {
        $d = DateTime::createFromFormat('d.m.Y', $date);
        return $d ? $d->format('Y-m') : '';
    }

    private function filter_rows_for_user($rows, $user) {
        if ($this->can_view_all($user)) return $rows;
        $out = array();
        foreach ($rows as $row) if (!empty($row['name']) && $this->employee_matches_user($row['name'], $user)) $out[] = $row;
        return $out;
    }

    private function get_own_rows($rows, $user) {
        $out = array();
        foreach ($rows as $row) if (!empty($row['name']) && $this->employee_matches_user($row['name'], $user)) $out[] = $row;
        return $out;
    }

    private function today_rows($rows, $user) {
        $today = wp_date('d.m.Y');
        $out = array();
        foreach ($rows as $row) if ((isset($row['date']) ? $row['date'] : '') === $today && !empty($row['name']) && $this->employee_matches_user($row['name'], $user)) $out[] = $row;
        return $out;
    }

    private function is_work_row($row) {
        $status = strtoupper(trim((string) (isset($row['status']) ? $row['status'] : '')));
        return !in_array($status, array('FR','FE','FI'), true);
    }

    private function get_today_team_rows() {
        $today = wp_date('d.m.Y');
        $out = array();
        foreach ($this->get_published_rows() as $row) {
            if ((isset($row['date']) ? $row['date'] : '') !== $today) continue;
            if (empty($row['name'])) continue;
            if (!$this->is_work_row($row)) continue;
            $out[] = $row;
        }
        usort($out, function($a, $b){
            $as = isset($a['start']) ? $a['start'] : '';
            $bs = isset($b['start']) ? $b['start'] : '';
            if ($as === $bs) return strcasecmp((string)$a['name'], (string)$b['name']);
            return strcmp($as, $bs);
        });
        return $out;
    }



    private function area_matches_employee($missing_area, $emp, $name = '') {
        $missing_area = strtolower(trim((string)$missing_area));
        if ($missing_area === '') return true;
        $candidates = array();
        if (!empty($emp['primary_area'])) $candidates[] = $emp['primary_area'];
        if (!empty($emp['extra_areas'])) {
            foreach (preg_split('/[,;]+/', (string)$emp['extra_areas']) as $part) {
                if (trim($part) !== '') $candidates[] = trim($part);
            }
        }
        if (!empty($candidates)) {
            foreach ($candidates as $cand) {
                if (strtolower(trim((string)$cand)) === $missing_area) return true;
            }
            return false;
        }
        if ($name === '') $name = isset($emp['name']) ? $emp['name'] : '';
        $aliases = !empty($emp['aliases']) && is_array($emp['aliases']) ? $emp['aliases'] : array($name);
        foreach ($this->get_published_rows() as $row) {
            if (empty($row['name']) || empty($row['area'])) continue;
            $name_match = false;
            foreach ($aliases as $alias) { if ($this->same_name($row['name'], $alias)) { $name_match = true; break; } }
            if (!$name_match) continue;
            if (strtolower(trim((string)$row['area'])) === $missing_area) return true;
        }
        return false;
    }

    private function area_order_weight($area) {
        $a = strtolower(trim((string)$area));
        $map = array('management'=>1,'manager'=>1,'service'=>2,'bar'=>3,'kÃ¼che'=>4,'kuche'=>4,'kitchen'=>4);
        return isset($map[$a]) ? $map[$a] : 99;
    }

    private function get_replacement_suggestions($missing_row, $limit = 3) {
        $today = wp_date('Y-m-d');
        $area = strtolower(trim((string) (isset($missing_row['area']) ? $missing_row['area'] : '')));
        $requests = $this->get_requests();
        $out = array();
        foreach ($this->get_employees() as $key => $emp) {
            $name = isset($emp['name']) ? $emp['name'] : '';
            if ($name === '' || $this->same_name($name, isset($missing_row['name']) ? $missing_row['name'] : '')) continue;
            $blocked = false;
            foreach ($requests as $r) {
                if (empty($r['employee_name']) || !$this->same_name($r['employee_name'], $name)) continue;
                if (empty($r['date_from']) || empty($r['date_to'])) continue;
                if ($today < $r['date_from'] || $today > $r['date_to']) continue;
                if (in_array(isset($r['request_type']) ? $r['request_type'] : '', array('krankmeldung','urlaub','wunschfrei'), true)) { $blocked = true; break; }
            }
            if ($blocked) continue;
            $today_row = $this->get_row_for_employee_and_date($name, wp_date('d.m.Y'));
            if (!$today_row) continue;
            $today_status = strtoupper(trim((string) (isset($today_row['status']) ? $today_row['status'] : '')));
            if ($today_status !== 'FR') continue;
            if (!$this->area_matches_employee($area, $emp, $name)) continue;
            $contact = $this->get_employee_contact_line($emp);
            $out[] = array(
                'key' => $key,
                'name' => $name,
                'role' => isset($emp['role']) ? $emp['role'] : 'employee',
                'area' => !empty($emp['primary_area']) ? $emp['primary_area'] : (isset($missing_row['area']) ? $missing_row['area'] : ''),
                'extra_areas' => isset($emp['extra_areas']) ? $emp['extra_areas'] : '',
                'contact' => $contact,
                'status' => 'Frei',
            );
        }
        usort($out, function($a, $b){ return strcasecmp($a['name'], $b['name']); });
        if ($limit > 0) $out = array_slice($out, 0, $limit);
        return $out;
    }


    private function get_free_employees_for_manual_request($missing_row) {
        $today = wp_date('Y-m-d');
        $requests = $this->get_requests();
        $out = array();
        foreach ($this->get_employees() as $key => $emp) {
            $name = isset($emp['name']) ? $emp['name'] : '';
            if ($name === '' || $this->same_name($name, isset($missing_row['name']) ? $missing_row['name'] : '')) continue;
            $today_row = $this->get_row_for_employee_and_date($name, wp_date('d.m.Y'));
            if (!$today_row) continue;
            $today_status = strtoupper(trim((string) (isset($today_row['status']) ? $today_row['status'] : '')));
            if ($today_status !== 'FR') continue;
            $blocked = false;
            foreach ($requests as $r) {
                if (empty($r['employee_name']) || !$this->same_name($r['employee_name'], $name)) continue;
                if (empty($r['date_from']) || empty($r['date_to'])) continue;
                if ($today < $r['date_from'] || $today > $r['date_to']) continue;
                if (in_array(isset($r['request_type']) ? $r['request_type'] : '', array('krankmeldung','urlaub','wunschfrei'), true)) { $blocked = true; break; }
            }
            if ($blocked) continue;
            $contact = $this->get_employee_contact_line($emp);
            $out[] = array(
                'key' => $key,
                'name' => $name,
                'area' => isset($today_row['area']) ? $today_row['area'] : (!empty($emp['primary_area']) ? $emp['primary_area'] : ''),
                'status' => 'Frei',
                'contact' => $contact,
            );
        }
        usort($out, function($a, $b){ return strcasecmp($a['name'], $b['name']); });
        return $out;
    }

    private function filter_unsent_replacement_employees($employees, $replacement) {
        if (empty($replacement) || empty($replacement['candidate_keys']) || !is_array($replacement['candidate_keys'])) return $employees;
        return array_values(array_filter($employees, function($emp) use ($replacement) {
            return empty($emp['key']) || !in_array($emp['key'], $replacement['candidate_keys'], true);
        }));
    }

    private function get_requested_replacement_label($replacement) {
        $count = !empty($replacement['candidate_keys']) && is_array($replacement['candidate_keys']) ? count($replacement['candidate_keys']) : 0;
        if ($count < 1) return '';
        return 'Anfrage bereits an '.$count.' Mitarbeiter gesendet.';
    }

    private function render_today($rows, $user) {
        $todayRows = $this->today_rows($rows, $user);
        $ownTodayRow = !empty($todayRows) ? $todayRows[0] : null;
        $todaySick = $this->can_view_all($user) ? $this->get_today_sick_requests() : array();
        $teamToday = $this->can_view_all($user) ? $this->get_today_team_rows() : array();
        $replacementRequests = $this->get_replacement_requests();
        $grouped = array();
        foreach ($teamToday as $row) {
            $area = !empty($row['area']) ? $row['area'] : 'Ohne Bereich';
            if (!isset($grouped[$area])) $grouped[$area] = array();
            $grouped[$area][] = $row;
        }
        uksort($grouped, function($a,$b){ $wa=$this->area_order_weight($a); $wb=$this->area_order_weight($b); return $wa===$wb ? strcasecmp($a,$b) : ($wa <=> $wb); });
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">Heute</div>';
        echo $this->render_emergency_board($user);

        echo '<div class="ldkp-today-box ldkp-my-plan-card" style="margin-bottom:14px;">';
        echo '<div class="ldkp-row" style="justify-content:space-between;align-items:center;margin-bottom:10px;"><strong>Mein heutiger Plan</strong><span class="ldkp-pill '.($ownTodayRow && $this->is_work_row($ownTodayRow) ? 'approved' : 'open').'">'.($ownTodayRow && $this->is_work_row($ownTodayRow) ? 'Heute geplant' : 'Heute kein Dienst').'</span></div>';
        if ($ownTodayRow && $this->is_work_row($ownTodayRow)) {
            echo '<div class="ldkp-grid">';
            foreach (array('Start'=>'start','Ende'=>'end','Pause'=>'break') as $label => $key) {
                echo '<div class="ldkp-col-4"><div class="ldkp-small">'.esc_html($label).'</div><div style="font-size:24px;font-weight:800;margin-top:8px;">'.esc_html(isset($ownTodayRow[$key]) && $ownTodayRow[$key] !== '' ? $ownTodayRow[$key] : 'â').'</div></div>';
            }
            echo '</div>';
        } else {
            echo '<div class="ldkp-muted">FÃ¼r dich ist heute kein eigener Arbeitsdienst eingetragen.</div>';
        }
        echo '</div>';

        if ($this->can_view_all($user)) {
            echo '<div class="ldkp-today-box" style="margin-bottom:14px;">';
            echo '<div class="ldkp-row" style="justify-content:space-between;align-items:center;margin-bottom:10px;"><strong>Heute im Einsatz</strong><span class="ldkp-pill approved">'.intval(count($teamToday)).' im Dienst</span></div>';
            if (empty($teamToday)) {
                echo '<div class="ldkp-muted">Heute ist aktuell niemand als Arbeitsdienst eingeplant.</div>';
            } else {
                foreach ($grouped as $area => $items) {
                    echo '<div class="ldkp-area-group"><div class="ldkp-area-head">'.esc_html($area).'</div>';
                    echo '<div class="ldkp-line-table"><div class="ldkp-line-row ldkp-line-head"><span>Name</span><span>Start</span><span>Ende</span><span>Pause</span></div>';
                    foreach ($items as $row) {
                        echo '<div class="ldkp-line-row"><span><strong>'.esc_html($row['name']).'</strong></span><span>'.esc_html(isset($row['start']) ? $row['start'] : 'â').'</span><span>'.esc_html(isset($row['end']) ? $row['end'] : 'â').'</span><span>'.esc_html(isset($row['break']) && $row['break'] !== '' ? $row['break'] : (isset($row['pause']) && $row['pause'] !== '' ? $row['pause'] : 'â')).'</span></div>';
                    }
                    echo '</div></div>';
                }
            }
            echo '</div>';

            echo '<div class="ldkp-today-box" style="margin-bottom:14px;"><div class="ldkp-row" style="justify-content:space-between;align-items:center;"><strong>Heutige Krankmeldungen</strong><span class="ldkp-pill rejected">'.intval(count($todaySick)).' heute</span></div>';
            if (empty($todaySick)) {
                echo '<div style="margin-top:8px;" class="ldkp-muted">Heute gibt es keine Krankmeldungen von eingeplanten Mitarbeitern.</div>';
            } else {
                foreach ($todaySick as $item) {
                    $r = $item['request']; $row = $item['today_row'];
                    $suggestions = $this->get_replacement_suggestions($row, 0);
                    $activeReplacement = null;
                    foreach ($replacementRequests as $rep) {
                        if (!empty($rep['source_request_id']) && $rep['source_request_id'] === $r['id'] && $rep['date'] === current_time('Y-m-d')) { $activeReplacement = $rep; break; }
                    }
                    echo '<div class="ldkp-notice unread" style="margin-top:10px;background:#fff;">';
                    echo '<div class="ldkp-row" style="justify-content:space-between;align-items:flex-start;"><div><strong>'.esc_html($r['employee_name']).'</strong> <span style="color:#b91c1c;font-weight:800;">KRANK GEMELDET</span><div class="ldkp-muted">'.esc_html($this->format_date_with_weekday($r['date_from'])).' bis '.esc_html($this->format_date_with_weekday($r['date_to'])).'</div></div></div>';
                    echo '<div style="margin-top:6px;"><strong>Heutiger Dienst:</strong> '.esc_html($row['area']).' | '.esc_html($row['start']).' - '.esc_html($row['end']).' | Pause '.esc_html(!empty($row['break'])?$row['break']:'â').'</div>';
                    if (!empty($r['reason'])) echo '<div style="margin-top:6px;"><strong>BegrÃ¼ndung:</strong> '.esc_html($r['reason']).'</div>';
                    if ($activeReplacement && !empty($activeReplacement['assigned_name'])) {
                        echo '<div style="margin-top:8px;"><span class="ldkp-pill approved">Ersatz Ã¼bernommen von '.esc_html($activeReplacement['assigned_name']).'</span></div>';
                    }
                    if (!empty($suggestions)) {
                        echo '<div style="margin-top:10px;"><strong>Passende Mitarbeiter</strong></div>';
                        foreach ($suggestions as $suggestion) {
                            echo '<div class="ldkp-line-row replacement"><span><strong>'.esc_html($suggestion['name']).'</strong></span><span>'.esc_html($suggestion['area']).'</span><span>'.esc_html($suggestion['status']).'</span><span>'.esc_html($suggestion['contact'] !== '' ? $suggestion['contact'] : 'Kein Kontakt').'</span></div>';
                        }
                    }
                    if ($this->can_view_all($user)) {
                        $manualEmployees = $this->filter_unsent_replacement_employees($this->get_free_employees_for_manual_request($row), $activeReplacement);
                        if ($activeReplacement && !empty($activeReplacement['candidate_keys'])) {
                            echo '<div class="ldkp-status ldkp-success ldkp-requested-note" data-requested-note data-replacement-id="'.esc_attr($activeReplacement['id']).'" style="margin-top:10px;">'.esc_html($this->get_requested_replacement_label($activeReplacement)).'</div>';
                        }
                        if (!$activeReplacement || empty($activeReplacement['all_sent'])) {
                            echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="margin-top:10px;">';
                            echo '<input type="hidden" name="action" value="ldk_portal_send_replacement_request"><input type="hidden" name="request_id" value="'.esc_attr($r['id']).'">';
                            wp_nonce_field('ldk_portal_send_replacement_request');
                            echo '<div class="ldkp-row" style="margin-bottom:8px;"><button class="ldkp-btn light" type="submit">Anfrage an alle passenden Ersatz-Mitarbeiter senden</button></div>';
                            echo '</form>';
                        }
                        echo '<div data-manual-request-wrap style="margin-top:10px;padding:12px;border:1px solid #e5e7eb;border-radius:12px;background:#f9fafb;">';
                        echo '<div style="font-weight:700;margin-bottom:8px;">Oder Mitarbeiter manuell auswÃ¤hlen</div>';
                        if (empty($manualEmployees)) {
                            echo '<div class="ldkp-muted">Alle passenden Mitarbeiter wurden bereits angefragt.</div>';
                        } else {
                            echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'">';
                            echo '<input type="hidden" name="action" value="ldk_portal_send_replacement_request"><input type="hidden" name="request_id" value="'.esc_attr($r['id']).'">';
                            wp_nonce_field('ldk_portal_send_replacement_request');
                            echo '<input type="text" class="ldkp-input" data-employee-search placeholder="Mitarbeiter suchen..." style="margin-bottom:8px;">';
                            echo '<select class="ldkp-select" name="selected_employee_key" data-manual-request-select>';
                            echo '<option value="">Mitarbeiter auswÃ¤hlen</option>';
                            foreach ($manualEmployees as $manualEmp) {
                                $label = $manualEmp['name'].' | '.$manualEmp['area'].' | '.$manualEmp['status'];
                                if (!empty($manualEmp['contact'])) $label .= ' | '.$manualEmp['contact'];
                                echo '<option value="'.esc_attr($manualEmp['key']).'">'.esc_html($label).'</option>';
                            }
                            echo '</select>';
                            echo '<div class="ldkp-row" style="margin-top:8px;"><button class="ldkp-btn green" type="submit">Anfrage an ausgewÃ¤hlten Mitarbeiter senden</button></div>';
                            echo '</form>';
                        }
                        echo '</div>';
                    }
                    if ($activeReplacement && !empty($activeReplacement['responses'])) {
                        echo '<div style="margin-top:12px;"><strong>RÃ¼ckmeldungen</strong></div>';
                        foreach ($activeReplacement['responses'] as $candKey => $response) {
                            $respStatus = isset($response['status']) ? $response['status'] : '';
                            $respLabel = $respStatus === 'accepted' ? 'Kann Ã¼bernehmen' : 'Kann nicht Ã¼bernehmen';
                            $pillClass = $respStatus === 'accepted' ? 'approved' : 'rejected';
                            echo '<div class="ldkp-line-row replacement" data-replacement-response-row><span><strong>'.esc_html($response['name']).'</strong></span><span><span class="ldkp-pill '.esc_attr($pillClass).'">'.esc_html($respLabel).'</span></span><span>'.esc_html($response['responded_at']).'</span><span>';
                            if ($respStatus === 'accepted' && empty($activeReplacement['assigned_key'])) {
                                echo '<button type="button" class="ldkp-btn green" data-assign-replacement="'.esc_attr($activeReplacement['id']).'" data-candidate-key="'.esc_attr($candKey).'">Akzeptieren</button>';
                            } elseif ($respStatus === 'accepted' && !empty($activeReplacement['assigned_key']) && $activeReplacement['assigned_key'] === $candKey) {
                                echo '<span class="ldkp-pill approved">AusgewÃ¤hlt</span>';
                            } else {
                                echo '<span class="ldkp-muted">â</span>';
                            }
                            echo '</span></div>';
                        }
                    }
                    echo '</div>';
                }
            }
            echo '</div>';
        }

        echo '</div>';
        return ob_get_clean();
    }

    private function render_plan_section($rows, $title, $allow_filters = true) {
        $days = $this->group_days($rows);
        $employees = array(); $dates = array(); $months = array();
        foreach ($rows as $r) {
            if (!empty($r['name'])) $employees[$r['name']] = $r['name'];
            if (!empty($r['date'])) { $dates[$r['date']] = $r['date']; $mk = $this->get_month_key($r['date']); if ($mk) $months[$mk] = $mk; }
        }
        natcasesort($employees); natcasesort($dates); natcasesort($months);
        ob_start();
        echo '<div class="ldkp-card ldkp-plan-card" data-plan-filters><div class="ldkp-plan-render" data-plan-render><div class="ldkp-day-header"><div class="ldkp-subtitle">'.esc_html($title).'</div><div class="ldkp-filter-count" data-filter-count></div></div>';
        if ($allow_filters) {
            echo '<div class="ldkp-filterbar"><div><label class="ldkp-inline-label">Ansicht</label><select class="ldkp-select" data-plan-mode><option value="day">Tagesplan</option><option value="month">Monatsplan</option></select></div>';
            echo '<div><label class="ldkp-inline-label">Von</label><select class="ldkp-select" data-plan-date-from><option value="">Alle Tage</option>'; foreach ($dates as $d) echo '<option value="'.esc_attr($d).'">'.esc_html($d).'</option>'; echo '</select></div>';
            echo '<div><label class="ldkp-inline-label">Bis</label><select class="ldkp-select" data-plan-date-to><option value="">Alle Tage</option>'; foreach ($dates as $d) echo '<option value="'.esc_attr($d).'">'.esc_html($d).'</option>'; echo '</select></div>';
            echo '<div><label class="ldkp-inline-label">Monat</label><select class="ldkp-select" data-plan-month><option value="">Alle Monate</option>'; foreach ($months as $m) echo '<option value="'.esc_attr($m).'">'.esc_html($m).'</option>'; echo '</select></div>';
            echo '<div><label class="ldkp-inline-label">Mitarbeiter</label><select class="ldkp-select" data-plan-employee><option value="">Alle Mitarbeiter</option>'; foreach ($employees as $name) echo '<option value="'.esc_attr($name).'">'.esc_html($name).'</option>'; echo '</select></div>';
            echo '<div><label class="ldkp-inline-label">Suche</label><input class="ldkp-input" data-plan-search placeholder="Name, Bereich, Schicht..."></div><div class="ldkp-head-actions"><button type="button" class="ldkp-btn light" data-plan-print>Drucken / PDF</button></div></div>';
        }
        if (empty($days)) {
            echo '<div class="ldkp-empty">Kein Plan vorhanden.</div></div>';
            return ob_get_clean();
        }
        foreach ($days as $date => $items) {
            $areas = array();
            foreach ($items as $row) { $area = !empty($row['area']) ? $row['area'] : 'Unbekannt'; $areas[$area][] = $row; }
            echo '<div class="ldkp-day" data-plan-day data-date="'.esc_attr($date).'" data-month="'.esc_attr($this->get_month_key($date)).'">';
            echo '<div class="ldkp-date">'.esc_html($this->format_date_with_weekday($date)).'</div>';
            foreach ($areas as $area => $areaRows) {
                echo '<div class="ldkp-area">'.esc_html($area).'</div>';
                echo '<div class="ldkp-table-wrap"><table class="ldkp-table ldkp-manage-table"><thead><tr><th>Mitarbeiter</th><th>Status</th><th>Schicht</th><th>Start</th><th>Ende</th><th>Pause</th></tr></thead><tbody>';
                foreach ($areaRows as $row) {
                    echo '<tr data-plan-row data-name="'.esc_attr($row['name']).'"><td>'.esc_html($row['name']).'</td><td><span class="ldkp-pill '.esc_attr($this->status_class($row['status'])).'">'.esc_html($this->status_label($row['status'])).'</span></td><td>'.esc_html($row['shift']).'</td><td>'.esc_html($row['start']).'</td><td>'.esc_html($row['end']).'</td><td>'.esc_html($row['break']).'</td></tr>';
                }
                echo '</tbody></table></div>';
                foreach ($areaRows as $row) {
                    echo '<div class="ldkp-mobile-item" data-plan-row data-name="'.esc_attr($row['name']).'"><div class="ldkp-row" style="justify-content:space-between;"><strong>'.esc_html($row['name']).'</strong><span class="ldkp-pill '.esc_attr($this->status_class($row['status'])).'">'.esc_html($this->status_label($row['status'])).'</span></div><div class="ldkp-grid" style="margin-top:10px;"><div class="ldkp-col-6"><div class="ldkp-small">Schicht</div><div>'.esc_html($row['shift']).'</div></div><div class="ldkp-col-6"><div class="ldkp-small">Bereich</div><div>'.esc_html($row['area']).'</div></div><div class="ldkp-col-6"><div class="ldkp-small">Start</div><div>'.esc_html($row['start']).'</div></div><div class="ldkp-col-6"><div class="ldkp-small">Ende</div><div>'.esc_html($row['end']).'</div></div><div class="ldkp-col-6"><div class="ldkp-small">Pause</div><div>'.esc_html($row['break']).'</div></div></div></div>';
                }
            }
            echo '</div>';
        }
        echo '</div></div>';
        return ob_get_clean();
    }

    private function format_row_summary($row) {
        if (!$row || !is_array($row)) return '-';
        return trim(implode(' | ', array_filter(array(
            isset($row['status']) ? $this->status_label($row['status']) : '',
            isset($row['area']) ? $row['area'] : '',
            isset($row['shift']) ? $row['shift'] : '',
            ((isset($row['start']) ? $row['start'] : '') || (isset($row['end']) ? $row['end'] : '')) ? trim((isset($row['start']) ? $row['start'] : '') . ' - ' . (isset($row['end']) ? $row['end'] : '')) : '',
            isset($row['break']) ? ('Pause ' . $row['break']) : '',
        ))));
    }

    private function render_request_form() {
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">FreiwÃ¼nsche / Urlaub</div><div class="ldkp-muted" style="margin-bottom:14px;">Hier reichst du Wunschfrei und Urlaub ein. Die Krankmeldung ist separat.</div><form method="post" action="'.esc_url(admin_url('admin-post.php')).'" enctype="multipart/form-data"><input type="hidden" name="action" value="ldk_portal_save_request">';
        wp_nonce_field('ldk_portal_save_request');
        echo '<div class="ldkp-grid"><div class="ldkp-col-4"><label class="ldkp-inline-label">Option</label><select name="request_type" id="ldkp-request-type" class="ldkp-select"><option value="wunschfrei">Wunschfrei</option><option value="urlaub">Urlaub</option></select></div><div class="ldkp-col-4 ldkp-hidden" id="ldkp-vacation-box"><label class="ldkp-inline-label">Urlaubsart</label><select name="vacation_type" class="ldkp-select"><option value="paid">Bezahlter Urlaub</option><option value="unpaid">Unbezahlter Urlaub</option></select></div><div class="ldkp-col-4"><label class="ldkp-inline-label">Von</label><input type="date" name="date_from" class="ldkp-input" required></div><div class="ldkp-col-4"><label class="ldkp-inline-label">Bis</label><input type="date" name="date_to" class="ldkp-input" required></div><div class="ldkp-col-12"><label class="ldkp-inline-label">BegrÃ¼ndung</label><textarea name="reason" class="ldkp-textarea" required></textarea></div><div class="ldkp-col-12"><button class="ldkp-btn">Speichern</button></div></div></form></div>';
        return ob_get_clean();
    }

    private function render_request_center($user) {
        if (!$this->can_manage_all($user)) {
            return $this->render_request_form();
        }
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">FreiwÃ¼nsche / Urlaub</div><div class="ldkp-muted" style="margin-bottom:14px;">Als Haupt-Manager kannst du hier eigene EintrÃ¤ge erstellen und alle eingereichten Urlaubs- und Wunschfrei-AntrÃ¤ge zentral prÃ¼fen.</div>';
        echo '<div class="ldkp-row" style="gap:10px;flex-wrap:wrap;margin-bottom:14px;"><button type="button" class="ldkp-btn light active" data-request-subtab-open="form">Eigener Eintrag</button><button type="button" class="ldkp-btn light" data-request-subtab-open="requests">AntrÃ¤ge</button></div>';
        echo '<div data-request-subtab="form">'.$this->render_request_form().'</div>';
        echo '<div data-request-subtab="requests" class="ldkp-hidden">'.$this->render_request_overview_admin($user).'</div>';
        echo '</div>';
        return ob_get_clean();
    }

    private function render_request_overview_admin($user) {
        if (!$this->can_manage_all($user)) return '';
        $urlaub = $this->get_request_rows_for_overview('urlaub');
        $wunschfrei = $this->get_request_rows_for_overview('wunschfrei');
        $urlaub_url = wp_nonce_url(admin_url('admin-post.php?action=ldk_portal_print_requests_pdf&type=urlaub'), 'ldk_portal_print_requests_pdf');
        $wunschfrei_url = wp_nonce_url(admin_url('admin-post.php?action=ldk_portal_print_requests_pdf&type=wunschfrei'), 'ldk_portal_print_requests_pdf');
        ob_start();
        echo '<div class="ldkp-row" style="gap:16px;flex-wrap:wrap;">';
        echo '<div class="ldkp-card" style="flex:1 1 100%;"><div class="ldkp-row" style="justify-content:space-between;align-items:center;gap:12px;"><div><div class="ldkp-subtitle" style="margin-bottom:4px;">Urlaub AntrÃ¤ge</div><div class="ldkp-muted">Alle UrlaubsantrÃ¤ge von allen Mitarbeitern.</div></div><a class="ldkp-btn" href="'.esc_url($urlaub_url).'" target="_blank" rel="noopener">Als PDF drucken</a></div><div style="margin-top:12px;">'.$this->render_request_type_table($urlaub, 'urlaub').'</div></div>';
        echo '<div class="ldkp-card" style="flex:1 1 100%;"><div class="ldkp-row" style="justify-content:space-between;align-items:center;gap:12px;"><div><div class="ldkp-subtitle" style="margin-bottom:4px;">WÃ¼nschfrei AntrÃ¤ge</div><div class="ldkp-muted">Alle Wunschfrei-AntrÃ¤ge von allen Mitarbeitern.</div></div><a class="ldkp-btn" href="'.esc_url($wunschfrei_url).'" target="_blank" rel="noopener">Als PDF drucken</a></div><div style="margin-top:12px;">'.$this->render_request_type_table($wunschfrei, 'wunschfrei').'</div></div>';
        echo '</div>';
        return ob_get_clean();
    }

    private function get_request_rows_for_overview($type) {
        $rows = array_values(array_filter($this->get_requests(), function($r) use ($type) {
            return (($r['request_type'] ?? '') === $type);
        }));
        usort($rows, function($a, $b) {
            return strcmp((string)($b['created_at'] ?? ''), (string)($a['created_at'] ?? ''));
        });
        return $rows;
    }

    private function render_request_type_table($requests, $type) {
        ob_start();
        echo '<div class="ldkp-table-wrap"><table class="ldkp-table"><thead><tr><th>Name</th><th>Rolle</th><th>Typ</th><th>Von</th><th>Bis</th><th>Tage</th><th>Status</th><th>BegrÃ¼ndung</th></tr></thead><tbody>';
        if (empty($requests)) {
            echo '<tr><td colspan="8"><span class="ldkp-muted">Keine EintrÃ¤ge vorhanden.</span></td></tr>';
        } else {
            foreach ($requests as $r) {
                $status = isset($r['approval_status']) ? $r['approval_status'] : 'open';
                echo '<tr>';
                echo '<td><strong>'.esc_html($r['employee_name'] ?? '').'</strong></td>';
                echo '<td>'.esc_html($this->role_label($r['role'] ?? 'employee')).'</td>';
                echo '<td>'.esc_html($this->request_type_label($r)).'</td>';
                echo '<td>'.esc_html($this->format_date_with_weekday($r['date_from'] ?? '')).'</td>';
                echo '<td>'.esc_html($this->format_date_with_weekday($r['date_to'] ?? '')).'</td>';
                echo '<td>'.intval($this->calculate_request_days($r)).'</td>';
                echo '<td><span class="ldkp-pill '.esc_attr($status).'">'.esc_html($this->request_status_label($status)).'</span></td>';
                echo '<td>'.esc_html($r['reason'] ?? '').'</td>';
                echo '</tr>';
            }
        }
        echo '</tbody></table></div>';
        return ob_get_clean();
    }

    public function print_requests_pdf() {
        $user = $this->current_portal_user();
        if (!$this->can_manage_all($user)) wp_die('Keine Berechtigung.');
        check_admin_referer('ldk_portal_print_requests_pdf');
        $type = sanitize_key(isset($_GET['type']) ? wp_unslash($_GET['type']) : '');
        if (!in_array($type, array('urlaub', 'wunschfrei'), true)) wp_die('UngÃ¼ltiger Typ.');
        $label = $type === 'urlaub' ? 'Urlaub AntrÃ¤ge' : 'WÃ¼nschfrei AntrÃ¤ge';
        $requests = $this->get_request_rows_for_overview($type);
        nocache_headers();
        header('Content-Type: text/html; charset=' . get_bloginfo('charset'));
        echo '<!doctype html><html><head><meta charset="'.esc_attr(get_bloginfo('charset')).'"><title>'.esc_html($label).'</title><style>body{font-family:Arial,sans-serif;color:#111;padding:24px}h1{font-size:26px;margin:0 0 8px}p{margin:0 0 16px;color:#555}table{width:100%;border-collapse:collapse;font-size:12px}th,td{border:1px solid #d1d5db;padding:8px;vertical-align:top;text-align:left}th{background:#f3f4f6} .meta{margin-bottom:18px;font-size:12px;color:#444} .pill{display:inline-block;padding:3px 8px;border-radius:999px;border:1px solid #d1d5db;font-size:11px}</style></head><body>';
        echo '<h1>'.esc_html($label).'</h1>';
        echo "<p>L'Osteria Mitarbeiter Portal â Ausdruck fÃ¼r Haupt-Manager</p>";
        echo '<div class="meta">Erstellt am '.esc_html(wp_date('d.m.Y H:i')).' â¢ Anzahl EintrÃ¤ge: '.intval(count($requests)).'</div>';
        echo '<table><thead><tr><th>Name</th><th>Rolle</th><th>Typ</th><th>Von</th><th>Bis</th><th>Tage</th><th>Status</th><th>BegrÃ¼ndung</th></tr></thead><tbody>';
        if (empty($requests)) {
            echo '<tr><td colspan="8">Keine EintrÃ¤ge vorhanden.</td></tr>';
        } else {
            foreach ($requests as $r) {
                $status = isset($r['approval_status']) ? $r['approval_status'] : 'open';
                echo '<tr>';
                echo '<td>'.esc_html($r['employee_name'] ?? '').'</td>';
                echo '<td>'.esc_html($this->role_label($r['role'] ?? 'employee')).'</td>';
                echo '<td>'.esc_html($this->request_type_label($r)).'</td>';
                echo '<td>'.esc_html($this->format_date_with_weekday($r['date_from'] ?? '')).'</td>';
                echo '<td>'.esc_html($this->format_date_with_weekday($r['date_to'] ?? '')).'</td>';
                echo '<td>'.intval($this->calculate_request_days($r)).'</td>';
                echo '<td>'.esc_html($this->request_status_label($status)).'</td>';
                echo '<td>'.esc_html($r['reason'] ?? '').'</td>';
                echo '</tr>';
            }
        }
        echo '</tbody></table><script>window.onload=function(){window.print();};</script></body></html>';
        exit;
    }

    private function get_user_sick_requests($user) {
        $requests = array_values(array_filter($this->get_requests(), function($r) use ($user){
            return isset($r['employee_key']) && $r['employee_key'] === $user['key'] && (($r['request_type'] ?? '') === 'krankmeldung');
        }));
        usort($requests, function($a, $b){ return strcmp($b['created_at'] ?? '', $a['created_at'] ?? ''); });
        return $requests;
    }

    private function get_user_total_sick_days($user) {
        $total = 0;
        foreach ($this->get_user_sick_requests($user) as $r) {
            $from = !empty($r['date_from']) ? strtotime($r['date_from']) : false;
            $to = !empty($r['date_to']) ? strtotime($r['date_to']) : false;
            if (!$from || !$to || $to < $from) continue;
            $days = (int) floor(($to - $from) / DAY_IN_SECONDS) + 1;
            $total += max(1, $days);
        }
        return $total;
    }

    private function calculate_request_days($request) {
        $from = !empty($request['date_from']) ? strtotime($request['date_from']) : false;
        $to = !empty($request['date_to']) ? strtotime($request['date_to']) : false;
        if (!$from || !$to || $to < $from) return 0;
        return (int) floor(($to - $from) / DAY_IN_SECONDS) + 1;
    }

    private function get_sick_summary_rows() {
        $requests = $this->get_requests_by_type('krankmeldung');
        $employees = $this->get_employees();
        $by_name = array();
        foreach ($requests as $r) {
            $name = isset($r['employee_name']) ? $r['employee_name'] : '';
            if ($name === '') continue;
            $days = $this->calculate_request_days($r);
            if (!isset($by_name[$name])) {
                $by_name[$name] = array('name' => $name, 'station' => '', 'total_days' => 0, 'entries' => array());
                foreach ($employees as $emp) {
                    if ($this->same_name($emp['name'] ?? '', $name)) {
                        $by_name[$name]['station'] = trim((string)($emp['primary_area'] ?? ''));
                        break;
                    }
                }
            }
            $by_name[$name]['total_days'] += $days;
            $by_name[$name]['entries'][] = array('from' => $r['date_from'] ?? '', 'to' => $r['date_to'] ?? '', 'reason' => $r['reason'] ?? '', 'days' => $days, 'status' => $r['approval_status'] ?? 'open', 'document' => $r);
        }
        uasort($by_name, function($a,$b){
            if (($b['total_days'] ?? 0) === ($a['total_days'] ?? 0)) return strcasecmp($a['name'] ?? '', $b['name'] ?? '');
            return ($b['total_days'] ?? 0) <=> ($a['total_days'] ?? 0);
        });
        return $by_name;
    }

    private function render_sick_history_table($entries) {
        if (empty($entries)) return '<div class="ldkp-empty">Keine Kranktage vorhanden.</div>';
        ob_start();
        echo '<div class="ldkp-table-wrap ldkp-sick-days-table"><table class="ldkp-table"><thead><tr><th>Datum von</th><th>Datum bis</th><th>BegrÃ¼ndung</th><th>Tage</th></tr></thead><tbody>';
        foreach ($entries as $entry) {
            echo '<tr><td>'.esc_html($this->format_date_with_weekday($entry['from'])).'</td><td>'.esc_html($this->format_date_with_weekday($entry['to'])).'</td><td>'.esc_html($entry['reason']).'</td><td>'.intval($entry['days']).'</td></tr>';
        }
        echo '</tbody></table></div>';
        return ob_get_clean();
    }

    private function render_employee_sick_summary($user) {
        $requests = $this->get_user_sick_requests($user);
        $entries = array();
        foreach ($requests as $r) {
            $entries[] = array('from' => $r['date_from'] ?? '', 'to' => $r['date_to'] ?? '', 'reason' => $r['reason'] ?? '', 'days' => $this->calculate_request_days($r));
        }
        $total_days = 0; foreach ($entries as $entry) $total_days += (int) $entry['days'];
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-row" style="justify-content:space-between;align-items:center;"><div><div class="ldkp-subtitle">Krankgeschriebene Tage</div><div class="ldkp-muted">Ãbersicht deiner bisherigen AU-ZeitrÃ¤ume.</div></div><span class="ldkp-pill rejected">'.intval($total_days).' Tage gesamt</span></div>';
        echo '<div style="margin-top:14px;">'.$this->render_sick_history_table($entries).'</div>';
        echo '</div>';
        return ob_get_clean();
    }

    private function render_sick_leave_form() {
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">Sofort Krankmelden</div><form method="post" action="'.esc_url(admin_url('admin-post.php')).'" enctype="multipart/form-data"><input type="hidden" name="action" value="ldk_portal_save_request"><input type="hidden" name="request_type" value="krankmeldung">';
        wp_nonce_field('ldk_portal_save_request');
        echo '<div class="ldkp-grid"><div class="ldkp-col-6"><label class="ldkp-inline-label">Von</label><input type="date" name="date_from" class="ldkp-input" required></div><div class="ldkp-col-6"><label class="ldkp-inline-label">Bis</label><input type="date" name="date_to" class="ldkp-input" required></div><div class="ldkp-col-12"><label class="ldkp-inline-label">BegrÃ¼ndung</label><textarea name="reason" class="ldkp-textarea" required placeholder="z. B. heute krank, Arzttermin, AU folgt..."></textarea></div><div class="ldkp-col-12"><label class="ldkp-inline-label">Krankmeldung-Dokument (optional direkt mitsenden)</label><input type="file" name="attachment" class="ldkp-input" accept=".pdf,.jpg,.jpeg,application/pdf,image/jpeg"></div><div class="ldkp-col-12"><button class="ldkp-btn red">Jetzt krankmelden</button></div></div></form></div>';
        return ob_get_clean();
    }

    private function render_own_sick_requests($user) {
        $requests = $this->get_user_sick_requests($user);
        $total_days = $this->get_user_total_sick_days($user);
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-row" style="justify-content:space-between;align-items:center;"><div><div class="ldkp-subtitle">Meine Krankmeldungs-AntrÃ¤ge</div><div class="ldkp-muted">Hier siehst du alle bereits eingereichten Krankmeldungen.</div></div><span class="ldkp-pill rejected">'.intval($total_days).' Tag'.($total_days === 1 ? '' : 'e').' krankgeschrieben</span></div>';
        if (empty($requests)) {
            echo '<div class="ldkp-status" style="margin-top:14px;">Noch keine Krankmeldungen vorhanden.</div>';
        } else {
            foreach ($requests as $r) {
                $status = isset($r['approval_status']) ? $r['approval_status'] : 'open';
                $from = !empty($r['date_from']) ? strtotime($r['date_from']) : false;
                $to = !empty($r['date_to']) ? strtotime($r['date_to']) : false;
                $days = ($from && $to && $to >= $from) ? ((int) floor(($to - $from) / DAY_IN_SECONDS) + 1) : 1;
                echo '<div class="ldkp-notice"><div class="ldkp-row" style="justify-content:space-between;"><div><strong>Krankmeldung</strong> <span class="ldkp-pill '.esc_attr($status).'">'.esc_html($this->request_status_label($status)).'</span><div class="ldkp-muted">'.esc_html($this->format_date_with_weekday($r['date_from'])).' bis '.esc_html($this->format_date_with_weekday($r['date_to'])).' Â· '.intval($days).' Tag'.($days === 1 ? '' : 'e').'</div></div>';
                if ($this->can_manage_all($user)) {
                    echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="margin:0" onsubmit="return confirm(&quot;Eintrag wirklich lÃ¶schen?&quot;)"><input type="hidden" name="action" value="ldk_portal_delete_request"><input type="hidden" name="request_id" value="'.esc_attr($r['id']).'">';
                    wp_nonce_field('ldk_portal_delete_request');
                    echo '<button class="ldkp-btn light">LÃ¶schen</button></form>';
                }
                echo '</div><div style="margin-top:8px;">'.nl2br(esc_html($r['reason'])).'</div>';
                echo '<div class="ldkp-doc-row" style="margin-top:10px;"><strong>Dokument:</strong> '.$this->request_attachment_link($r, 'Dokument Ã¶ffnen').'</div>';
                if (!$this->request_has_attachment($r)) {
                    echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" enctype="multipart/form-data" class="ldkp-grid" style="margin-top:12px;"><input type="hidden" name="action" value="ldk_portal_upload_request_document"><input type="hidden" name="request_id" value="'.esc_attr($r['id']).'">';
                    wp_nonce_field('ldk_portal_upload_request_document');
                    echo '<div class="ldkp-col-8"><input type="file" name="attachment" class="ldkp-input" accept=".pdf,.jpg,.jpeg,application/pdf,image/jpeg" required></div><div class="ldkp-col-4"><button class="ldkp-btn light" style="width:100%;">Dokument nachreichen</button></div></form>';
                }
                echo '</div>';
            }
        }
        echo '</div>';
        return ob_get_clean();
    }

    private function render_sick_leave_section($user) {
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">Krankmeldung</div><div class="ldkp-row" style="margin-top:8px;gap:12px;"><button type="button" class="ldkp-btn red" data-sick-tab-target="form">1. Sofort Krankmelden</button><button type="button" class="ldkp-btn light" data-sick-tab-target="list">2. Meine Krankmeldungs-AntrÃ¤ge sehen</button><button type="button" class="ldkp-btn light" data-sick-tab-target="summary">3. Krankgeschriebene Tage</button></div></div>';
        echo '<div data-sick-tab="form" style="margin-top:14px;">'.$this->render_sick_leave_form().'</div>';
        echo '<div data-sick-tab="list" class="ldkp-hidden" style="margin-top:14px;">'.$this->render_own_sick_requests($user).'</div>';
        echo '<div data-sick-tab="summary" class="ldkp-hidden" style="margin-top:14px;">'.$this->render_employee_sick_summary($user).'</div>';
        return ob_get_clean();
    }

    private function render_own_requests($user) {
        $requests = array_values(array_filter($this->get_requests(), function($r) use ($user){ return isset($r['employee_key']) && $r['employee_key'] === $user['key'] && (($r['request_type'] ?? '') !== 'head_message') && (($r['request_type'] ?? '') !== 'krankmeldung'); }));
        usort($requests, function($a, $b){ return strcmp($b['created_at'], $a['created_at']); });
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">Meine AntrÃ¤ge</div>';
        if (empty($requests)) {
            echo '<div class="ldkp-status">Noch keine EintrÃ¤ge vorhanden.</div>';
        } else {
            foreach ($requests as $r) {
                $status = isset($r['approval_status']) ? $r['approval_status'] : 'open';
                echo '<div class="ldkp-notice"><div class="ldkp-row" style="justify-content:space-between;"><div><strong>'.esc_html($this->request_type_label($r)).'</strong> <span class="ldkp-pill '.esc_attr($status).'">'.esc_html($this->request_status_label($status)).'</span><div class="ldkp-muted">'.esc_html($this->format_date_with_weekday($r['date_from'])).' bis '.esc_html($this->format_date_with_weekday($r['date_to'])).'</div></div><form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="margin:0" onsubmit="return confirm(&quot;Eintrag wirklich lÃ¶schen?&quot;)"><input type="hidden" name="action" value="ldk_portal_delete_request"><input type="hidden" name="request_id" value="'.esc_attr($r['id']).'">';
                wp_nonce_field('ldk_portal_delete_request');
                echo '<button class="ldkp-btn light">LÃ¶schen</button></form></div><div style="margin-top:8px;">'.nl2br(esc_html($r['reason'])).'</div></div>';
            }
        }
        echo '</div>';
        return ob_get_clean();
    }

    private function render_approvals($user) {
        if (!$this->can_manage_all($user)) return '';
        $requests = $this->get_requests();
        usort($requests, function($a, $b){ return strcmp($b['created_at'], $a['created_at']); });
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">Genehmigungen</div>';
        if (empty($requests)) {
            echo '<div class="ldkp-status">Keine AntrÃ¤ge vorhanden.</div>';
        } else {
            echo '<div class="ldkp-summary-grid"><div class="ldkp-summary-card" data-approval-summary="open"><span class="ldkp-small">Offen</span><strong>'.intval(count(array_filter($requests,function($r){ return (($r['approval_status'] ?? 'open') === 'open'); }))).'</strong></div><div class="ldkp-summary-card" data-approval-summary="approved"><span class="ldkp-small">Genehmigt</span><strong>'.intval(count(array_filter($requests,function($r){ return (($r['approval_status'] ?? '') === 'approved'); }))).'</strong></div><div class="ldkp-summary-card" data-approval-summary="rejected"><span class="ldkp-small">Nicht genehmigt</span><strong>'.intval(count(array_filter($requests,function($r){ return (($r['approval_status'] ?? '') === 'rejected'); }))).'</strong></div><div class="ldkp-summary-card" data-approval-summary="closed"><span class="ldkp-small">Erledigt</span><strong>'.intval(count(array_filter($requests,function($r){ return (($r['approval_status'] ?? '') === 'closed'); }))).'</strong></div></div><div class="ldkp-table-wrap ldkp-approvals-table"><table class="ldkp-table"><thead><tr><th>Name</th><th>Rolle</th><th>Typ</th><th>Zeitraum</th><th>BegrÃ¼ndung</th><th>Dokument</th><th>Status</th><th>Aktion</th></tr></thead><tbody>';
            foreach ($requests as $r) {
                $status = isset($r['approval_status']) ? $r['approval_status'] : 'open';
                echo '<tr data-request-row><td><strong>'.esc_html($r['employee_name']).'</strong></td><td>'.esc_html($this->role_label(isset($r['role'])?$r['role']:'employee')).'</td><td><div class="ldkp-approval-type">'.esc_html($this->request_type_label($r)).'</div><div class="ldkp-approval-sub">'.intval($this->calculate_request_days($r)).' Tage</div></td><td><strong>'.esc_html($this->format_date_with_weekday($r['date_from'])).'</strong><div class="ldkp-approval-sub">bis '.esc_html($this->format_date_with_weekday($r['date_to'])).'</div></td><td>'.esc_html($r['reason']).'</td><td>'.($this->request_has_attachment($r) ? str_replace('ldkp-btn light', 'ldkp-doc-chip', $this->request_attachment_link($r, 'Dokument Ã¶ffnen')) : '<span class="ldkp-muted">Kein Dokument</span>').'</td><td><span data-role="status-pill" class="ldkp-pill '.esc_attr($status).'">'.esc_html($this->request_status_label($status)).'</span></td><td class="ldkp-actions-col"><button type="button" class="ldkp-btn green '.($status==='approved'?'is-active':'').'" data-request-id="'.esc_attr($r['id']).'" data-request-status="approved">Genehmigt</button><button type="button" class="ldkp-btn red '.($status==='rejected'?'is-active':'').'" data-request-id="'.esc_attr($r['id']).'" data-request-status="rejected">Nicht genehmigt</button><button type="button" class="ldkp-btn light '.($status==='open'?'is-active':'').'" data-request-id="'.esc_attr($r['id']).'" data-request-status="open">Offen</button><button type="button" class="ldkp-btn light '.($status==='closed'?'is-active':'').'" data-request-id="'.esc_attr($r['id']).'" data-request-status="closed">Erledigt</button><button type="button" class="ldkp-btn red" data-request-delete="'.esc_attr($r['id']).'">LÃ¶schen</button></td></tr>';
            }
            echo '</tbody></table></div>';
        }
        echo '</div>';
        return ob_get_clean();
    }


    private function render_sick_requests($user) {
        if (!$this->can_view_all($user)) return '';
        $today = current_time('Y-m-d');
        $requests = $this->get_requests_by_type('krankmeldung');
        usort($requests, function($a, $b){ return strcmp(isset($b['created_at']) ? $b['created_at'] : '', isset($a['created_at']) ? $a['created_at'] : ''); });
        $summaryRows = $this->get_sick_summary_rows();
        $totalDays = 0; foreach ($summaryRows as $row) { $totalDays += (int) ($row['total_days'] ?? 0); }
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-row" style="justify-content:space-between;align-items:center;"><div><div class="ldkp-subtitle">AU & Meldungen</div><div class="ldkp-muted">Ãbersicht aller gemeldeten Kranktage und einzelnen AU-ZeitrÃ¤ume.</div></div><span class="ldkp-pill rejected">'.intval($totalDays).' Kranktage gesamt</span></div>';
        echo '<div class="ldkp-sick-overview"><div class="ldkp-sick-stat-card"><div class="ldkp-small">Mitarbeiter mit AU</div><div class="ldkp-sick-stat-total">'.intval(count($summaryRows)).'</div><div class="ldkp-muted" style="margin-top:6px;">Gesamte Ãbersicht nach Namen, Station und AU-Tagen.</div></div><div class="ldkp-table-wrap"><table class="ldkp-table"><thead><tr><th>Name</th><th>Station</th><th>Gesamt Krankheitstage</th></tr></thead><tbody>';
        foreach ($summaryRows as $row) {
            echo '<tr><td><strong>'.esc_html($row['name']).'</strong></td><td>'.esc_html($row['station'] !== '' ? $row['station'] : 'â').'</td><td>'.intval($row['total_days']).'</td></tr>';
        }
        if (empty($summaryRows)) echo '<tr><td colspan="3"><span class="ldkp-muted">Keine Krankmeldungen vorhanden.</span></td></tr>';
        echo '</tbody></table></div></div>';
        foreach ($summaryRows as $row) {
            echo '<div class="ldkp-card" style="margin-top:14px;"><div class="ldkp-row" style="justify-content:space-between;align-items:center;"><div><div class="ldkp-subtitle" style="font-size:20px;margin-bottom:4px;">'.esc_html($row['name']).'</div><div class="ldkp-muted">Station: '.esc_html($row['station'] !== '' ? $row['station'] : 'â').'</div></div><span class="ldkp-pill rejected">'.intval($row['total_days']).' Tage</span></div><div style="margin-top:12px;">'.$this->render_sick_history_table($row['entries']).'</div></div>';
        }
        if (!empty($requests)) {
            echo '<div class="ldkp-card" style="margin-top:14px;"><div class="ldkp-subtitle">Aktuelle Einzelmeldungen</div><div class="ldkp-table-wrap"><table class="ldkp-table"><thead><tr><th>Name</th><th>Von</th><th>Bis</th><th>BegrÃ¼ndung</th><th>Tage</th><th>Dokument</th><th>Status</th><th>Heutiger geplanter Dienst</th></tr></thead><tbody>';
            foreach ($requests as $r) {
                $status = isset($r['approval_status']) ? $r['approval_status'] : 'open';
                $todayRow = null;
                if ($today >= ($r['date_from'] ?? '') && $today <= ($r['date_to'] ?? '')) {
                    $todayRow = $this->get_row_for_employee_and_date($r['employee_name'], date('d.m.Y', strtotime($today)));
                }
                echo '<tr><td>'.esc_html($r['employee_name']).'</td><td>'.esc_html($this->format_date_with_weekday($r['date_from'])).'</td><td>'.esc_html($this->format_date_with_weekday($r['date_to'])).'</td><td>'.esc_html($r['reason']).'</td><td>'.intval($this->calculate_request_days($r)).'</td><td>'.$this->request_attachment_link($r, 'Dokument Ã¶ffnen').'</td><td><span class="ldkp-pill '.esc_attr($status).'">'.esc_html($this->request_status_label($status)).'</span></td><td>';
                if ($todayRow) echo esc_html($todayRow['start'].' - '.$todayRow['end'].' | '.$todayRow['area'].' | '.$todayRow['shift']);
                else echo '<span class="ldkp-muted">Heute kein Dienst im Plan</span>';
                echo '</td></tr>';
            }
            echo '</tbody></table></div></div>';
        }
        echo '</div>';
        return ob_get_clean();
    }

    private function render_frontend_editor($user) {
        if (!$this->can_manage_all($user)) return '';
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">Frontend Verwaltung</div><div class="ldkp-muted">Haupt-Manager kann hier den Plan wie im Backend laden, filtern, bearbeiten, speichern, freigeben, lÃ¶schen und als CSV exportieren. PDF lÃ¤uft Ã¼ber Drucken / Als PDF speichern im Browser.</div><div id="ldkp-editor-wrap" style="margin-top:14px;">';
        echo '<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>';
        echo '<div class="ldkp-head-actions"><input type="file" data-editor-file accept=".xlsx,.xls,.csv"><button type="button" class="ldkp-btn light" data-editor-add>Zeile hinzufÃ¼gen</button><button type="button" class="ldkp-btn" data-editor-save>Entwurf speichern</button><button type="button" class="ldkp-btn green" data-editor-publish>Plan freigeben</button><button type="button" class="ldkp-btn light" data-editor-export>CSV exportieren</button><button type="button" class="ldkp-btn red" data-editor-delete>Plan lÃ¶schen</button></div><div class="ldkp-status" data-editor-status style="margin-top:12px;">Status wird geladen...</div><div class="ldkp-table-wrap"><table class="ldkp-table ldkp-editor-table"><thead><tr><th>Datum</th><th>Name</th><th>Status</th><th>Bereich</th><th>Schicht</th><th>Start</th><th>Ende</th><th>Pause</th><th>Aktion</th></tr></thead><tbody></tbody></table></div></div></div>';
        return ob_get_clean();
    }

    private function render_frontend_employee_management($user) {
        if (!$this->can_manage_all($user)) return '';
        $employees = $this->get_employee_display_list();
        ob_start();
        echo '<div class="ldkp-card"><div class="ldkp-subtitle">Mitarbeiter & PIN Verwaltung</div><div class="ldkp-muted">Hier kannst du Benutzer, Kontakt, Rolle, Hauptbereich und Zusatzbereiche im Frontend pflegen.</div>';
        echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" class="ldkp-employee-create"><input type="hidden" name="action" value="ldk_portal_create_employee">';
        wp_nonce_field('ldk_portal_create_employee');
        echo '<div class="ldkp-employee-create-head"><div><div class="ldkp-employee-create-title">Neuen Benutzer anlegen</div><div class="ldkp-employee-create-sub">Lege neue Mitarbeiter, Manager oder Haupt-Manager in einem klaren Formular an. Rolle, Kontakt und Bereiche kannst du direkt mit erfassen.</div></div><span class="ldkp-section-chip">Schnell anlegen</span></div>';
        echo '<div class="ldkp-grid"><div class="ldkp-col-4"><label class="ldkp-inline-label">Name</label><input type="text" name="employee_name" class="ldkp-input" placeholder="Vor- und Nachname" required><span class="ldkp-field-hint">So erscheint der Name spÃ¤ter im Portal und im Dienstplan.</span></div><div class="ldkp-col-4"><label class="ldkp-inline-label">PIN</label><input type="text" name="employee_pin" class="ldkp-input" inputmode="numeric" placeholder="z. B. 1234" required><span class="ldkp-field-hint">Nur Zahlen verwenden. Die PIN dient fÃ¼r den Mitarbeiter-Login.</span></div><div class="ldkp-col-4"><label class="ldkp-inline-label">Rolle</label><select name="employee_role" class="ldkp-select"><option value="employee">Mitarbeiter</option><option value="manager">Manager</option><option value="head_manager">Haupt-Manager</option></select><span class="ldkp-field-hint">Lege fest, welche Bereiche im Portal sichtbar sind.</span></div><div class="ldkp-col-12"><div class="ldkp-employee-divider"></div></div><div class="ldkp-col-3"><label class="ldkp-inline-label">Telefon</label><input type="text" name="employee_phone" class="ldkp-input" placeholder="Telefonnummer"></div><div class="ldkp-col-3"><label class="ldkp-inline-label">WhatsApp</label><input type="text" name="employee_whatsapp" class="ldkp-input" placeholder="WhatsApp Nummer"></div><div class="ldkp-col-3"><label class="ldkp-inline-label">Hauptbereich</label><input type="text" name="employee_primary_area" class="ldkp-input" placeholder="z. B. Service"></div><div class="ldkp-col-3"><label class="ldkp-inline-label">Zusatzbereiche</label><input type="text" name="employee_extra_areas" class="ldkp-input" placeholder="z. B. KÃ¼che, Bar"></div><div class="ldkp-col-12"><div class="ldkp-row" style="justify-content:space-between;align-items:center;"><span class="ldkp-muted">Du kannst die Daten spÃ¤ter jederzeit in der Liste darunter Ã¤ndern.</span><button class="ldkp-btn">Benutzer anlegen</button></div></div></div></form>';

        echo '<div class="ldkp-employee-filterbar" data-employee-filter-wrap><div class="ldkp-grid"><div class="ldkp-col-4"><label class="ldkp-inline-label">Suche</label><input type="text" class="ldkp-input" data-employee-filter-search placeholder="Name, Telefon, WhatsApp, Bereich..."></div><div class="ldkp-col-4"><label class="ldkp-inline-label">Rolle filtern</label><select class="ldkp-select" data-employee-filter-role><option value="">Alle Rollen</option><option value="Mitarbeiter">Mitarbeiter</option><option value="Manager">Manager</option><option value="Haupt-Manager">Haupt-Manager</option></select></div><div class="ldkp-col-4"><label class="ldkp-inline-label">Bereich filtern</label><input type="text" class="ldkp-input" data-employee-filter-area placeholder="z. B. Service, Bar, KÃ¼che"></div><div class="ldkp-col-12"><div class="ldkp-row" style="justify-content:space-between;align-items:center;"><span class="ldkp-muted" data-employee-filter-count></span><button type="button" class="ldkp-btn light" data-employee-filter-reset>Filter zurÃ¼cksetzen</button></div></div></div></div>';
        echo '<div class="ldkp-employee-list">';
        foreach ($employees as $key => $emp) {
            $contact = $this->get_employee_contact_line($emp);
            $areas = trim((isset($emp['primary_area']) ? $emp['primary_area'] : '').(!empty($emp['extra_areas']) ? ' Â· '.$emp['extra_areas'] : ''));
            $pin_status = !empty($emp['pin_hash']) ? '<span class="ldkp-pill approved">PIN gesetzt</span>' : '<span class="ldkp-pill open">keine PIN</span>';
            $role_label = $this->role_label(isset($emp['role']) ? $emp['role'] : 'employee');
            $search_blob = strtolower(trim(implode(' ', array_filter(array($emp['name'] ?? '', $contact, $areas, $role_label, $emp['phone'] ?? '', $emp['whatsapp'] ?? '')))));
            echo '<div class="ldkp-employee-manage-card" data-employee-card data-employee-search="'.esc_attr($search_blob).'" data-employee-role="'.esc_attr($role_label).'" data-employee-area="'.esc_attr(strtolower($areas)).'">';
            echo '<div class="ldkp-employee-manage-head"><div><div class="ldkp-employee-name">'.esc_html($emp['name']).'</div><div class="ldkp-muted">Benutzerdaten bearbeiten und PIN verwalten</div></div><div class="ldkp-employee-manage-badges"><span class="ldkp-soft-pill">'.esc_html($role_label).'</span>'.$pin_status.'</div></div>';
            echo '<div class="ldkp-employee-info-grid"><div class="ldkp-info-box"><div class="ldkp-label-small">Kontakt</div><div class="ldkp-info-value">'.esc_html($contact !== '' ? $contact : 'â').'</div></div><div class="ldkp-info-box"><div class="ldkp-label-small">Bereiche</div><div class="ldkp-info-value">'.esc_html($areas !== '' ? $areas : 'â').'</div></div></div>';
            echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" class="ldkp-employee-form-grid"><input type="hidden" name="action" value="ldk_portal_save_employee"><input type="hidden" name="employee_key" value="'.esc_attr($key).'">';
            wp_nonce_field('ldk_portal_save_employee');
            echo '<div><label class="ldkp-label-small">Neue PIN</label><input type="text" name="employee_pin" class="ldkp-input" inputmode="numeric" placeholder="Neue PIN"></div>';
            echo '<div><label class="ldkp-label-small">Rolle</label><select name="employee_role" class="ldkp-select"><option value="employee"'.selected(isset($emp['role'])?$emp['role']:'employee','employee',false).'>Mitarbeiter</option><option value="manager"'.selected(isset($emp['role'])?$emp['role']:'employee','manager',false).'>Manager</option><option value="head_manager"'.selected(isset($emp['role'])?$emp['role']:'employee','head_manager',false).'>Haupt-Manager</option></select></div>';
            echo '<div><label class="ldkp-label-small">Telefon</label><input type="text" name="employee_phone" class="ldkp-input" placeholder="Telefon" value="'.esc_attr(isset($emp['phone'])?$emp['phone']:'').'"></div>';
            echo '<div><label class="ldkp-label-small">WhatsApp</label><input type="text" name="employee_whatsapp" class="ldkp-input" placeholder="WhatsApp" value="'.esc_attr(isset($emp['whatsapp'])?$emp['whatsapp']:'').'"></div>';
            echo '<div><label class="ldkp-label-small">Hauptbereich</label><input type="text" name="employee_primary_area" class="ldkp-input" placeholder="Hauptbereich" value="'.esc_attr(isset($emp['primary_area'])?$emp['primary_area']:'').'"></div>';
            echo '<div><label class="ldkp-label-small">Zusatzbereiche</label><input type="text" name="employee_extra_areas" class="ldkp-input" placeholder="Zusatzbereiche" value="'.esc_attr(isset($emp['extra_areas'])?$emp['extra_areas']:'').'"></div>';
            echo '<div class="ldkp-span-2 ldkp-employee-actions-row"><label class="ldkp-checkline"><input type="checkbox" name="employee_clear_pin" value="1"> PIN lÃ¶schen</label><div class="ldkp-edit-actions"><button class="ldkp-btn light">Speichern</button></div></div></form>';
            echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" class="ldkp-employee-delete-form" onsubmit="return confirm(&quot;Benutzer wirklich lÃ¶schen?&quot;)"><input type="hidden" name="action" value="ldk_portal_delete_employee"><input type="hidden" name="employee_key" value="'.esc_attr($key).'">';
            wp_nonce_field('ldk_portal_delete_employee');
            echo '<div class="ldkp-employee-actions-row"><span class="ldkp-muted">LÃ¶schen ist immer sichtbar und nicht mehr abgeschnitten.</span><button class="ldkp-btn red">Benutzer lÃ¶schen</button></div></form>';
            echo '</div>';
        }
        echo '</div>';
        echo '</div>';
        return ob_get_clean();
    }

    public function admin_page() {
        $portal_user = $this->current_portal_user();
        if (!$this->can_manage_employees($portal_user)) wp_die('Keine Berechtigung.');
        $employees = $this->get_employee_display_list();
        $requests = $this->get_requests();
        $status = sanitize_text_field(isset($_GET['status']) ? $_GET['status'] : '');
        echo '<div class="wrap"><h1>Mitarbeiter Portal Pro v3.3</h1>';
        if ($status === 'saved') echo '<div class="notice notice-success"><p>Gespeichert.</p></div>';
        echo '<p><strong>Shortcode:</strong> <code>[ldk_mitarbeiter_portal]</code> <span class="ldkp-muted">(intern vorbereitet fÃ¼r Restaurant-ID, aktuell aktiv: Restaurant 1)</span></p><div class="ldkp-admin-grid"><div class="ldkp-card"><h2>Mitarbeiter aus Dienstplan</h2><p>PIN und Rolle bleiben jetzt stabil erhalten. Neue Namen werden nur ergÃ¤nzt.</p>';
        foreach ($employees as $key => $emp) {
            echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="border:1px solid #e5e7eb;border-radius:14px;padding:12px;margin-bottom:12px;"><input type="hidden" name="action" value="ldk_portal_save_employee"><input type="hidden" name="employee_key" value="'.esc_attr($key).'">';
            wp_nonce_field('ldk_portal_save_employee');
            echo '<div style="font-weight:700;margin-bottom:8px;">'.esc_html($emp['name']).'</div><div class="ldkp-grid"><div class="ldkp-col-6"><label class="ldkp-inline-label">PIN neu setzen</label><input type="text" name="employee_pin" class="ldkp-input" inputmode="numeric"></div><div class="ldkp-col-6"><label class="ldkp-inline-label">Rolle</label><select name="employee_role" class="ldkp-select"><option value="employee"'.selected(isset($emp['role'])?$emp['role']:'employee','employee',false).'>Mitarbeiter</option><option value="manager"'.selected(isset($emp['role'])?$emp['role']:'employee','manager',false).'>Manager</option><option value="head_manager"'.selected(isset($emp['role'])?$emp['role']:'employee','head_manager',false).'>Haupt-Manager</option></select></div><div class="ldkp-col-12"><button class="button button-primary">Speichern</button></div></div></form>';
        }
        echo '</div><div class="ldkp-card"><h2>Ãbersicht</h2><div class="ldkp-kpi"><div class="ldkp-kpi-item"><div>Mitarbeiter</div><div class="ldkp-kpi-value">'.count($employees).'</div></div><div class="ldkp-kpi-item"><div>AntrÃ¤ge</div><div class="ldkp-kpi-value">'.count($requests).'</div></div><div class="ldkp-kpi-item"><div>Status</div><div class="ldkp-kpi-value">stabil</div></div></div></div></div></div>';
    }

    public function portal_shortcode($atts = array()) {
        $user = $this->current_portal_user();
        $employees = $this->get_employee_display_list();
        $rows = $this->get_published_rows();

        ob_start();
        echo '<div class="ldkp-wrap"><div class="ldkp-card"><div class="ldkp-row" style="justify-content:space-between;align-items:flex-start;"><div><div class="ldkp-title">Dienstplan Portal</div><div class="ldkp-muted">Ein Login reicht fÃ¼r Dienstplan, Abwesenheiten, Krankmeldungen und interne Benachrichtigungen. Intern ist das Portal jetzt auf Restaurant-ID vorbereitet; aktuell lÃ¤uft alles auf Restaurant 1.</div></div>';
        if ($user) echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'" style="margin:0;"><input type="hidden" name="action" value="ldk_portal_logout">'.wp_nonce_field('ldk_portal_logout', '_wpnonce', true, false).'<button class="ldkp-btn light">Abmelden</button></form>';
        echo '</div>'.$this->notice_message();

        if (!$user) {
            echo '<div class="ldkp-login"><div class="ldkp-status">Bitte mit Mitarbeitername und PIN einloggen.</div><form method="post" action="'.esc_url(admin_url('admin-post.php')).'"><input type="hidden" name="action" value="ldk_portal_login">'.wp_nonce_field('ldk_portal_login', '_wpnonce', true, false).'<div style="margin-bottom:12px;"><label class="ldkp-inline-label">Mitarbeiter</label><select name="employee_key" class="ldkp-select" required><option value="">Bitte auswÃ¤hlen</option>'; 
            foreach ($employees as $key => $emp) if (!empty($emp['pin_hash'])) echo '<option value="'.esc_attr($key).'">'.esc_html($emp['name']).' â '.esc_html($this->role_label(isset($emp['role'])?$emp['role']:'employee')).'</option>';
            echo '</select></div><div style="margin-bottom:12px;"><label class="ldkp-inline-label">PIN</label><input type="password" name="employee_pin" class="ldkp-input" inputmode="numeric" required></div><button class="ldkp-btn">Einloggen</button></form></div></div></div>';
            return ob_get_clean();
        }

        echo '<script>window.LDKP_AJAX = '.wp_json_encode(array('url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('ldk_portal_ajax'), 'liveInterval' => 8000)).';</script>';
        $unread_count = $this->get_unread_notification_count($user);
        echo '<div class="ldkp-status"><div class="ldkp-userline"><strong>Angemeldet als:</strong> <span>'.esc_html($user['name']).'</span><span class="ldkp-tag">'.esc_html($this->role_label($user['role'])).'</span><span data-notification-wrap class="'.($unread_count ? '' : 'ldkp-hidden').'"><button type="button" class="ldkp-noti-chip" data-open-notifications>Benachrichtigungen <span class="ldkp-noti-count" data-notification-count data-count="'.esc_attr($unread_count).'">'.intval($unread_count).'</span></button></span></div>';
        echo '</div>';

        $open_head_count = $this->get_open_head_message_count();
        if ($this->can_manage_all($user)) {
            $menu = array(
                'today' => 'Dashboard',
                'my_plan' => 'Mein Plan',
                'request_form' => 'FreiwÃ¼nsche / Urlaub',
                'all_plans' => 'Alle PlÃ¤ne',
                'sick_leave' => 'Krankmeldung',
                'sick_requests' => 'AU & Meldungen',
                'head_messages_inbox' => 'Posteingang Mitarbeiter'.($open_head_count ? ' (' . intval($open_head_count) . ')' : ''),
                'approvals' => 'Genehmigungen',
                'manage_frontend' => 'Verwaltung',
                'manage_employees' => 'Benutzer & PIN',
                'emergency_messages' => 'Notfallmelden',
            );
        } elseif ($this->can_view_all($user)) {
            $menu = array(
                'today' => 'Dashboard',
                'my_plan' => 'Mein Plan',
                'request_form' => 'FreiwÃ¼nsche / Urlaub',
                'all_plans' => 'Alle PlÃ¤ne',
                'sick_leave' => 'Krankmeldung',
                'sick_requests' => 'AU & Meldungen',
                'emergency_messages' => 'Notfallmelden',
            );
        } else {
            $menu = array('today' => 'Dashboard','my_plan' => 'Mein Plan','request_form' => 'FreiwÃ¼nsche / Urlaub','my_requests' => 'Meine AntrÃ¤ge','sick_leave' => 'Krankmeldung','head_message' => 'Dokumente & Anliegen');
        }

        echo '<div class="ldkp-main-menu">';
        foreach ($menu as $key => $label) echo '<button type="button" class="ldkp-btn light'.($key === 'today' ? ' active' : '').'" data-open-section="'.esc_attr($key).'">'.esc_html($label).'</button>';
        echo '</div>';

        $myRows = $this->get_own_rows($rows, $user);
        $visibleRows = $this->filter_rows_for_user($rows, $user);

        echo '<div class="ldkp-section active" data-section="today">'.$this->render_today($rows, $user).'</div>';
        echo '<div class="ldkp-section" data-section="my_plan">'.$this->render_plan_section($myRows, 'Mein Dienstplan').'</div>';
        echo '<div class="ldkp-section" data-section="request_form">'.$this->render_request_center($user).'</div>';
        echo '<div class="ldkp-section" data-section="my_requests">'.$this->render_own_requests($user).'</div>';
        echo '<div class="ldkp-section" data-section="sick_leave">'.$this->render_sick_leave_section($user).'</div>';
        echo '<div class="ldkp-section" data-section="head_message">'.$this->render_head_message_form($user).'</div>';
        echo '<div class="ldkp-section" data-section="notifications">'.$this->render_notifications($user).'</div>';
        if ($this->can_view_all($user)) {
            echo '<div class="ldkp-section" data-section="all_plans">'.$this->render_plan_section($visibleRows, 'Alle DienstplÃ¤ne').'</div>';
            echo '<div class="ldkp-section" data-section="sick_requests">'.$this->render_sick_requests($user).'</div>';
            echo '<div class="ldkp-section" data-section="emergency_messages">'.$this->render_emergency_section($user).'</div>';
        }
        if ($this->can_manage_all($user)) {
            echo '<div class="ldkp-section" data-section="head_messages_inbox">'.$this->render_head_manager_messages($user).'</div>';
            echo '<div class="ldkp-section" data-section="approvals">'.$this->render_approvals($user).'</div>';
            echo '<div class="ldkp-section" data-section="manage_frontend">'.$this->render_frontend_editor($user).'</div>';
            echo '<div class="ldkp-section" data-section="manage_employees">'.$this->render_frontend_employee_management($user).'</div>