Skip to main content

HR Performance Analytics



<!DOCTYPE html>
<!-- ============================================================
     HR PERFORMANCE DASHBOARD  v11
     White Theme | Manual Entry | 6 Ranked Employees + 1 Specialist
     14 Services | PDF + PPTX Export
     ============================================================
     HOW TO EDIT IN NOTEPAD:
     1. Find the section you want by searching (Ctrl+F) the label
        e.g. "BLOCK: EMPLOYEES CONFIG" to change employee names/IDs
        e.g. "BLOCK: SERVICES CONFIG" to change service names
        e.g. "BLOCK: SPECIALIST CONFIG" to change specialist details
     2. Each block is clearly marked with START and END comments
     3. Save the file and refresh in your browser
     ============================================================ -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HR Performance Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pptxgenjs@3.12.0/dist/pptxgen.bundle.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=IBM+Plex+Mono:wght@400;600&display=swap" rel="stylesheet">

<!-- ============================================================
     BLOCK: CSS VARIABLES  (colours, spacing)
     Change colour values here to retheme the dashboard
     ============================================================ -->
<style>
:root {
  /* --- Background & Surfaces --- */
  --bg:       #F0F3F7;
  --surface:  #FFFFFF;
  --surface2: #F4F6FA;
  --surface3: #E8EDF5;
  /* --- Borders --- */
  --border:   #DDE3EC;
  --border2:  #C4CDD9;
  /* --- Text --- */
  --ink:      #0D1825;
  --body:     #2C3A4E;
  --muted:    #68788E;
  /* --- Green Scale (primary accent) --- */
  --g1:  #0A5C38;
  --g2:  #138050;
  --g3:  #1DAD6C;
  --g4:  #5EDCA8;
  --g5:  #C2F0DC;
  --g6:  #EBF9F3;
  /* --- Secondary Accents --- */
  --blue:   #1A5CB4;
  --purple: #6836D0;
  --orange: #D95B0A;
  --amber:  #B57D00;
  --red:    #C43030;
  --teal:   #0E8A8A;
  /* --- Specialist Employee Accent --- */
  --spec:  #7C3AED;
  --spec2: #F0EAFF;
  --spec3: #DDD0FF;
}
/* ============================================================
   END BLOCK: CSS VARIABLES
   ============================================================ */

/* ============================================================
   BLOCK: BASE STYLES
   ============================================================ */
* { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 15px; }
body {
  background: var(--bg);
  color: var(--body);
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  line-height: 1.6;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: var(--surface2); }
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 3px; }
/* ============================================================
   END BLOCK: BASE STYLES
   ============================================================ */

/* ============================================================
   BLOCK: HEADER STYLES
   ============================================================ */
header {
  background: var(--g1);
  padding: 0 28px;
  display: flex; align-items: center; justify-content: space-between;
  height: 62px;
  position: sticky; top: 0; z-index: 300;
  box-shadow: 0 2px 12px rgba(10,92,56,.25);
}
.logo { display: flex; align-items: center; gap: 12px; }
.logo-icon {
  width: 38px; height: 38px;
  background: rgba(255,255,255,.18);
  border: 1.5px solid rgba(255,255,255,.35);
  border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  font-family: 'IBM Plex Mono', monospace; font-size: 13px; font-weight: 700; color: #fff;
}
.logo-main { font-size: 15px; font-weight: 700; color: #fff; }
.logo-sub { font-size: 10px; color: rgba(255,255,255,.6); letter-spacing: 1.2px; text-transform: uppercase; margin-top: 1px; }
.hdr-right { display: flex; align-items: center; gap: 9px; }
.hdr-badge {
  background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.28);
  border-radius: 20px; padding: 4px 13px;
  font-family: 'IBM Plex Mono', monospace; font-size: 9px;
  color: rgba(255,255,255,.75); letter-spacing: 1px; text-transform: uppercase;
}
.exp-btn {
  display: flex; align-items: center; gap: 6px; padding: 7px 14px;
  border-radius: 7px; font-size: 12px; font-weight: 600; cursor: pointer;
  border: 1.5px solid; transition: .18s; white-space: nowrap;
}
.exp-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0,0,0,.18); }
.exp-btn.pdf  { background: rgba(255,255,255,.92); border-color: transparent; color: var(--g1); }
.exp-btn.pptx { background: rgba(255,255,255,.14); border-color: rgba(255,255,255,.4); color: #fff; }
.exp-btn.all  { background: var(--g4); border-color: var(--g4); color: var(--g1); }
/* ============================================================
   END BLOCK: HEADER STYLES
   ============================================================ */

/* ============================================================
   BLOCK: EXPORT OVERLAY STYLES
   ============================================================ */
.exp-overlay {
  display: none; position: fixed; inset: 0; z-index: 9999;
  background: rgba(13,24,37,.6); backdrop-filter: blur(7px);
  align-items: center; justify-content: center;
  flex-direction: column; gap: 16px;
}
.exp-overlay.show { display: flex; }
.exp-spin {
  width: 46px; height: 46px;
  border: 3px solid rgba(255,255,255,.2);
  border-top-color: var(--g3);
  border-radius: 50%; animation: spin .75s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.exp-msg { font-size: 15px; font-weight: 700; color: #fff; }
.exp-sub  { font-size: 12px; color: rgba(255,255,255,.6); }
/* ============================================================
   END BLOCK: EXPORT OVERLAY STYLES
   ============================================================ */

/* ============================================================
   BLOCK: STATUS BAR STYLES
   ============================================================ */
.status-bar {
  display: flex; gap: 8px; padding: 14px 28px 0; flex-wrap: wrap;
}
.s-pill {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 20px; padding: 5px 13px;
  font-size: 12px; font-weight: 500; color: var(--muted);
  display: flex; align-items: center; gap: 6px;
  box-shadow: 0 1px 3px rgba(0,0,0,.05);
}
.s-dot      { width: 7px; height: 7px; border-radius: 50%; background: var(--g3); box-shadow: 0 0 0 2px var(--g6); }
.s-dot.spec { background: var(--spec); box-shadow: 0 0 0 2px var(--spec2); }
/* ============================================================
   END BLOCK: STATUS BAR STYLES
   ============================================================ */

/* ============================================================
   BLOCK: TAB NAVIGATION STYLES
   ============================================================ */
.tab-bar {
  display: flex; padding: 0 28px;
  background: var(--surface);
  border-bottom: 2px solid var(--border);
  margin-top: 14px; overflow-x: auto; gap: 2px;
}
.tab-btn {
  padding: 13px 20px; font-size: 13px; font-weight: 600; color: var(--muted);
  background: transparent; border: none; cursor: pointer;
  position: relative; transition: .2s; white-space: nowrap; flex-shrink: 0;
}
.tab-btn::after {
  content: ''; position: absolute; bottom: -2px; left: 0; right: 0;
  height: 2px; background: var(--g1); transform: scaleX(0); transition: .2s;
}
.tab-btn:hover { color: var(--ink); }
.tab-btn.active { color: var(--g1); }
.tab-btn.active::after { transform: scaleX(1); }
/* per-tab active accent overrides */
.tab-btn[data-tab="tp-insights"].active  { color: var(--g1); }
.tab-btn[data-tab="tp-emp"].active       { color: var(--blue); }
.tab-btn[data-tab="tp-emp"].active::after { background: var(--blue); }
.tab-btn[data-tab="tp-svc"].active       { color: var(--purple); }
.tab-btn[data-tab="tp-svc"].active::after { background: var(--purple); }
.tab-btn[data-tab="tp-dist"].active      { color: var(--teal); }
.tab-btn[data-tab="tp-dist"].active::after { background: var(--teal); }
.tab-btn[data-tab="tp-other"].active     { color: var(--spec); }
.tab-btn[data-tab="tp-other"].active::after { background: var(--spec); }
.tab-btn[data-tab="tp-entry"].active     { color: var(--orange); }
.tab-btn[data-tab="tp-entry"].active::after { background: var(--orange); }
.tab-pane { display: none; padding: 24px 28px; }
.tab-pane.active { display: block; }
/* ============================================================
   END BLOCK: TAB NAVIGATION STYLES
   ============================================================ */

/* ============================================================
   BLOCK: SECTION TITLE STYLES
   ============================================================ */
.sec-t {
  font-size: 10px; font-weight: 700; letter-spacing: 2.5px;
  text-transform: uppercase; color: var(--muted);
  margin-bottom: 14px;
  display: flex; align-items: center; gap: 10px;
}
.sec-t::after { content: ''; flex: 1; height: 1px; background: var(--border); }
/* ============================================================
   END BLOCK: SECTION TITLE STYLES
   ============================================================ */

/* ============================================================
   BLOCK: KPI CARD STYLES
   ============================================================ */
.kpi-strip { display: grid; grid-template-columns: repeat(auto-fill, minmax(155px,1fr)); gap: 10px; margin-bottom: 22px; }
.kpi-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 11px; padding: 15px 17px;
  position: relative; overflow: hidden; transition: .2s;
  box-shadow: 0 1px 4px rgba(0,0,0,.05);
}
.kpi-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; }
.kpi-card.cg::before { background: var(--g1); }
.kpi-card.cb::before { background: var(--blue); }
.kpi-card.co::before { background: var(--orange); }
.kpi-card.cr::before { background: var(--red); }
.kpi-card.cp::before { background: var(--purple); }
.kpi-card.ct::before { background: var(--teal); }
.kpi-card:hover { box-shadow: 0 4px 14px rgba(0,0,0,.1); transform: translateY(-1px); }
.kpi-lbl { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .9px; color: var(--muted); }
.kpi-val { font-size: 24px; font-weight: 800; color: var(--ink); line-height: 1.1; margin: 4px 0 3px; }
.kpi-sub { font-size: 11px; color: var(--muted); }
.kpi-ico { position: absolute; right: 13px; top: 13px; font-size: 18px; opacity: .14; }
/* ============================================================
   END BLOCK: KPI CARD STYLES
   ============================================================ */

/* ============================================================
   BLOCK: INSIGHT CARD STYLES
   ============================================================ */
.ins-kpi { display: grid; grid-template-columns: repeat(auto-fill, minmax(195px,1fr)); gap: 10px; margin-bottom: 20px; }
.ik {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 11px; padding: 15px 17px;
  display: flex; gap: 13px; align-items: center;
  box-shadow: 0 1px 4px rgba(0,0,0,.05); transition: .2s;
  position: relative; overflow: hidden;
}
.ik::before { content: ''; position: absolute; top: 0; left: 0; bottom: 0; width: 4px; }
.ik.green::before  { background: var(--g1); }
.ik.blue::before   { background: var(--blue); }
.ik.orange::before { background: var(--orange); }
.ik.red::before    { background: var(--red); }
.ik.purple::before { background: var(--purple); }
.ik.teal::before   { background: var(--teal); }
.ik:hover { box-shadow: 0 4px 14px rgba(0,0,0,.09); }
.ik-ico { font-size: 20px; flex-shrink: 0; }
.ik-lbl { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); margin-bottom: 2px; }
.ik-val { font-size: 20px; font-weight: 800; color: var(--ink); }
.ik-sub { font-size: 11px; color: var(--muted); margin-top: 1px; }
/* ============================================================
   END BLOCK: INSIGHT CARD STYLES
   ============================================================ */

/* ============================================================
   BLOCK: CHART CARD STYLES
   ============================================================ */
.chart-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 18px; transition: .2s;
  box-shadow: 0 1px 4px rgba(0,0,0,.05);
}
.chart-card:hover { box-shadow: 0 5px 16px rgba(0,0,0,.09); }
.cc-title {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1.5px;
  color: var(--g1); margin-bottom: 13px; padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 8px;
}
.cc-title span { font-size: 14px; }
.cc-ch { position: relative; height: 220px; }
/* ============================================================
   END BLOCK: CHART CARD STYLES
   ============================================================ */

/* ============================================================
   BLOCK: LEADERBOARD STYLES
   ============================================================ */
.lb-wrap { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 18px; box-shadow: 0 1px 4px rgba(0,0,0,.05); }
.lb-title { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1.5px; color: var(--amber); margin-bottom: 13px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
.lb-row { display: flex; align-items: center; gap: 11px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.lb-row:last-child { border-bottom: none; }
.lb-rank { font-family: 'IBM Plex Mono', monospace; font-size: 12px; font-weight: 700; width: 24px; text-align: center; flex-shrink: 0; }
.lb-rank.r1 { color: #9A7000; }
.lb-rank.r2 { color: #707070; }
.lb-rank.r3 { color: #8B5A2B; }
.lb-rank.rn { color: var(--muted); }
.lb-av { width: 31px; height: 31px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-family: 'IBM Plex Mono', monospace; font-size: 11px; font-weight: 700; color: #fff; flex-shrink: 0; }
.lb-info { flex: 1; min-width: 0; }
.lb-name { font-size: 13px; font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-sub  { font-size: 11px; color: var(--muted); }
.lb-bar-wrap { flex: 1; background: var(--surface2); border-radius: 4px; height: 6px; max-width: 110px; }
.lb-bar  { height: 100%; border-radius: 4px; }
.lb-score { font-family: 'IBM Plex Mono', monospace; font-size: 13px; font-weight: 700; min-width: 34px; text-align: right; }
/* ============================================================
   END BLOCK: LEADERBOARD STYLES
   ============================================================ */

/* ============================================================
   BLOCK: EMPLOYEE CARD STYLES
   ============================================================ */
.emp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px,1fr)); gap: 14px; }
.emp-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 18px; transition: .2s;
  box-shadow: 0 1px 4px rgba(0,0,0,.05);
  position: relative; overflow: hidden;
}
.emp-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--g1), var(--blue));
  opacity: 0; transition: .2s;
}
.emp-card:hover { box-shadow: 0 6px 18px rgba(0,0,0,.1); transform: translateY(-2px); border-color: var(--g5); }
.emp-card:hover::before { opacity: 1; }
.emp-head { display: flex; align-items: center; gap: 11px; margin-bottom: 11px; }
.emp-av { width: 42px; height: 42px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-family: 'IBM Plex Mono', monospace; font-size: 13px; font-weight: 700; flex-shrink: 0; color: #fff; }
.emp-nm { font-size: 14px; font-weight: 700; color: var(--ink); }
.emp-id { font-family: 'IBM Plex Mono', monospace; font-size: 9px; color: var(--muted); margin-top: 1px; }
.emp-badges { display: flex; gap: 7px; margin-bottom: 12px; flex-wrap: wrap; }
.e-badge { background: var(--surface2); border: 1px solid var(--border); border-radius: 7px; padding: 5px 10px; font-size: 11px; display: flex; flex-direction: column; gap: 1px; }
.e-badge .bl { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .7px; color: var(--muted); }
.e-badge .bv { font-size: 14px; font-weight: 700; }
.e-badge.tot  .bv { color: var(--g1); }
.e-badge.avg  .bv { color: var(--blue); }
.e-badge.best .bv { color: var(--amber); font-size: 11px; }
.e-badge.weak .bv { color: var(--red);   font-size: 11px; }
.emp-ch { position: relative; height: 225px; }
/* ============================================================
   END BLOCK: EMPLOYEE CARD STYLES
   ============================================================ */

/* ============================================================
   BLOCK: SERVICE TAB STYLES
   ============================================================ */
.svc-tot-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(155px,1fr)); gap: 8px; margin-bottom: 20px; }
.stc { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 13px 14px; transition: .2s; box-shadow: 0 1px 3px rgba(0,0,0,.04); }
.stc:hover { box-shadow: 0 4px 12px rgba(0,0,0,.09); }
.stc-lbl { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); margin-bottom: 3px; }
.stc-val { font-size: 21px; font-weight: 800; color: var(--g1); }
.stc-avg { font-size: 11px; color: var(--muted); margin-top: 1px; }
.svc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(380px,1fr)); gap: 14px; }
.svc-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 17px; transition: .2s; box-shadow: 0 1px 4px rgba(0,0,0,.05); }
.svc-card:hover { box-shadow: 0 5px 16px rgba(0,0,0,.09); }
.svc-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 11px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
.svc-name { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--g1); }
.svc-stats { display: flex; gap: 12px; }
.svc-stat { font-size: 10px; font-weight: 600; color: var(--muted); display: flex; flex-direction: column; align-items: flex-end; }
.svc-stat strong { font-size: 13px; font-weight: 700; color: var(--ink); }
.svc-ch { position: relative; height: 170px; }
/* ============================================================
   END BLOCK: SERVICE TAB STYLES
   ============================================================ */

/* ============================================================
   BLOCK: DISTRIBUTION TAB STYLES
   ============================================================ */
.dist-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(265px,1fr)); gap: 14px; }
.dist-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 17px; transition: .2s; box-shadow: 0 1px 4px rgba(0,0,0,.05); }
.dist-card:hover { box-shadow: 0 5px 16px rgba(0,0,0,.09); }
.dist-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 11px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
.dist-name { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--purple); }
.dist-tot  { font-family: 'IBM Plex Mono', monospace; font-size: 11px; color: var(--muted); background: var(--surface2); padding: 3px 9px; border-radius: 5px; }
.dist-ch   { position: relative; height: 200px; }
/* ============================================================
   END BLOCK: DISTRIBUTION TAB STYLES
   ============================================================ */

/* ============================================================
   BLOCK: RADAR CHART STYLES
   ============================================================ */
.radar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(258px,1fr)); gap: 14px; }
.radar-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 15px; transition: .2s; box-shadow: 0 1px 4px rgba(0,0,0,.05); }
.radar-card:hover { box-shadow: 0 5px 14px rgba(0,0,0,.09); }
.radar-head { display: flex; align-items: center; gap: 9px; margin-bottom: 11px; }
.radar-av { width: 32px; height: 32px; border-radius: 7px; display: flex; align-items: center; justify-content: center; font-family: 'IBM Plex Mono', monospace; font-size: 11px; font-weight: 700; color: #fff; flex-shrink: 0; }
.radar-nm { font-size: 13px; font-weight: 700; color: var(--ink); }
.radar-sc { font-size: 11px; color: var(--muted); }
.radar-ch  { position: relative; height: 195px; }
/* ============================================================
   END BLOCK: RADAR CHART STYLES
   ============================================================ */

/* ============================================================
   BLOCK: OTHER ACTIONS TAB STYLES  (Specialist Employee)
   ============================================================ */
.oa-layout { display: grid; grid-template-columns: 1fr 2fr; gap: 20px; align-items: start; }
@media (max-width: 900px) { .oa-layout { grid-template-columns: 1fr; } }
.oa-total-card {
  background: var(--surface); border: 1.5px solid var(--spec3);
  border-radius: 14px; padding: 24px;
  box-shadow: 0 2px 12px rgba(124,58,237,.1);
  position: relative; overflow: hidden;
}
.oa-total-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 4px; background: linear-gradient(90deg, var(--spec), var(--blue)); }
.oa-emp-badge { display: flex; align-items: center; gap: 10px; background: var(--spec2); border: 1px solid var(--spec3); border-radius: 9px; padding: 10px 14px; margin-bottom: 18px; }
.oa-emp-av { width: 36px; height: 36px; border-radius: 8px; background: linear-gradient(135deg, var(--spec), var(--blue)); display: flex; align-items: center; justify-content: center; font-family: 'IBM Plex Mono', monospace; font-size: 12px; font-weight: 700; color: #fff; flex-shrink: 0; }
.oa-emp-name { font-size: 13px; font-weight: 700; color: var(--ink); }
.oa-emp-sub  { font-size: 10px; color: var(--muted); }
.oa-big-stat { text-align: center; padding: 18px 0; border-top: 1px solid var(--spec3); border-bottom: 1px solid var(--spec3); margin-bottom: 16px; }
.oa-big-lbl { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 6px; }
.oa-big-val { font-size: 56px; font-weight: 800; color: var(--spec); font-family: 'IBM Plex Mono', monospace; line-height: 1; }
.oa-big-sub { font-size: 12px; color: var(--muted); margin-top: 6px; }
.oa-breakdown { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.oa-chip { background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; text-align: center; }
.oa-chip-lbl { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 3px; }
.oa-chip-val { font-size: 20px; font-weight: 800; font-family: 'IBM Plex Mono', monospace; }
.oa-chip-val.processed { color: var(--g1); }
.oa-chip-val.pending   { color: var(--amber); }
.oa-chip-val.rejected  { color: var(--red); }
.oa-chip-val.docs      { color: var(--blue); }
.oa-chip-val.pages     { color: var(--teal); }
.oa-right { display: flex; flex-direction: column; gap: 18px; }
/* ============================================================
   END BLOCK: OTHER ACTIONS TAB STYLES
   ============================================================ */

/* ============================================================
   BLOCK: MANUAL ENTRY TAB STYLES
   ============================================================ */
.entry-intro {
  display: flex; align-items: flex-start; gap: 14px;
  background: rgba(217,91,10,.06); border: 1px solid rgba(217,91,10,.25);
  border-radius: 11px; padding: 16px 20px; margin-bottom: 22px;
}
.entry-intro-icon { font-size: 22px; flex-shrink: 0; margin-top: 2px; }
.entry-intro-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--orange); margin-bottom: 4px; }
.entry-intro-text  { font-size: 13px; color: var(--body); line-height: 1.6; }
.entry-intro-text strong { color: var(--ink); font-weight: 600; }
/* Employee selector row */
.entry-select-row {
  display: flex; align-items: center; gap: 14px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 10px; padding: 14px 18px; margin-bottom: 20px;
  flex-wrap: wrap;
}
.entry-select-row label { font-size: 13px; font-weight: 600; color: var(--ink); flex-shrink: 0; }
.entry-emp-select {
  padding: 8px 13px; border: 1.5px solid var(--border);
  border-radius: 7px; background: #fff; color: var(--ink);
  font-size: 13px; font-weight: 500; outline: none; cursor: pointer;
  min-width: 220px; flex-shrink: 0;
}
.entry-emp-select:focus { border-color: var(--orange); }
.entry-save-btn {
  padding: 8px 22px; background: var(--g1); color: #fff;
  border: none; border-radius: 7px; font-size: 13px; font-weight: 600;
  cursor: pointer; transition: .18s; margin-left: auto;
}
.entry-save-btn:hover { background: var(--g2); transform: translateY(-1px); }
.entry-save-btn.saved { background: var(--g3); }
/* Score entry grid */
.entry-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px,1fr)); gap: 12px; margin-bottom: 20px; }
.entry-field { display: flex; flex-direction: column; gap: 5px; }
.entry-field label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .7px; color: var(--muted); }
.entry-field input[type=number] {
  padding: 10px 13px; border: 1.5px solid var(--border);
  border-radius: 8px; background: #fff; color: var(--ink);
  font-size: 16px; font-weight: 600; outline: none; transition: .2s;
  font-family: 'IBM Plex Mono', monospace;
}
.entry-field input[type=number]:focus { border-color: var(--orange); box-shadow: 0 0 0 3px rgba(217,91,10,.1); }
.entry-field input[type=number]:valid { border-color: var(--g3); }
.entry-hint { font-size: 10px; color: var(--muted); }
/* Specialist entry */
.spec-entry-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px,1fr)); gap: 12px; margin-bottom: 18px; }
.spec-entry-field { display: flex; flex-direction: column; gap: 5px; }
.spec-entry-field label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .7px; color: var(--muted); }
.spec-entry-field input[type=number] {
  padding: 10px 13px; border: 1.5px solid var(--spec3);
  border-radius: 8px; background: var(--spec2); color: var(--ink);
  font-size: 16px; font-weight: 600; outline: none; transition: .2s;
  font-family: 'IBM Plex Mono', monospace;
}
.spec-entry-field input[type=number]:focus { border-color: var(--spec); box-shadow: 0 0 0 3px rgba(124,58,237,.1); }
.spec-save-btn {
  padding: 8px 22px; background: var(--spec); color: #fff;
  border: none; border-radius: 7px; font-size: 13px; font-weight: 600;
  cursor: pointer; transition: .18s;
}
.spec-save-btn:hover { background: #6030C0; transform: translateY(-1px); }
.spec-save-btn.saved { background: var(--g3); color: #fff; }
/* Feedback toast */
.toast {
  position: fixed; bottom: 28px; right: 28px; z-index: 8888;
  background: var(--g1); color: #fff;
  padding: 12px 22px; border-radius: 9px;
  font-size: 13px; font-weight: 600;
  box-shadow: 0 4px 16px rgba(10,92,56,.3);
  transform: translateY(80px); opacity: 0;
  transition: transform .3s ease, opacity .3s ease;
}
.toast.show { transform: translateY(0); opacity: 1; }
.toast.spec-toast { background: var(--spec); box-shadow: 0 4px 16px rgba(124,58,237,.3); }
/* ============================================================
   END BLOCK: MANUAL ENTRY TAB STYLES
   ============================================================ */

/* ============================================================
   BLOCK: LAYOUT HELPERS
   ============================================================ */
.ins-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 18px; }
.ins-grid  { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px,1fr)); gap: 14px; }
/* ============================================================
   END BLOCK: LAYOUT HELPERS
   ============================================================ */
</style>
</head>
<body>

<!-- ============================================================
     BLOCK: EXPORT OVERLAY HTML
     ============================================================ -->
<div class="exp-overlay" id="expOverlay">
  <div class="exp-spin"></div>
  <div class="exp-msg" id="expMsg">Generating…</div>
  <div class="exp-sub" id="expSub">Please wait</div>
</div>
<!-- ============================================================
     END BLOCK: EXPORT OVERLAY HTML
     ============================================================ -->

<!-- ============================================================
     BLOCK: SAVE FEEDBACK TOAST
     ============================================================ -->
<div class="toast" id="toast">✓ Scores saved — charts updated</div>
<!-- ============================================================
     END BLOCK: SAVE FEEDBACK TOAST
     ============================================================ -->

<!-- ============================================================
     BLOCK: HEADER HTML
     ============================================================ -->
<header>
  <div class="logo">
    <div class="logo-icon">HR</div>
    <div>
      <div class="logo-main">Performance Analytics</div>
      <div class="logo-sub">Human Resources Dashboard</div>
    </div>
  </div>
  <div class="hdr-right">
    <button class="exp-btn pdf"  onclick="exportPDF()">📄 Export PDF</button>
    <button class="exp-btn pptx" onclick="exportPPTX()">📊 Export PPTX</button>
    <button class="exp-btn all"  onclick="exportBoth()">⬇ Export All</button>
  </div>
</header>
<!-- ============================================================
     END BLOCK: HEADER HTML
     ============================================================ -->

<!-- ============================================================
     BLOCK: STATUS BAR HTML
     ============================================================ -->
<div class="status-bar">
  <div class="s-pill"><span class="s-dot"></span>6 Ranked Employees</div>
  <div class="s-pill"><span class="s-dot spec"></span>1 Specialist Employee</div>
  <div class="s-pill"><span class="s-dot"></span>14 Services</div>
  <div class="s-pill">
    <span class="s-dot" style="background:var(--g1);box-shadow:0 0 0 2px var(--g6)"></span>
    Grand Total: <strong id="gtVal" style="color:var(--g1);font-family:'IBM Plex Mono',monospace;font-size:11px;margin-left:4px;font-weight:700">—</strong>
  </div>
  <div class="s-pill">
    <span class="s-dot spec"></span>
    IT Processed: <strong id="itProcStatus" style="color:var(--spec);font-family:'IBM Plex Mono',monospace;font-size:11px;margin-left:4px;font-weight:700">—</strong>
  </div>
</div>
<!-- ============================================================
     END BLOCK: STATUS BAR HTML
     ============================================================ -->

<!-- ============================================================
     BLOCK: TAB BAR HTML
     ============================================================ -->
<div class="tab-bar">
  <button class="tab-btn active" data-tab="tp-insights">Insights</button>
  <button class="tab-btn" data-tab="tp-emp">Per Employee</button>
  <button class="tab-btn" data-tab="tp-svc">Per Service</button>
  <button class="tab-btn" data-tab="tp-dist">Distribution</button>
  <button class="tab-btn" data-tab="tp-other">Other Actions</button>
  <button class="tab-btn" data-tab="tp-entry">📝 Data Entry</button>
</div>
<!-- ============================================================
     END BLOCK: TAB BAR HTML
     ============================================================ -->

<!-- ============================================================
     BLOCK: TAB PANE - INSIGHTS
     ============================================================ -->
<div class="tab-pane active" id="tp-insights">
  <div class="sec-t">Performance Intelligence — 6 Ranked Employees</div>
  <div class="ins-kpi" id="insKpi"></div>
  <div class="ins-2col">
    <div class="lb-wrap">
      <div class="lb-title">🏆 Overall Ranking — 6 Employees</div>
      <div id="lbList"></div>
    </div>
    <div class="chart-card">
      <div class="cc-title"><span>📡</span>Team Average by Service</div>
      <div class="cc-ch"><canvas id="teamAvgChart"></canvas></div>
    </div>
  </div>
  <div class="sec-t">Individual Radar Profiles</div>
  <div class="radar-grid" id="radarGrid"></div>
  <div class="sec-t">Comparative Analysis</div>
  <div class="ins-grid" id="insGrid"></div>
</div>
<!-- ============================================================
     END BLOCK: TAB PANE - INSIGHTS
     ============================================================ -->

<!-- ============================================================
     BLOCK: TAB PANE - PER EMPLOYEE
     ============================================================ -->
<div class="tab-pane" id="tp-emp">
  <div class="sec-t">KPI Overview — 6 Ranked Employees</div>
  <div class="kpi-strip" id="empKpi"></div>
  <div class="sec-t">Employee Performance Cards</div>
  <div class="emp-grid" id="empGrid"></div>
</div>
<!-- ============================================================
     END BLOCK: TAB PANE - PER EMPLOYEE
     ============================================================ -->

<!-- ============================================================
     BLOCK: TAB PANE - PER SERVICE
     ============================================================ -->
<div class="tab-pane" id="tp-svc">
  <div class="sec-t">Service Totals</div>
  <div class="svc-tot-grid" id="svcTotals"></div>
  <div class="sec-t">Service Comparison — 6 Employees</div>
  <div class="svc-grid" id="svcGrid"></div>
</div>
<!-- ============================================================
     END BLOCK: TAB PANE - PER SERVICE
     ============================================================ -->

<!-- ============================================================
     BLOCK: TAB PANE - DISTRIBUTION
     ============================================================ -->
<div class="tab-pane" id="tp-dist">
  <div class="sec-t">Score Distribution by Service</div>
  <div class="dist-grid" id="distGrid"></div>
</div>
<!-- ============================================================
     END BLOCK: TAB PANE - DISTRIBUTION
     ============================================================ -->

<!-- ============================================================
     BLOCK: TAB PANE - OTHER ACTIONS  (Specialist employee stats)
     ============================================================ -->
<div class="tab-pane" id="tp-other">
  <div class="sec-t">Specialist Employee — Internal Transfer Focus</div>
  <div class="oa-layout">
    <!-- Left: stats card -->
    <div class="oa-total-card">
      <div class="oa-emp-badge">
        <div class="oa-emp-av" id="oaSpecAv">?</div>
        <div>
          <div class="oa-emp-name" id="oaSpecName">Specialist Employee</div>
          <div class="oa-emp-sub"  id="oaSpecId">—</div>
        </div>
      </div>
      <div class="oa-big-stat">
        <div class="oa-big-lbl">Total IT Requests Processed</div>
        <div class="oa-big-val" id="oaItTotal">0</div>
        <div class="oa-big-sub" id="oaItSub">Enter data via Data Entry tab</div>
      </div>
      <div class="oa-breakdown">
        <div class="oa-chip"><div class="oa-chip-lbl">Processed</div><div class="oa-chip-val processed" id="oaProc">0</div></div>
        <div class="oa-chip"><div class="oa-chip-lbl">Pending</div>  <div class="oa-chip-val pending"   id="oaPend">0</div></div>
        <div class="oa-chip"><div class="oa-chip-lbl">Rejected</div> <div class="oa-chip-val rejected"  id="oaRej">0</div></div>
        <div class="oa-chip"><div class="oa-chip-lbl">Docs Scanned</div><div class="oa-chip-val docs" id="oaDocs">0</div></div>
        <div class="oa-chip" style="grid-column:1/-1;">
          <div class="oa-chip-lbl">Total Pages Scanned</div>
          <div class="oa-chip-val pages" id="oaPages">0</div>
        </div>
      </div>
    </div>
    <!-- Right: chart -->
    <div class="oa-right">
      <div class="chart-card" style="border-color:rgba(14,138,138,.3);">
        <div class="cc-title" style="color:var(--teal);"><span>🗂️</span>Documents Scanned per HR Service</div>
        <div style="position:relative;height:280px;"><canvas id="docScanChart"></canvas></div>
      </div>
    </div>
  </div>
</div>
<!-- ============================================================
     END BLOCK: TAB PANE - OTHER ACTIONS
     ============================================================ -->

<!-- ============================================================
     BLOCK: TAB PANE - DATA ENTRY
     This tab handles ALL manual data entry for:
     1. 6 ranked employees (service scores 0-100)
     2. 1 specialist employee (IT requests + doc scans)
     ============================================================ -->
<div class="tab-pane" id="tp-entry">

  <!-- Intro banner -->
  <div class="entry-intro">
    <div class="entry-intro-icon">📝</div>
    <div>
      <div class="entry-intro-title">Manual Data Entry</div>
      <div class="entry-intro-text">
        Select an employee from the dropdown, enter their scores for each service (0–100),
        then click <strong>Save &amp; Update Charts</strong>. All charts and rankings update instantly.
        Use the Specialist section at the bottom to enter Internal Transfer and document scan data.
      </div>
    </div>
  </div>

  <!-- ── RANKED EMPLOYEE SECTION ── -->
  <div class="sec-t">Ranked Employee Scores (0 – 100 per service)</div>

  <!-- Employee selector + save button -->
  <div class="entry-select-row">
    <label for="entryEmpSelect">Employee:</label>
    <select id="entryEmpSelect" class="entry-emp-select"></select>
    <button class="entry-save-btn" id="entrySaveBtn" onclick="saveEmployeeScores()">
      ✓ Save &amp; Update Charts
    </button>
  </div>

  <!-- Score input grid — dynamically built by JS -->
  <div class="entry-grid" id="entryGrid"></div>

  <!-- ── SPECIALIST EMPLOYEE SECTION ── -->
  <div class="sec-t" style="margin-top:12px;">Specialist Employee — <span style="color:var(--spec);">Internal Transfer &amp; Document Data</span></div>

  <div style="background:var(--spec2);border:1.5px solid var(--spec3);border-radius:12px;padding:20px;margin-bottom:20px;">
    <div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:14px;border-bottom:1px solid var(--spec3);">
      <div style="width:38px;height:38px;border-radius:9px;background:linear-gradient(135deg,var(--spec),var(--blue));display:flex;align-items:center;justify-content:center;font-family:'IBM Plex Mono',monospace;font-size:13px;font-weight:700;color:#fff;flex-shrink:0;" id="specAvEntry">?</div>
      <div>
        <div style="font-size:14px;font-weight:700;color:var(--ink);" id="specNameEntry">Specialist Employee</div>
        <div style="font-size:10px;color:var(--muted);" id="specIdEntry">—</div>
      </div>
    </div>

    <!-- IT Request counts -->
    <div style="margin-bottom:16px;">
      <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);margin-bottom:10px;">Internal Transfer Requests</div>
      <div class="spec-entry-grid">
        <div class="spec-entry-field">
          <label>Requests Processed</label>
          <input type="number" id="spec_processed" min="0" value="0" placeholder="0">
        </div>
        <div class="spec-entry-field">
          <label>Requests Pending</label>
          <input type="number" id="spec_pending" min="0" value="0" placeholder="0">
        </div>
        <div class="spec-entry-field">
          <label>Requests Rejected</label>
          <input type="number" id="spec_rejected" min="0" value="0" placeholder="0">
        </div>
      </div>
    </div>

    <!-- Doc scan counts per service — dynamically built -->
    <div style="margin-bottom:16px;">
      <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);margin-bottom:10px;">Documents Scanned per Service</div>
      <div class="spec-entry-grid" id="specDocGrid"></div>
    </div>

    <button class="spec-save-btn" id="specSaveBtn" onclick="saveSpecialistData()">
      ✓ Save Specialist Data &amp; Update Charts
    </button>
  </div>

</div>
<!-- ============================================================
     END BLOCK: TAB PANE - DATA ENTRY
     ============================================================ -->

<!-- ============================================================
     BLOCK: JAVASCRIPT — DATA CONFIG
     Edit RANKED_EMPLOYEES, SERVICES, and SPECIALIST here
     ============================================================ -->
<script>
/* ============================================================
   BLOCK: EMPLOYEES CONFIG
   ──────────────────────────────────────────────────────────
   STEP-BY-STEP: How to edit employee names in Notepad
   ──────────────────────────────────────────────────────────
   STEP 1 — Open this file in Notepad (or Notepad++)
   STEP 2 — Press Ctrl+F and search for:  BLOCK: EMPLOYEES CONFIG
   STEP 3 — Find the list below (id and name pairs)
   STEP 4 — Change the name value inside the quotes
             Example: change 'Osama' to 'Osama Al-Ghamdi'
   STEP 5 — Keep exactly 6 entries (do NOT add or remove rows)
   STEP 6 — The id (e.g. 'EMP001') must match the id used in
             BLOCK: INITIAL SCORES below — keep them in sync
   STEP 7 — Save the file, then refresh in your browser
   ──────────────────────────────────────────────────────────
   Keep exactly 6 entries for the ranking tabs.
   ============================================================ */
const RANKED_EMPLOYEES = [
  { id: 'EMP001', name: 'Osama'    },
  { id: 'EMP002', name: 'Alhamdan' },
  { id: 'EMP003', name: 'Alzahri'  },
  { id: 'EMP004', name: 'Omar'     },
  { id: 'EMP005', name: 'Alaslani' },
  { id: 'EMP006', name: 'Marwa'    }
];
/* ============================================================
   END BLOCK: EMPLOYEES CONFIG
   ============================================================ */

/* ============================================================
   BLOCK: SPECIALIST CONFIG
   ──────────────────────────────────────────────────────────
   STEP-BY-STEP: How to edit the Specialist name/role
   ──────────────────────────────────────────────────────────
   STEP 1 — Press Ctrl+F and search for:  BLOCK: SPECIALIST CONFIG
   STEP 2 — Find the name and role lines below
   STEP 3 — Change the text inside the quotes
             Example: change 'Runel' to 'Runel Santos'
   STEP 4 — Save the file and refresh in your browser
   STEP 5 — To change IT requests and document scan numbers,
             see BLOCK: SPECIALIST INITIAL DATA below
   ──────────────────────────────────────────────────────────
   The Specialist is the 7th employee focused on Internal
   Transfer work. They are NOT included in the ranking.
   ============================================================ */
const SPECIALIST = {
  id:   'EMP007',
  name: 'Runel',
  role: 'Internal Transfer Specialist'
};
/* ============================================================
   END BLOCK: SPECIALIST CONFIG
   ============================================================ */

/* ============================================================
   BLOCK: SERVICES CONFIG
   ──────────────────────────────────────────────────────────
   STEP-BY-STEP: How to edit service display names
   ──────────────────────────────────────────────────────────
   STEP 1 — Press Ctrl+F and search for:  BLOCK: SERVICES CONFIG
   STEP 2 — Each service has two parts:
             key   — the internal code (DO NOT change this)
             label — the display name shown in charts/tables
   STEP 3 — Only change the label text inside the quotes
             Example: change 'Hiring' to 'Recruitment & Hiring'
   STEP 4 — Do NOT change the key — it links to score data
   STEP 5 — Save the file and refresh in your browser
   ──────────────────────────────────────────────────────────
   Keep exactly 14 entries to match the INITIAL SCORES block.
   ============================================================ */
const SERVICES = [
  { key: 'hiring',                label: 'Hiring'              },
  { key: 'wages',                 label: 'Wages'               },
  { key: 'salary_advance',        label: 'Salary Advance'      },
  { key: 'education_program',     label: 'Education Program'   },
  { key: 'work_schedule_ot',      label: 'Work Schedule & OT'  },
  { key: 'employee_status_update',label: 'Status Update'       },
  { key: 'timekeeping_system',    label: 'Timekeeping'         },
  { key: 'internal_transfer',     label: 'Internal Transfer'   },
  { key: 'medical_insurance',     label: 'Medical Insurance'   },
  { key: 'hop_distribution',      label: 'HOP Distribution'    },
  { key: 'muskan_distribution',   label: 'Muskan Distribution' },
  { key: 'home_loan_program',     label: 'Home Loan Program'   },
  { key: 'housing_issues',        label: 'Housing Issues'      },
  { key: 'separation',            label: 'Separation'          }
];
/* ============================================================
   END BLOCK: SERVICES CONFIG
   ============================================================ */

/* ============================================================
   BLOCK: INITIAL SCORES
   ──────────────────────────────────────────────────────────
   STEP-BY-STEP: How to edit employee scores in Notepad
   ──────────────────────────────────────────────────────────
   STEP 1 — Press Ctrl+F and search for:  BLOCK: INITIAL SCORES
   STEP 2 — Find the employee you want to update by their ID
             e.g. EMP001 is Osama, EMP002 is Alhamdan, etc.
   STEP 3 — Find the service you want to change inside that
             employee's block, e.g.  hiring: 20
   STEP 4 — Change the number after the colon
             Example: change  hiring: 20  to  hiring: 45
   STEP 5 — Do NOT change the service key (the word before :)
             Only change the number after :
   STEP 6 — Numbers can be any positive whole number (0 or more)
             These are transaction counts, NOT percentages
   STEP 7 — Save the file and refresh in your browser
             Charts and rankings will update automatically
   ──────────────────────────────────────────────────────────
   Each employee block must have ALL 14 service keys.
   Use 0 for services that have no transactions.
   ============================================================ */
let DATA = {

  /* ── EMP001: Osama ── */
  EMP001: {
    hiring:                 20,
    wages:                  30,
    salary_advance:         40,
    education_program:      50,
    work_schedule_ot:       60,
    employee_status_update: 50,
    timekeeping_system:     30,
    internal_transfer:     300,
    medical_insurance:       0,
    hop_distribution:        0,
    muskan_distribution:     0,
    home_loan_program:       0,
    housing_issues:          0,
    separation:             20
  },

  /* ── EMP002: Alhamdan ── */
  EMP002: {
    hiring:                 20,
    wages:                  30,
    salary_advance:         40,
    education_program:      50,
    work_schedule_ot:       60,
    employee_status_update: 50,
    timekeeping_system:     30,
    internal_transfer:     220,
    medical_insurance:       0,
    hop_distribution:        0,
    muskan_distribution:     0,
    home_loan_program:       0,
    housing_issues:          0,
    separation:             20
  },

  /* ── EMP003: Alzahri ── */
  EMP003: {
    hiring:                 20,
    wages:                  30,
    salary_advance:         40,
    education_program:      50,
    work_schedule_ot:       60,
    employee_status_update: 50,
    timekeeping_system:     30,
    internal_transfer:     220,
    medical_insurance:       0,
    hop_distribution:        0,
    muskan_distribution:     0,
    home_loan_program:       0,
    housing_issues:          0,
    separation:             20
  },

  /* ── EMP004: Omar ── */
  EMP004: {
    hiring:                 20,
    wages:                  30,
    salary_advance:         40,
    education_program:      50,
    work_schedule_ot:       60,
    employee_status_update: 50,
    timekeeping_system:     30,
    internal_transfer:     220,
    medical_insurance:       0,
    hop_distribution:        0,
    muskan_distribution:     0,
    home_loan_program:       0,
    housing_issues:          0,
    separation:             20
  },

  /* ── EMP005: Alaslani ── */
  EMP005: {
    hiring:                 20,
    wages:                  30,
    salary_advance:         40,
    education_program:      50,
    work_schedule_ot:       60,
    employee_status_update: 50,
    timekeeping_system:     30,
    internal_transfer:     220,
    medical_insurance:       0,
    hop_distribution:        0,
    muskan_distribution:     0,
    home_loan_program:       0,
    housing_issues:          0,
    separation:             20
  },

  /* ── EMP006: Marwa ── */
  EMP006: {
    hiring:                 20,
    wages:                  30,
    salary_advance:         40,
    education_program:      50,
    work_schedule_ot:       60,
    employee_status_update: 50,
    timekeeping_system:     30,
    internal_transfer:     220,
    medical_insurance:       0,
    hop_distribution:        0,
    muskan_distribution:     0,
    home_loan_program:       0,
    housing_issues:          0,
    separation:             20
  }

};
/* ============================================================
   END BLOCK: INITIAL SCORES
   ============================================================ */

/* ============================================================
   BLOCK: SPECIALIST INITIAL DATA
   ──────────────────────────────────────────────────────────
   STEP-BY-STEP: How to edit Specialist data in Notepad
   ──────────────────────────────────────────────────────────
   STEP 1 — Press Ctrl+F and search for:  BLOCK: SPECIALIST INITIAL DATA
   STEP 2 — To change IT request counts, find and edit:
             processed: 1230   ← total requests processed
             pending:   0      ← requests still in progress
             rejected:  0      ← requests that were rejected
   STEP 3 — To change documents scanned per service, find the
             doc_scans section below and edit the docs number
             for each service.  Example:
               hiring:   { docs: 15 }   ← change 15 to new value
   STEP 4 — Services NOT listed below default to 0 documents
             If a service should show 0, set docs: 0
   STEP 5 — Save the file and refresh in your browser
             The Other Actions tab and charts update instantly
   ──────────────────────────────────────────────────────────
   doc_scans keys must match the keys in SERVICES CONFIG above
   ============================================================ */
let SPEC_DATA = {
  processed: 1230,   /* ← EDIT: Total IT requests processed by Runel */
  pending:      0,   /* ← EDIT: IT requests still pending            */
  rejected:     0,   /* ← EDIT: IT requests rejected                 */

  /* Documents scanned by service — edit the docs number for each */
  doc_scans: {
    hiring:                 { docs:   15, pages:  0 },  /* ← Hiring = 15          */
    wages:                  { docs:  123, pages:  0 },  /* ← Wages = 123          */
    salary_advance:         { docs:    0, pages:  0 },  /* ← Salary Advance = 0   */
    education_program:      { docs:  133, pages:  0 },  /* ← Education Program = 133 */
    work_schedule_ot:       { docs:    0, pages:  0 },  /* ← Work Schedule & OT = 0  */
    employee_status_update: { docs:    0, pages:  0 },  /* ← Status Update = 0    */
    timekeeping_system:     { docs:    0, pages:  0 },  /* ← Timekeeping = 0      */
    internal_transfer:      { docs: 1350, pages:  0 },  /* ← Internal Transfer = 1350 */
    medical_insurance:      { docs:    0, pages:  0 },  /* ← Medical Insurance = 0    */
    hop_distribution:       { docs:  300, pages:  0 },  /* ← HOP Distribution = 300   */
    muskan_distribution:    { docs:    0, pages:  0 },  /* ← Muskan Distribution = 0  */
    home_loan_program:      { docs:    0, pages:  0 },  /* ← Home Loan Program = 0    */
    housing_issues:         { docs:    0, pages:  0 },  /* ← Housing Issues = 0       */
    separation:             { docs:   30, pages:  0 }   /* ← Separation = 30          */
  }
};
/* ============================================================
   END BLOCK: SPECIALIST INITIAL DATA
   ============================================================ */

/* ============================================================
   BLOCK: CHART COLOUR PALETTE
   One colour per employee (6 entries).
   Change hex values to retheme the charts.
   Must match number of RANKED_EMPLOYEES.
   ============================================================ */
const ECOLS = [
  '#1A5CB4',   // EMP001
  '#138050',   // EMP002
  '#6836D0',   // EMP003
  '#D95B0A',   // EMP004
  '#B57D00',   // EMP005
  '#0E8A8A'    // EMP006
];
/* ============================================================
   END BLOCK: CHART COLOUR PALETTE
   ============================================================ */

/* ============================================================
   BLOCK: AVATAR GRADIENTS
   Background gradients for employee avatar circles.
   Must match number of RANKED_EMPLOYEES.
   ============================================================ */
const AVT = [
  'linear-gradient(135deg,#1A5CB4,#0E8A8A)',
  'linear-gradient(135deg,#138050,#0A5C38)',
  'linear-gradient(135deg,#6836D0,#1A5CB4)',
  'linear-gradient(135deg,#D95B0A,#B57D00)',
  'linear-gradient(135deg,#B57D00,#D95B0A)',
  'linear-gradient(135deg,#0E8A8A,#138050)'
];
/* ============================================================
   END BLOCK: AVATAR GRADIENTS
   ============================================================ */

/* ============================================================
   BLOCK: CHART INSTANCES (internal — do not edit)
   ============================================================ */
const CH = { emp:{}, svc:{}, dist:{}, ins:{}, oa:{} };
/* ============================================================
   END BLOCK: CHART INSTANCES
   ============================================================ */

/* ============================================================
   BLOCK: DATA HELPER FUNCTIONS
   ============================================================ */
function EA()         { return RANKED_EMPLOYEES.map(e => ({ ...DATA[e.id], id: e.id, name: e.name })); }
function eTotal(e)    { return SERVICES.reduce((s, sv) => s + (e[sv.key] || 0), 0); }
function eAvg(e)      { return eTotal(e) / SERVICES.length; }
function eBest(e)     { return SERVICES.reduce((b, s) => (e[s.key]||0) > (e[b.key]||0) ? s : b, SERVICES[0]); }
function eWorst(e)    { return SERVICES.reduce((w, s) => (e[s.key]||0) < (e[w.key]||0) ? s : w, SERVICES[0]); }
function sTotal(k)    { return EA().reduce((s, e) => s + (e[k] || 0), 0); }
function sAvg(k)      { return sTotal(k) / EA().length; }
function GT()         { return EA().reduce((s, e) => s + eTotal(e), 0); }
function INI(n)       { return n.split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase(); }
/* ============================================================
   END BLOCK: DATA HELPER FUNCTIONS
   ============================================================ */

/* ============================================================
   BLOCK: CHART.JS DEFAULTS
   ============================================================ */
Chart.defaults.color       = '#68788E';
Chart.defaults.borderColor = '#DDE3EC';
Chart.defaults.font.family = 'Inter';
Chart.defaults.font.size   = 12;
const CB = () => ({
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: { display: false },
    tooltip: {
      backgroundColor: '#0D1825',
      borderColor: '#1A5CB4', borderWidth: 1,
      titleColor: '#fff', bodyColor: '#A0B0C0',
      padding: 10, cornerRadius: 7,
      titleFont: { size: 13, weight: '700' },
      bodyFont: { size: 12 }
    }
  }
});
function destroyCharts(obj) {
  Object.values(obj).forEach(c => { try { c && c.destroy && c.destroy(); } catch(e) {} });
}
/* ============================================================
   END BLOCK: CHART.JS DEFAULTS
   ============================================================ */

/* ============================================================
   BLOCK: RENDER — OTHER ACTIONS TAB  (Specialist stats)
   ============================================================ */
function renderOA() {
  // Update specialist header
  document.getElementById('oaSpecAv').textContent   = INI(SPECIALIST.name);
  document.getElementById('oaSpecName').textContent = SPECIALIST.name;
  document.getElementById('oaSpecId').textContent   = SPECIALIST.id + ' · ' + SPECIALIST.role;

  // IT request totals
  const proc  = SPEC_DATA.processed || 0;
  const pend  = SPEC_DATA.pending   || 0;
  const rej   = SPEC_DATA.rejected  || 0;
  document.getElementById('oaItTotal').textContent = proc;
  document.getElementById('oaItSub').textContent   = (proc + pend + rej) + ' total requests logged';
  document.getElementById('oaProc').textContent    = proc;
  document.getElementById('oaPend').textContent    = pend;
  document.getElementById('oaRej').textContent     = rej;
  document.getElementById('itProcStatus').textContent = proc;

  // Doc scan totals
  let totalDocs = 0, totalPages = 0;
  Object.values(SPEC_DATA.doc_scans).forEach(d => {
    totalDocs  += (parseInt(d.docs)  || 0);
    totalPages += (parseInt(d.pages) || 0);
  });
  document.getElementById('oaDocs').textContent  = totalDocs;
  document.getElementById('oaPages').textContent = totalPages.toLocaleString();

  // Doc scan bar chart
  if (CH.oa.docScan) CH.oa.docScan.destroy();
  const dsLabels = SERVICES.map(s => s.label);
  const dsVals   = SERVICES.map(s => parseInt((SPEC_DATA.doc_scans[s.key] || {}).docs) || 0);
  CH.oa.docScan = new Chart(document.getElementById('docScanChart').getContext('2d'), {
    type: 'bar',
    data: {
      labels: dsLabels,
      datasets: [{
        label: 'Documents Scanned',
        data: dsVals,
        backgroundColor: dsVals.map((_, i) => ECOLS[i % ECOLS.length] + '33'),
        borderColor:      dsVals.map((_, i) => ECOLS[i % ECOLS.length]),
        borderWidth: 2, borderRadius: 6, borderSkipped: false
      }]
    },
    options: {
      ...CB(),
      plugins: { ...CB().plugins, legend: { display: false } },
      scales: {
        y: { beginAtZero: true, grid: { color: '#EDF0F4' }, ticks: { font: { size: 11 } } },
        x: { grid: { display: false }, ticks: { font: { size: 10 }, color: '#2C3A4E', maxRotation: 35 } }
      }
    }
  });
}
/* ============================================================
   END BLOCK: RENDER — OTHER ACTIONS TAB
   ============================================================ */

/* ============================================================
   BLOCK: RENDER — INSIGHTS TAB
   ============================================================ */
function renderInsights() {
  const arr    = EA();
  const sorted = [...arr].sort((a, b) => eTotal(b) - eTotal(a));
  const gt     = GT();
  const pct    = ((gt / (arr.length * SERVICES.length * 100)) * 100).toFixed(1);
  const top    = sorted[0], bot = sorted[sorted.length - 1];
  const bSvc   = SERVICES.reduce((b, s) => sTotal(s.key) > sTotal(b.key) ? s : b, SERVICES[0]);
  const wSvc   = SERVICES.reduce((b, s) => sTotal(s.key) < sTotal(b.key) ? s : b, SERVICES[0]);

  // KPI row
  document.getElementById('insKpi').innerHTML = `
    <div class="ik green"><div class="ik-ico">🎯</div><div><div class="ik-lbl">Efficiency</div><div class="ik-val">${pct}%</div><div class="ik-sub">of max possible</div></div></div>
    <div class="ik blue"><div class="ik-ico">📊</div><div><div class="ik-lbl">Grand Total</div><div class="ik-val">${gt}</div><div class="ik-sub">${arr.length} emp × ${SERVICES.length} svc</div></div></div>
    <div class="ik orange"><div class="ik-ico">🏆</div><div><div class="ik-lbl">Top Performer</div><div class="ik-val">${top.name.split(' ')[0]}</div><div class="ik-sub">Score: ${eTotal(top)}</div></div></div>
    <div class="ik red"><div class="ik-ico">📌</div><div><div class="ik-lbl">Needs Support</div><div class="ik-val">${bot.name.split(' ')[0]}</div><div class="ik-sub">Gap: ${eTotal(top) - eTotal(bot)}</div></div></div>
    <div class="ik teal"><div class="ik-ico">⬆</div><div><div class="ik-lbl">Best Service</div><div class="ik-val">${bSvc.label}</div><div class="ik-sub">Total: ${sTotal(bSvc.key)}</div></div></div>
    <div class="ik purple"><div class="ik-ico">⬇</div><div><div class="ik-lbl">Weakest Svc</div><div class="ik-val">${wSvc.label}</div><div class="ik-sub">Total: ${sTotal(wSvc.key)}</div></div></div>
  `;
  document.getElementById('gtVal').textContent = gt;

  // Leaderboard
  const lb   = document.getElementById('lbList'); lb.innerHTML = '';
  const maxT = eTotal(sorted[0]) || 1;
  sorted.forEach((emp, i) => {
    const t   = eTotal(emp);
    const ei  = arr.indexOf(arr.find(a => a.id === emp.id));
    const col = ECOLS[ei % ECOLS.length];
    const rc  = i === 0 ? 'r1' : i === 1 ? 'r2' : i === 2 ? 'r3' : 'rn';
    const ri  = i === 0 ? '🥇' : i === 1 ? '🥈' : i === 2 ? '🥉' : `#${i + 1}`;
    lb.innerHTML += `<div class="lb-row">
      <div class="lb-rank ${rc}">${ri}</div>
      <div class="lb-av" style="background:${AVT[ei % AVT.length]}">${INI(emp.name)}</div>
      <div class="lb-info"><div class="lb-name">${emp.name}</div><div class="lb-sub">Avg ${eAvg(emp).toFixed(1)} · Best: ${eBest(emp).label}</div></div>
      <div class="lb-bar-wrap"><div class="lb-bar" style="width:${(t / maxT * 100).toFixed(0)}%;background:${col}"></div></div>
      <div class="lb-score" style="color:${col}">${t}</div>
    </div>`;
  });

  // Team avg by service chart
  if (CH.ins.ta) CH.ins.ta.destroy();
  CH.ins.ta = new Chart(document.getElementById('teamAvgChart').getContext('2d'), {
    type: 'bar',
    data: {
      labels: SERVICES.map(s => s.label),
      datasets: [{
        data: SERVICES.map(s => sAvg(s.key)),
        backgroundColor: SERVICES.map((_, i) => ECOLS[i % ECOLS.length] + '33'),
        borderColor:      SERVICES.map((_, i) => ECOLS[i % ECOLS.length]),
        borderWidth: 2, borderRadius: 4, borderSkipped: false
      }]
    },
    options: { ...CB(), indexAxis: 'y', scales: {
      x: { beginAtZero: true, max: 100, grid: { color: '#EDF0F4' }, ticks: { font: { size: 10 } } },
      y: { grid: { display: false }, ticks: { font: { size: 10 }, color: '#2C3A4E' } }
    }}
  });

  // Individual radar charts
  destroyCharts(CH.ins.radars || {}); CH.ins.radars = {};
  const rg = document.getElementById('radarGrid'); rg.innerHTML = '';
  arr.forEach((emp, i) => {
    const col = ECOLS[i % ECOLS.length];
    const c   = document.createElement('div'); c.className = 'radar-card';
    c.innerHTML = `<div class="radar-head">
      <div class="radar-av" style="background:${AVT[i % AVT.length]}">${INI(emp.name)}</div>
      <div><div class="radar-nm">${emp.name}</div><div class="radar-sc">Total: ${eTotal(emp)} · Avg: ${eAvg(emp).toFixed(1)}</div></div>
    </div><div class="radar-ch"><canvas id="rc-${emp.id}"></canvas></div>`;
    rg.appendChild(c);
    CH.ins.radars[emp.id] = new Chart(c.querySelector(`#rc-${emp.id}`).getContext('2d'), {
      type: 'radar',
      data: {
        labels: SERVICES.map(s => s.label),
        datasets: [{ data: SERVICES.map(s => emp[s.key] || 0), borderColor: col, backgroundColor: col + '22', pointBackgroundColor: col, pointRadius: 3, borderWidth: 2 }]
      },
      options: { ...CB(), scales: { r: {
        beginAtZero: true, max: 100,
        grid: { color: '#DDE3EC' }, angleLines: { color: '#DDE3EC' },
        pointLabels: { color: '#68788E', font: { size: 8 } },
        ticks: { backdropColor: 'transparent', color: '#9AAAB9', font: { size: 8 }, stepSize: 25 }
      }}}
    });
  });

  // Comparative insight charts
  destroyCharts(CH.ins.extra || {}); CH.ins.extra = {};
  const ig = document.getElementById('insGrid'); ig.innerHTML = '';
  const mkC = (id, title, icon, col) => {
    const c = document.createElement('div'); c.className = 'chart-card';
    c.innerHTML = `<div class="cc-title" style="color:${col}"><span>${icon}</span>${title}</div><div class="cc-ch"><canvas id="${id}"></canvas></div>`;
    ig.appendChild(c); return c;
  };
  // Total score bar
  const srtd = [...arr].sort((a, b) => eTotal(b) - eTotal(a));
  const cA = mkC('ins-tot', 'Total Score per Employee', '📈', 'var(--blue)');
  CH.ins.extra.tot = new Chart(cA.querySelector('#ins-tot').getContext('2d'), {
    type: 'bar',
    data: { labels: srtd.map(e => e.name.split(' ')[0]),
      datasets: [{ data: srtd.map(e => eTotal(e)),
        backgroundColor: srtd.map(e => ECOLS[arr.findIndex(a => a.id === e.id) % ECOLS.length] + '44'),
        borderColor:      srtd.map(e => ECOLS[arr.findIndex(a => a.id === e.id) % ECOLS.length]),
        borderWidth: 2, borderRadius: 5, borderSkipped: false }]
    },
    options: { ...CB(), scales: {
      y: { beginAtZero: true, grid: { color: '#EDF0F4' }, ticks: { font: { size: 11 } } },
      x: { grid: { display: false }, ticks: { font: { size: 12 }, color: '#2C3A4E' } }
    }}
  });
  // Performance gap bar
  const gaps = SERVICES.map(s => { const v = arr.map(e => e[s.key] || 0); return Math.max(...v) - Math.min(...v); });
  const cC = mkC('ins-gap', 'Performance Gap per Service (Max − Min)', '⚡', 'var(--orange)');
  CH.ins.extra.gap = new Chart(cC.querySelector('#ins-gap').getContext('2d'), {
    type: 'bar',
    data: { labels: SERVICES.map(s => s.label),
      datasets: [{ data: gaps,
        backgroundColor: gaps.map(g => g > 40 ? '#C4303033' : g > 20 ? '#B57D0033' : '#13805033'),
        borderColor:      gaps.map(g => g > 40 ? '#C43030' : g > 20 ? '#B57D00' : '#138050'),
        borderWidth: 2, borderRadius: 4, borderSkipped: false }]
    },
    options: { ...CB(), indexAxis: 'y', scales: {
      x: { beginAtZero: true, grid: { color: '#EDF0F4' }, ticks: { font: { size: 10 } } },
      y: { grid: { display: false }, ticks: { font: { size: 10 }, color: '#2C3A4E' } }
    }}
  });
  // Average score line
  const cD = mkC('ins-avg', 'Average Score per Employee', '📉', 'var(--g1)');
  CH.ins.extra.avg = new Chart(cD.querySelector('#ins-avg').getContext('2d'), {
    type: 'line',
    data: { labels: arr.map(e => e.name.split(' ')[0]),
      datasets: [{ data: arr.map(e => parseFloat(eAvg(e).toFixed(1))),
        borderColor: '#138050', backgroundColor: '#13805018',
        pointBackgroundColor: ECOLS, pointRadius: 7, pointHoverRadius: 9,
        borderWidth: 2.5, tension: .35, fill: true }]
    },
    options: { ...CB(), scales: {
      y: { beginAtZero: true, max: 100, grid: { color: '#EDF0F4' }, ticks: { font: { size: 11 } } },
      x: { grid: { display: false }, ticks: { font: { size: 12 }, color: '#2C3A4E' } }
    }}
  });
  // Service volume polar
  const cB = mkC('ins-pol', 'Service Volume (Polar)', '🌐', 'var(--purple)');
  CH.ins.extra.pol = new Chart(cB.querySelector('#ins-pol').getContext('2d'), {
    type: 'polarArea',
    data: { labels: SERVICES.map(s => s.label),
      datasets: [{ data: SERVICES.map(s => sTotal(s.key)),
        backgroundColor: ECOLS.map(c => c + '55'), borderColor: ECOLS, borderWidth: 1.5 }]
    },
    options: { ...CB(), scales: { r: { beginAtZero: true, grid: { color: '#DDE3EC' }, ticks: { backdropColor: 'transparent', color: '#9AAAB9', font: { size: 8 } } } },
      plugins: { ...CB().plugins, legend: { display: true, position: 'right', labels: { color: '#2C3A4E', font: { size: 11 }, boxWidth: 11, padding: 7 } } }
    }
  });
}
/* ============================================================
   END BLOCK: RENDER — INSIGHTS TAB
   ============================================================ */

/* ============================================================
   BLOCK: RENDER — PER EMPLOYEE TAB
   ============================================================ */
function renderEmpKpi() {
  const arr = EA(), gt = GT(), avg = (gt / (arr.length * SERVICES.length)).toFixed(1);
  const srt  = [...arr].sort((a, b) => eTotal(b) - eTotal(a));
  const bS   = SERVICES.reduce((b, s) => sTotal(s.key) > sTotal(b.key) ? s : b, SERVICES[0]);
  const wS   = SERVICES.reduce((b, s) => sTotal(s.key) < sTotal(b.key) ? s : b, SERVICES[0]);
  document.getElementById('empKpi').innerHTML = `
    <div class="kpi-card cg"><div class="kpi-lbl">Grand Total</div><div class="kpi-val">${gt}</div><div class="kpi-sub">6 emp × 14 services</div><div class="kpi-ico">∑</div></div>
    <div class="kpi-card cb"><div class="kpi-lbl">Team Average</div><div class="kpi-val">${avg}</div><div class="kpi-sub">Per emp per service</div><div class="kpi-ico">≈</div></div>
    <div class="kpi-card co"><div class="kpi-lbl">Top Performer</div><div class="kpi-val">${srt[0].name.split(' ')[0]}</div><div class="kpi-sub">Score: ${eTotal(srt[0])}</div><div class="kpi-ico">🏆</div></div>
    <div class="kpi-card cr"><div class="kpi-lbl">Needs Support</div><div class="kpi-val">${srt[srt.length-1].name.split(' ')[0]}</div><div class="kpi-sub">Score: ${eTotal(srt[srt.length-1])}</div><div class="kpi-ico">📌</div></div>
    <div class="kpi-card cg"><div class="kpi-lbl">Strongest Svc</div><div class="kpi-val">${bS.label}</div><div class="kpi-sub">Total: ${sTotal(bS.key)}</div><div class="kpi-ico">⬆</div></div>
    <div class="kpi-card cp"><div class="kpi-lbl">Weakest Svc</div><div class="kpi-val">${wS.label}</div><div class="kpi-sub">Total: ${sTotal(wS.key)}</div><div class="kpi-ico">⬇</div></div>
  `;
}
function renderEmp() {
  renderEmpKpi();
  destroyCharts(CH.emp); CH.emp = {};
  const g = document.getElementById('empGrid'); g.innerHTML = '';
  EA().forEach((emp, i) => {
    const t = eTotal(emp), a = eAvg(emp).toFixed(1), b = eBest(emp), w = eWorst(emp);
    const c = document.createElement('div'); c.className = 'emp-card';
    c.innerHTML = `<div class="emp-head">
      <div class="emp-av" style="background:${AVT[i % AVT.length]}">${INI(emp.name)}</div>
      <div><div class="emp-nm">${emp.name}</div><div class="emp-id">${emp.id}</div></div>
    </div>
    <div class="emp-badges">
      <div class="e-badge tot"><span class="bl">Total</span><span class="bv">${t}</span></div>
      <div class="e-badge avg"><span class="bl">Average</span><span class="bv">${a}</span></div>
      <div class="e-badge best"><span class="bl">Best</span><span class="bv">${b.label}</span></div>
      <div class="e-badge weak"><span class="bl">Weakest</span><span class="bv">${w.label}</span></div>
    </div>
    <div class="emp-ch"><canvas id="ec-${emp.id}"></canvas></div>`;
    g.appendChild(c);
    const col = ECOLS[i % ECOLS.length];
    CH.emp[emp.id] = new Chart(c.querySelector(`#ec-${emp.id}`).getContext('2d'), {
      type: 'bar',
      data: { labels: SERVICES.map(s => s.label),
        datasets: [{ data: SERVICES.map(s => emp[s.key] || 0),
          backgroundColor: col + '33', borderColor: col,
          borderWidth: 2, borderRadius: 4, borderSkipped: false }]
      },
      options: { ...CB(), indexAxis: 'y', scales: {
        x: { beginAtZero: true, max: 100, grid: { color: '#EDF0F4' }, ticks: { font: { size: 10 } } },
        y: { grid: { display: false }, ticks: { font: { size: 10 }, color: '#68788E' } }
      }}
    });
  });
}
/* ============================================================
   END BLOCK: RENDER — PER EMPLOYEE TAB
   ============================================================ */

/* ============================================================
   BLOCK: RENDER — PER SERVICE TAB
   ============================================================ */
function renderSvc() {
  destroyCharts(CH.svc); CH.svc = {};
  // Totals strip
  const ts = document.getElementById('svcTotals'); ts.innerHTML = '';
  SERVICES.forEach(s => {
    const t = sTotal(s.key), a = sAvg(s.key).toFixed(1);
    const d = document.createElement('div'); d.className = 'stc';
    d.innerHTML = `<div class="stc-lbl">${s.label}</div><div class="stc-val">${t}</div><div class="stc-avg">avg ${a}/emp</div>`;
    ts.appendChild(d);
  });
  // Charts
  const g = document.getElementById('svcGrid'); g.innerHTML = '';
  SERVICES.forEach(s => {
    const emps = EA(), vals = emps.map(e => e[s.key] || 0);
    const t = sTotal(s.key), a = sAvg(s.key).toFixed(1);
    const c = document.createElement('div'); c.className = 'svc-card';
    c.innerHTML = `<div class="svc-head">
      <div class="svc-name">${s.label}</div>
      <div class="svc-stats">
        <div class="svc-stat"><span>Total</span><strong>${t}</strong></div>
        <div class="svc-stat"><span>Avg</span><strong>${a}</strong></div>
      </div>
    </div><div class="svc-ch"><canvas id="sc-${s.key}"></canvas></div>`;
    g.appendChild(c);
    CH.svc[s.key] = new Chart(c.querySelector(`#sc-${s.key}`).getContext('2d'), {
      type: 'bar',
      data: { labels: emps.map(e => e.name.split(' ')[0]),
        datasets: [{ data: vals, backgroundColor: ECOLS.map(c => c + '33'), borderColor: ECOLS, borderWidth: 2, borderRadius: 4, borderSkipped: false }]
      },
      options: { ...CB(), scales: {
        y: { beginAtZero: true, max: 100, grid: { color: '#EDF0F4' }, ticks: { font: { size: 10 } } },
        x: { grid: { display: false }, ticks: { font: { size: 11 }, color: '#2C3A4E' } }
      }}
    });
  });
}
/* ============================================================
   END BLOCK: RENDER — PER SERVICE TAB
   ============================================================ */

/* ============================================================
   BLOCK: RENDER — DISTRIBUTION TAB
   ============================================================ */
function renderDist() {
  destroyCharts(CH.dist); CH.dist = {};
  const g = document.getElementById('distGrid'); g.innerHTML = '';
  SERVICES.forEach(s => {
    const emps = EA(), vals = emps.map(e => e[s.key] || 0), t = sTotal(s.key);
    const c = document.createElement('div'); c.className = 'dist-card';
    c.innerHTML = `<div class="dist-head">
      <div class="dist-name">${s.label}</div>
      <div class="dist-tot">Total: ${t}</div>
    </div><div class="dist-ch"><canvas id="dc-${s.key}"></canvas></div>`;
    g.appendChild(c);
    CH.dist[s.key] = new Chart(c.querySelector(`#dc-${s.key}`).getContext('2d'), {
      type: 'doughnut',
      data: { labels: emps.map(e => e.name.split(' ')[0]),
        datasets: [{ data: vals, backgroundColor: ECOLS.map(c => c + 'CC'), borderColor: '#fff', borderWidth: 3 }]
      },
      options: { ...CB(), cutout: '58%',
        plugins: { ...CB().plugins, legend: { display: true, position: 'bottom', labels: { color: '#2C3A4E', font: { size: 11 }, boxWidth: 11, padding: 9 } } }
      }
    });
  });
}
/* ============================================================
   END BLOCK: RENDER — DISTRIBUTION TAB
   ============================================================ */

/* ============================================================
   BLOCK: RENDER — DATA ENTRY TAB  (form generation)
   ============================================================ */
function buildEntryForm() {
  // Populate employee dropdown
  const sel = document.getElementById('entryEmpSelect');
  sel.innerHTML = '';
  RANKED_EMPLOYEES.forEach(e => {
    const opt = document.createElement('option');
    opt.value = e.id; opt.textContent = e.name + ' — ' + e.id;
    sel.appendChild(opt);
  });
  // Build score input grid for first employee
  buildScoreInputs(RANKED_EMPLOYEES[0].id);
  sel.addEventListener('change', () => buildScoreInputs(sel.value));

  // Build specialist entry
  document.getElementById('specAvEntry').textContent   = INI(SPECIALIST.name);
  document.getElementById('specNameEntry').textContent = SPECIALIST.name;
  document.getElementById('specIdEntry').textContent   = SPECIALIST.id + ' · ' + SPECIALIST.role;
  // Populate existing specialist values
  document.getElementById('spec_processed').value = SPEC_DATA.processed || 0;
  document.getElementById('spec_pending').value   = SPEC_DATA.pending   || 0;
  document.getElementById('spec_rejected').value  = SPEC_DATA.rejected  || 0;
  // Build doc scan inputs per service
  buildDocScanInputs();
}

function buildScoreInputs(empId) {
  const grid = document.getElementById('entryGrid'); grid.innerHTML = '';
  const scores = DATA[empId] || {};
  SERVICES.forEach(s => {
    const div = document.createElement('div'); div.className = 'entry-field';
    div.innerHTML = `
      <label>${s.label}</label>
      <input type="number" id="score_${s.key}" min="0" max="100"
             value="${scores[s.key] || 0}" placeholder="0–100">
      <span class="entry-hint">Score 0 – 100</span>`;
    grid.appendChild(div);
  });
}

function buildDocScanInputs() {
  const grid = document.getElementById('specDocGrid'); grid.innerHTML = '';
  SERVICES.forEach(s => {
    const existing = SPEC_DATA.doc_scans[s.key] || { docs: 0, pages: 0 };
    const div = document.createElement('div'); div.className = 'spec-entry-field';
    div.innerHTML = `
      <label>${s.label} — Docs Scanned</label>
      <input type="number" id="docs_${s.key}" min="0"
             value="${existing.docs || 0}" placeholder="0">`;
    grid.appendChild(div);
  });
}
/* ============================================================
   END BLOCK: RENDER — DATA ENTRY TAB
   ============================================================ */

/* ============================================================
   BLOCK: SAVE — EMPLOYEE SCORES
   Called when user clicks "Save & Update Charts"
   ============================================================ */
function saveEmployeeScores() {
  const empId = document.getElementById('entryEmpSelect').value;
  if (!DATA[empId]) DATA[empId] = {};
  SERVICES.forEach(s => {
    const inp = document.getElementById('score_' + s.key);
    if (inp) DATA[empId][s.key] = Math.min(100, Math.max(0, parseInt(inp.value) || 0));
  });
  renderAll();
  showToast('✓ ' + RANKED_EMPLOYEES.find(e => e.id === empId).name + ' — scores saved & charts updated', false);
  // Flash the button green
  const btn = document.getElementById('entrySaveBtn');
  btn.classList.add('saved'); btn.textContent = '✓ Saved!';
  setTimeout(() => { btn.classList.remove('saved'); btn.textContent = '✓ Save & Update Charts'; }, 2000);
}
/* ============================================================
   END BLOCK: SAVE — EMPLOYEE SCORES
   ============================================================ */

/* ============================================================
   BLOCK: SAVE — SPECIALIST DATA
   Called when user clicks "Save Specialist Data"
   ============================================================ */
function saveSpecialistData() {
  SPEC_DATA.processed = parseInt(document.getElementById('spec_processed').value) || 0;
  SPEC_DATA.pending   = parseInt(document.getElementById('spec_pending').value)   || 0;
  SPEC_DATA.rejected  = parseInt(document.getElementById('spec_rejected').value)  || 0;
  SERVICES.forEach(s => {
    const inp = document.getElementById('docs_' + s.key);
    if (!SPEC_DATA.doc_scans[s.key]) SPEC_DATA.doc_scans[s.key] = { docs: 0, pages: 0 };
    if (inp) SPEC_DATA.doc_scans[s.key].docs = parseInt(inp.value) || 0;
  });
  renderOA();
  showToast('✓ Specialist data saved — Other Actions updated', true);
  const btn = document.getElementById('specSaveBtn');
  btn.classList.add('saved'); btn.textContent = '✓ Saved!';
  setTimeout(() => { btn.classList.remove('saved'); btn.textContent = '✓ Save Specialist Data & Update Charts'; }, 2000);
}
/* ============================================================
   END BLOCK: SAVE — SPECIALIST DATA
   ============================================================ */

/* ============================================================
   BLOCK: TOAST NOTIFICATION
   ============================================================ */
function showToast(msg, isSpec) {
  const t = document.getElementById('toast');
  t.textContent = msg;
  t.className = 'toast' + (isSpec ? ' spec-toast' : '');
  t.classList.add('show');
  setTimeout(() => t.classList.remove('show'), 3000);
}
/* ============================================================
   END BLOCK: TOAST NOTIFICATION
   ============================================================ */

/* ============================================================
   BLOCK: RENDER ALL CHARTS
   ============================================================ */
function renderAll() {
  renderInsights();
  renderEmp();
  renderSvc();
  renderDist();
  renderOA();
}
/* ============================================================
   END BLOCK: RENDER ALL CHARTS
   ============================================================ */

/* ============================================================
   BLOCK: TAB SWITCHING
   ============================================================ */
document.querySelectorAll('.tab-btn').forEach(btn => {
  btn.addEventListener('click', () => {
    document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
    document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
    btn.classList.add('active');
    document.getElementById(btn.dataset.tab).classList.add('active');
  });
});
/* ============================================================
   END BLOCK: TAB SWITCHING
   ============================================================ */

/* ============================================================
   BLOCK: EXPORT HELPERS
   ============================================================ */
function showOverlay(msg, sub) { document.getElementById('expOverlay').classList.add('show'); document.getElementById('expMsg').textContent = msg; document.getElementById('expSub').textContent = sub; }
function updateMsg(m)          { document.getElementById('expMsg').textContent = m; }
function hideOverlay()         { document.getElementById('expOverlay').classList.remove('show'); }
/* ============================================================
   END BLOCK: EXPORT HELPERS
   ============================================================ */

/* ============================================================
   BLOCK: PDF EXPORT — WHITE + FOREST GREEN
   ============================================================ */
async function exportPDF() {
  showOverlay('Generating PDF', 'Building report…');
  await new Promise(r => setTimeout(r, 200));
  try {
    const { jsPDF } = window.jspdf;
    const pdf = new jsPDF('l', 'mm', 'a4');
    const pw = 297, ph = 210;
    const OW=[240,243,247], OW2=[225,232,240], INK=[13,24,37], BODY=[44,58,78], MUTED=[104,120,142], RULE=[175,192,210];
    const G1=[10,92,56], G2=[19,128,80], G3=[29,173,108];
    const S_H=[10,128,72], S_M=[181,125,0], S_L=[196,48,48];
    const EC=[[26,92,180],[19,128,80],[104,54,208],[217,91,10],[181,125,0],[14,138,138]];
    const arr = EA(), gt = GT(), sorted = [...arr].sort((a, b) => eTotal(b) - eTotal(a));

    const chrome = (title, sub) => {
      pdf.setFillColor(...OW); pdf.rect(0,0,pw,ph,'F');
      pdf.setFillColor(...G1); pdf.rect(0,0,pw,8,'F');
      pdf.setFillColor(...G3); pdf.rect(0,8,pw,1.2,'F');
      pdf.setFillColor(...OW2); pdf.rect(0,ph-9,pw,9,'F');
      pdf.setDrawColor(...RULE); pdf.setLineWidth(.3); pdf.line(0,ph-9,pw,ph-9);
      pdf.setTextColor(...MUTED); pdf.setFontSize(8); pdf.setFont('helvetica','normal');
      pdf.text('HR Performance Analytics · Confidential', 12, ph-3.8);
      pdf.text(`Page ${pdf.getCurrentPageInfo().pageNumber}`, pw-12, ph-3.8, {align:'right'});
      if (title) { pdf.setTextColor(...INK); pdf.setFontSize(18); pdf.setFont('helvetica','bold'); pdf.text(title, 12, 20); }
      if (sub)   { pdf.setTextColor(...MUTED); pdf.setFontSize(10); pdf.setFont('helvetica','normal'); pdf.text(sub, 12, 29); }
      pdf.setDrawColor(...G3); pdf.setLineWidth(.6); pdf.line(12,32,pw-12,32);
    };

    // Cover page
    pdf.setFillColor(...OW); pdf.rect(0,0,pw,ph,'F');
    pdf.setFillColor(...G1); pdf.rect(0,0,pw,58,'F');
    pdf.setFillColor(...G2); pdf.rect(0,58,pw,2.5,'F');
    pdf.setFillColor(...G3); pdf.rect(0,60.5,pw,.8,'F');
    pdf.setFillColor(...G2); pdf.rect(0,0,4,ph,'F');
    pdf.setTextColor(196,240,220); pdf.setFontSize(8.5); pdf.setFont('helvetica','bold');
    pdf.text('HUMAN RESOURCES · PERFORMANCE ANALYTICS', pw/2+2, 12, {align:'center'});
    pdf.setTextColor(255,255,255); pdf.setFontSize(32);
    pdf.text('Employee Performance', pw/2+2, 33, {align:'center'});
    pdf.setFontSize(26); pdf.setFont('helvetica','normal');
    pdf.text('Dashboard Report', pw/2+2, 48, {align:'center'});
    pdf.setTextColor(...MUTED); pdf.setFontSize(10); pdf.setFont('helvetica','normal');
    pdf.text(new Date().toLocaleDateString('en-GB',{weekday:'long',year:'numeric',month:'long',day:'numeric'}), pw/2+2, 73, {align:'center'});
    pdf.setDrawColor(...RULE); pdf.setLineWidth(.4); pdf.line(50,77,pw-50,77);
    pdf.setFontSize(9.5);
    pdf.text(`6 Ranked Employees  ·  14 Services  ·  Grand Total: ${gt}  ·  + 1 Specialist`, pw/2+2, 85, {align:'center'});
    // KPI boxes
    [{l:'Grand Total',v:String(gt),g:G1},{l:'Team Avg',v:(gt/(arr.length*SERVICES.length)).toFixed(1),g:G2},{l:'Top Performer',v:sorted[0].name.split(' ')[0],g:G1},{l:'Efficiency',v:((gt/(arr.length*SERVICES.length*100))*100).toFixed(1)+'%',g:G2}].forEach((k,i)=>{
      const x=14+i*68, y=96;
      pdf.setFillColor(...OW2); pdf.roundedRect(x,y,60,34,2,2,'F');
      pdf.setDrawColor(...k.g); pdf.setLineWidth(.8); pdf.roundedRect(x,y,60,34,2,2,'S');
      pdf.setFillColor(...k.g); pdf.roundedRect(x,y,60,5,2,2,'F'); pdf.rect(x,y+2.5,60,2.5,'F');
      pdf.setTextColor(...MUTED); pdf.setFontSize(7.5); pdf.setFont('helvetica','normal'); pdf.text(k.l,x+30,y+13,{align:'center'});
      pdf.setTextColor(...k.g); pdf.setFontSize(18); pdf.setFont('helvetica','bold'); pdf.text(String(k.v),x+30,y+27,{align:'center'});
    });
    // Ranking strip
    const GN=[196,240,220];
    pdf.setFillColor(...GN); pdf.roundedRect(14,139,pw-28,57,2,2,'F');
    pdf.setFillColor(...G2); pdf.roundedRect(14,139,pw-28,7,2,2,'F'); pdf.rect(14,143,pw-28,3,'F');
    pdf.setTextColor(255,255,255); pdf.setFontSize(9); pdf.setFont('helvetica','bold'); pdf.text('TOP PERFORMERS — 6 EMPLOYEES', pw/2, 144.5, {align:'center'});
    sorted.forEach((emp,i)=>{
      const rx=20, ry=153+i*7.3, rgb=EC[arr.findIndex(a=>a.id===emp.id)%EC.length];
      pdf.setFillColor(...rgb); pdf.circle(rx+1,ry-1.2,1.8,'F');
      pdf.setTextColor(...MUTED); pdf.setFontSize(7.5); pdf.setFont('helvetica','normal');
      pdf.text(i===0?'1st':i===1?'2nd':i===2?'3rd':`#${i+1}`, rx+5, ry);
      pdf.setTextColor(...BODY); pdf.text(emp.name, rx+20, ry);
      const bx=rx+90, bw=145, pct=eTotal(emp)/(eTotal(sorted[0])||1);
      pdf.setFillColor(...RULE); pdf.roundedRect(bx,ry-3.2,bw,3.8,.5,.5,'F');
      pdf.setFillColor(...rgb); pdf.roundedRect(bx,ry-3.2,bw*pct,3.8,.5,.5,'F');
      pdf.setTextColor(...rgb); pdf.setFontSize(8); pdf.setFont('helvetica','bold'); pdf.text(String(eTotal(emp)),bx+bw+4,ry);
    });
    pdf.setFillColor(240,234,255); pdf.roundedRect(14,200,pw-28,6,1,1,'F');
    pdf.setTextColor(104,54,208); pdf.setFontSize(7.5); pdf.setFont('helvetica','bold');
    pdf.text(`Specialist: ${SPECIALIST.name} (${SPECIALIST.id}) — ${SPECIALIST.role}  ·  IT Processed: ${SPEC_DATA.processed||0}`, pw/2, 203.8, {align:'center'});

    // Employee table
    pdf.addPage(); chrome('Employee Performance Summary — 6 Ranked Employees','Scores across all 14 HR services');
    const cols=['#','Employee','ID',...SERVICES.map(s=>s.label.slice(0,8)),'Total','Avg'];
    const colW=[9,34,15,...SERVICES.map(()=>13),15,12];
    const sx=10, hy=36;
    pdf.setFillColor(...G1); pdf.rect(sx,hy,pw-20,8,'F');
    pdf.setTextColor(255,255,255); pdf.setFontSize(7); pdf.setFont('helvetica','bold');
    let cx=sx; cols.forEach((c,i)=>{pdf.text(c,cx+colW[i]/2,hy+5.5,{align:'center'});cx+=colW[i];});
    sorted.forEach((emp,ri)=>{
      const rowY=hy+8+ri*10;
      pdf.setFillColor(...(ri%2===0?GN:OW)); pdf.rect(sx,rowY,pw-20,10,'F');
      pdf.setDrawColor(...RULE); pdf.setLineWidth(.15); pdf.rect(sx,rowY,pw-20,10,'S');
      const cells=[`#${ri+1}`,emp.name,emp.id,...SERVICES.map(s=>String(emp[s.key]||0)),String(eTotal(emp)),eAvg(emp).toFixed(1)];
      cx=sx; pdf.setFontSize(7);
      cells.forEach((cell,ci)=>{
        if(ci===0){const mc=ri===0?G1:ri===1?[120,120,120]:ri===2?[180,100,30]:MUTED;pdf.setTextColor(...mc);pdf.setFont('helvetica','bold');}
        else if(ci===cols.length-2){pdf.setTextColor(...G1);pdf.setFont('helvetica','bold');}
        else if(ci===cols.length-1){pdf.setTextColor(26,92,180);pdf.setFont('helvetica','bold');}
        else if(ci>2){const v=parseFloat(cell)||0;pdf.setTextColor(...(v>=80?S_H:v>=60?S_M:S_L));pdf.setFont('helvetica','normal');}
        else{pdf.setTextColor(...BODY);pdf.setFont('helvetica','normal');}
        pdf.text(cell,cx+colW[ci]/2,rowY+6.5,{align:'center'}); cx+=colW[ci];
      });
    });

    // Service charts
    updateMsg('Rendering service charts…');
    for (let si=0; si<SERVICES.length; si+=2) {
      pdf.addPage(); chrome('Service Performance Comparison','Employee scores per service — 0 to 100 scale');
      for (let ci=0; ci<2 && si+ci<SERVICES.length; ci++) {
        const svc=SERVICES[si+ci], emps=EA(), vals=emps.map(e=>e[svc.key]||0);
        const ox=10+ci*140, oy=36, cW=133, cH=148;
        pdf.setFillColor(...OW2); pdf.roundedRect(ox,oy,cW,cH,2,2,'F');
        pdf.setDrawColor(...RULE); pdf.setLineWidth(.3); pdf.roundedRect(ox,oy,cW,cH,2,2,'S');
        pdf.setFillColor(...G1); pdf.roundedRect(ox,oy,cW,8,2,2,'F'); pdf.rect(ox,oy+4,cW,4,'F');
        pdf.setTextColor(255,255,255); pdf.setFontSize(8.5); pdf.setFont('helvetica','bold'); pdf.text(svc.label,ox+cW/2,oy+5.8,{align:'center'});
        pdf.setFillColor(...GN); pdf.rect(ox,oy+8,cW,7,'F');
        pdf.setTextColor(...G1); pdf.setFontSize(7.5); pdf.setFont('helvetica','bold');
        pdf.text(`TOTAL: ${sTotal(svc.key)}`,ox+cW*.26,oy+13,{align:'center'});
        pdf.text(`AVG: ${sAvg(svc.key).toFixed(1)}`,ox+cW*.76,oy+13,{align:'center'});
        const bax=ox+32, bay=oy+20, mxV=Math.max(...vals,1), slot=cH/(emps.length+1.5);
        emps.forEach((emp,ei)=>{
          const v=vals[ei], pct=v/mxV, rgb=EC[ei%EC.length];
          const by=bay+slot*(ei+0.5), bh=Math.max(slot*.5,3), bw=Math.max(pct*(cW-38),.5);
          pdf.setFillColor(...RULE); pdf.roundedRect(bax,by-bh/2,cW-38,bh,.4,.4,'F');
          pdf.setFillColor(...rgb); pdf.roundedRect(bax,by-bh/2,bw,bh,.4,.4,'F');
          pdf.setTextColor(...MUTED); pdf.setFontSize(6.5); pdf.setFont('helvetica','normal'); pdf.text(emp.name.split(' ')[0],bax-2,by+2,{align:'right'});
          pdf.setTextColor(...rgb); pdf.setFontSize(7); pdf.setFont('helvetica','bold'); pdf.text(String(v),bax+bw+2,by+2);
        });
      }
      await new Promise(r=>setTimeout(r,0));
    }

    // Leaderboard
    pdf.addPage(); chrome('Performance Leaderboard — 6 Employees','Ranked by cumulative score across all 14 services');
    sorted.forEach((emp,i)=>{
      const rowY=36+i*24, t=eTotal(emp), pct=t/(eTotal(sorted[0])||1);
      const rgb=EC[arr.findIndex(a=>a.id===emp.id)%EC.length];
      pdf.setFillColor(...(i%2===0?GN:OW)); pdf.roundedRect(10,rowY,pw-20,20,1.5,1.5,'F');
      pdf.setDrawColor(...RULE); pdf.setLineWidth(.2); pdf.roundedRect(10,rowY,pw-20,20,1.5,1.5,'S');
      pdf.setFillColor(...rgb); pdf.roundedRect(10,rowY,3.5,20,1,1,'F');
      const mc=i===0?G1:i===1?[120,120,120]:i===2?[180,100,30]:MUTED;
      pdf.setTextColor(...mc); pdf.setFontSize(11); pdf.setFont('helvetica','bold');
      pdf.text(i===0?'1ST':i===1?'2ND':i===2?'3RD':`#${i+1}`,20,rowY+13);
      pdf.setTextColor(...INK); pdf.setFontSize(11.5); pdf.setFont('helvetica','bold'); pdf.text(emp.name,40,rowY+7.5);
      pdf.setTextColor(...MUTED); pdf.setFontSize(8.5); pdf.setFont('helvetica','normal');
      pdf.text(`Avg: ${eAvg(emp).toFixed(1)}   Best: ${eBest(emp).label}   Weakest: ${eWorst(emp).label}`,40,rowY+15);
      const bx=148, by=rowY+7, bw=115, bh=5.5;
      pdf.setFillColor(...RULE); pdf.roundedRect(bx,by,bw,bh,1,1,'F');
      pdf.setFillColor(...rgb); pdf.roundedRect(bx,by,bw*pct,bh,1,1,'F');
      pdf.setTextColor(...rgb); pdf.setFontSize(11); pdf.setFont('helvetica','bold'); pdf.text(String(t),bx+bw+5,rowY+13);
    });
    // Specialist row on leaderboard page
    const sRowY=36+sorted.length*24+4;
    pdf.setFillColor(240,234,255); pdf.roundedRect(10,sRowY,pw-20,20,1.5,1.5,'F');
    pdf.setDrawColor(124,58,237); pdf.setLineWidth(.4); pdf.roundedRect(10,sRowY,pw-20,20,1.5,1.5,'S');
    pdf.setFillColor(124,58,237); pdf.roundedRect(10,sRowY,3.5,20,1,1,'F');
    pdf.setTextColor(104,54,208); pdf.setFontSize(9); pdf.setFont('helvetica','bold'); pdf.text('SPECIALIST — NOT RANKED',20,sRowY+8);
    pdf.setTextColor(...BODY); pdf.setFontSize(10); pdf.text(SPECIALIST.name,40,sRowY+8);
    pdf.setTextColor(...MUTED); pdf.setFontSize(8); pdf.setFont('helvetica','normal');
    const procCount=SPEC_DATA.processed||0, totalDocs=Object.values(SPEC_DATA.doc_scans).reduce((s,d)=>s+(parseInt(d.docs)||0),0);
    pdf.text(`${SPECIALIST.role}  ·  IT Requests Processed: ${procCount}  ·  Total Docs Scanned: ${totalDocs}`,40,sRowY+16);

    pdf.save('HR_Performance_Report.pdf');
    hideOverlay();
  } catch(err) { console.error(err); hideOverlay(); alert('PDF export failed: ' + err.message); }
}
/* ============================================================
   END BLOCK: PDF EXPORT
   ============================================================ */

/* ============================================================
   BLOCK: PPTX EXPORT — WHITE + FOREST GREEN
   ============================================================ */
async function exportPPTX() {
  showOverlay('Generating PowerPoint', 'Building slides…');
  await new Promise(r => setTimeout(r, 200));
  try {
    const pres = new PptxGenJS();
    pres.layout = 'LAYOUT_WIDE'; pres.title = 'HR Performance Dashboard';
    const BG='F0F3F7', LT='E5EBF2', GN='C2F0DC';
    const INK='0D1825', MUTED='68788E', RULE='B0C0D0';
    const G1='0A5C38', G2='138050', G3='1DAD6C', G4='C2F0DC';
    const RED='C43030', AMBER='B57D00', BLUE='1A5CB4', PURPLE='6836D0', TEAL='0E8A8A';
    const SPEC='7C3AED';
    const EAC=[BLUE,G2,PURPLE,'D95B0A',AMBER,TEAL];
    const arr=EA(), gt=GT(), sorted=[...arr].sort((a,b)=>eTotal(b)-eTotal(a));
    const empNames=arr.map(e=>e.name.split(' ')[0]);

    const sh=(sl,ac,title,sub)=>{
      sl.background={color:BG};
      sl.addShape(pres.shapes.RECTANGLE,{x:0,y:0,w:13.3,h:.14,fill:{color:ac}});
      sl.addShape(pres.shapes.LINE,{x:0,y:7.33,w:13.3,h:0,line:{color:RULE,width:.5}});
      sl.addText('HR Performance Analytics · Confidential',{x:.3,y:7.35,w:8,h:.15,fontSize:7,color:MUTED,fontFace:'Calibri'});
      if(title) sl.addText(title,{x:.4,y:.22,w:10,h:.6,fontSize:22,bold:true,color:INK,fontFace:'Calibri'});
      if(sub)   sl.addText(sub,{x:.4,y:.8,w:12.5,h:.28,fontSize:10,color:MUTED,fontFace:'Calibri'});
    };

    // Cover slide
    let sl=pres.addSlide(); sl.background={color:BG};
    sl.addShape(pres.shapes.RECTANGLE,{x:0,y:0,w:.22,h:7.5,fill:{color:G1}});
    sl.addShape(pres.shapes.RECTANGLE,{x:.22,y:0,w:13.08,h:2.35,fill:{color:LT}});
    sl.addText('HR PERFORMANCE ANALYTICS',{x:.5,y:.36,w:12.5,h:.46,fontSize:12,bold:true,color:G1,charSpacing:4,fontFace:'Calibri'});
    sl.addShape(pres.shapes.LINE,{x:.5,y:.92,w:12.4,h:0,line:{color:RULE,width:.6}});
    sl.addText('Performance Dashboard',{x:.5,y:1.04,w:12.4,h:.96,fontSize:44,bold:true,color:INK,fontFace:'Calibri'});
    sl.addText(`Generated ${new Date().toLocaleDateString('en-GB',{year:'numeric',month:'long',day:'numeric'})}`,{x:.5,y:2.5,w:12.4,h:.38,fontSize:12,color:MUTED,fontFace:'Calibri'});
    sl.addShape(pres.shapes.LINE,{x:.5,y:2.94,w:12.4,h:0,line:{color:RULE,width:.5}});
    [{l:'Grand Total',v:String(gt),c:G1},{l:'Team Avg',v:(gt/(arr.length*SERVICES.length)).toFixed(1),c:G2},{l:'Ranked',v:'6',c:BLUE},{l:'Services',v:'14',c:PURPLE}].forEach((k,i)=>{
      const x=.5+i*3.2, y=3.15;
      sl.addShape(pres.shapes.RECTANGLE,{x,y,w:3.0,h:1.68,fill:{color:G4},line:{color:RULE,width:.4}});
      sl.addShape(pres.shapes.RECTANGLE,{x,y,w:3.0,h:.14,fill:{color:k.c}});
      sl.addText(k.l,{x,y:y+.2,w:3.0,h:.32,fontSize:9,color:MUTED,align:'center',fontFace:'Calibri'});
      sl.addText(k.v,{x,y:y+.54,w:3.0,h:.86,fontSize:34,bold:true,color:k.c,align:'center',fontFace:'Calibri'});
    });
    sl.addShape(pres.shapes.RECTANGLE,{x:.5,y:5.0,w:12.5,h:1.92,fill:{color:LT}});
    sl.addText('Top Performers',{x:.65,y:5.1,w:5,h:.3,fontSize:10,bold:true,color:INK,fontFace:'Calibri'});
    sorted.forEach((emp,i)=>{
      const ei=arr.findIndex(a=>a.id===emp.id), col=EAC[ei%EAC.length], ry=5.5+i*.3;
      sl.addShape(pres.shapes.RECTANGLE,{x:.65,y:ry,w:.08,h:.22,fill:{color:col}});
      sl.addText(`${i===0?'1st':i===1?'2nd':i===2?'3rd':'#'+(i+1)}  ${emp.name}`,{x:.8,y:ry,w:4.5,h:.26,fontSize:9.5,color:INK,fontFace:'Calibri'});
      const bx=5.5, bw=6.8, pct=eTotal(emp)/(eTotal(sorted[0])||1);
      sl.addShape(pres.shapes.RECTANGLE,{x:bx,y:ry+.05,w:bw,h:.12,fill:{color:RULE}});
      sl.addShape(pres.shapes.RECTANGLE,{x:bx,y:ry+.05,w:bw*pct,h:.12,fill:{color:col}});
      sl.addText(String(eTotal(emp)),{x:bx+bw+.1,y:ry,w:.7,h:.26,fontSize:9.5,bold:true,color:col,fontFace:'Calibri'});
    });
    sl.addShape(pres.shapes.RECTANGLE,{x:.5,y:6.98,w:12.5,h:.44,fill:{color:'F0EAFF'},line:{color:'DDD0FF',width:.4}});
    sl.addText(`Specialist (Not Ranked): ${SPECIALIST.name} — ${SPECIALIST.role}`,{x:.65,y:7.02,w:12.2,h:.34,fontSize:9.5,color:SPEC,italic:true,fontFace:'Calibri'});

    // Employee summary table
    updateMsg('Building table…');
    sl=pres.addSlide(); sh(sl,G1,'Employee Performance Summary','6 Ranked Employees · 14 Services');
    const tC=['#','Name','ID',...SERVICES.map(s=>s.label.slice(0,8)),'Total','Avg'];
    const tW=[.28,.88,.57,...SERVICES.map(()=>.56),.58,.5];
    const tRows=[[tC.map(c=>({text:c,options:{bold:true,color:'FFFFFF',fontSize:6.5,align:'center',fill:{color:G1},border:{pt:.5,color:RULE}}}))]];
    sorted.forEach((emp,ri)=>{
      const rf=ri%2===0?BG:GN, row=[];
      row.push({text:`#${ri+1}`,options:{bold:true,color:ri===0?AMBER:ri===1?MUTED:ri===2?'8C5000':MUTED,fontSize:6.5,align:'center',fill:{color:rf},border:{pt:.3,color:RULE}}});
      row.push({text:emp.name,options:{color:INK,fontSize:7,fill:{color:rf},border:{pt:.3,color:RULE}}});
      row.push({text:emp.id,options:{color:MUTED,fontSize:6.5,align:'center',fill:{color:rf},border:{pt:.3,color:RULE}}});
      SERVICES.forEach(s=>{const v=emp[s.key]||0;row.push({text:String(v),options:{color:v>=80?G1:v>=60?AMBER:RED,fontSize:7,align:'center',fill:{color:rf},border:{pt:.3,color:RULE}}});});
      row.push({text:String(eTotal(emp)),options:{bold:true,color:G1,fontSize:8,align:'center',fill:{color:rf},border:{pt:.3,color:RULE}}});
      row.push({text:eAvg(emp).toFixed(1),options:{bold:true,color:BLUE,fontSize:7,align:'center',fill:{color:rf},border:{pt:.3,color:RULE}}});
      tRows.push(row);
    });
    sl.addTable(tRows,{x:.3,y:1.12,w:12.7,colW:tW,rowH:.41,border:{pt:.3,color:RULE}});

    // Service bar charts
    updateMsg('Adding service charts…');
    for(let si=0;si<SERVICES.length;si+=2){
      sl=pres.addSlide(); sh(sl,G2,'Service Performance Comparison','Employee scores — 0 to 100 scale');
      for(let ci=0;ci<2&&si+ci<SERVICES.length;ci++){
        const svc=SERVICES[si+ci], ox=.3+ci*6.65, oy=1.08, cw=6.2;
        const vals=arr.map(e=>e[svc.key]||0);
        sl.addShape(pres.shapes.RECTANGLE,{x:ox,y:oy,w:cw,h:.54,fill:{color:LT},line:{color:RULE,width:.4}});
        sl.addShape(pres.shapes.RECTANGLE,{x:ox,y:oy,w:.12,h:.54,fill:{color:G1}});
        sl.addText(svc.label,{x:ox+.18,y:oy+.07,w:cw*.55,h:.4,fontSize:12,bold:true,color:INK,fontFace:'Calibri'});
        sl.addText(`Total: ${sTotal(svc.key)}  ·  Avg: ${sAvg(svc.key).toFixed(1)}`,{x:ox+cw*.5,y:oy+.14,w:cw*.48,h:.28,fontSize:9,color:MUTED,align:'right',fontFace:'Calibri'});
        sl.addChart(pres.charts.BAR,[{name:svc.label,labels:empNames,values:vals}],{x:ox,y:oy+.56,w:cw,h:5.6,barDir:'col',chartColors:EAC,chartArea:{fill:{color:BG}},catAxisLabelColor:MUTED,valAxisLabelColor:MUTED,catAxisLineShow:false,valAxisLineShow:false,valGridLine:{color:RULE,size:.4},catGridLine:{style:'none'},valAxisMinVal:0,valAxisMaxVal:100,showValue:true,dataLabelColor:INK,dataLabelFontSize:8,showLegend:false});
      }
      await new Promise(r=>setTimeout(r,0));
    }

    // Distribution charts
    updateMsg('Adding distribution…');
    for(let si=0;si<SERVICES.length;si+=3){
      sl=pres.addSlide(); sh(sl,PURPLE,'Service Distribution','Score share per employee');
      for(let ci=0;ci<3&&si+ci<SERVICES.length;ci++){
        const svc=SERVICES[si+ci], ox=.25+ci*4.36, oy=1.08, cw=4.1;
        const vals=arr.map(e=>e[svc.key]||0);
        sl.addShape(pres.shapes.RECTANGLE,{x:ox,y:oy,w:cw,h:.48,fill:{color:LT},line:{color:RULE,width:.4}});
        sl.addShape(pres.shapes.RECTANGLE,{x:ox,y:oy,w:.12,h:.48,fill:{color:PURPLE}});
        sl.addText(svc.label,{x:ox+.16,y:oy+.09,w:cw-.22,h:.32,fontSize:10,bold:true,color:INK,fontFace:'Calibri'});
        sl.addChart(pres.charts.DOUGHNUT,[{name:svc.label,labels:empNames,values:vals}],{x:ox,y:oy+.5,w:cw,h:5.78,holeSize:55,chartColors:EAC,chartArea:{fill:{color:BG}},dataLabelFontSize:7.5,dataLabelColor:INK,showPercent:true,legendPos:'b',showLegend:true,legendFontSize:8,legendColor:MUTED});
      }
      await new Promise(r=>setTimeout(r,0));
    }

    // Leaderboard slide
    updateMsg('Building leaderboard…');
    sl=pres.addSlide(); sh(sl,AMBER,'Performance Leaderboard','6 Employees ranked by cumulative score');
    sl.addText(`Grand Total: ${gt}`,{x:9.5,y:.22,w:3.5,h:.56,fontSize:15,bold:true,color:G1,align:'right',fontFace:'Calibri'});
    sorted.forEach((emp,i)=>{
      const ry=1.14+i*.8, t=eTotal(emp), pct=t/(eTotal(sorted[0])||1);
      const ei=arr.findIndex(a=>a.id===emp.id), col=EAC[ei%EAC.length];
      sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:ry,w:12.7,h:.68,fill:{color:i%2===0?GN:BG},line:{color:RULE,width:.3}});
      sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:ry,w:.14,h:.68,fill:{color:col}});
      sl.addText(i===0?'🥇':i===1?'🥈':i===2?'🥉':`#${i+1}`,{x:.5,y:ry+.07,w:.64,h:.52,fontSize:15,align:'center',fontFace:'Segoe UI Emoji'});
      sl.addText(emp.name,{x:1.25,y:ry+.06,w:4,h:.28,fontSize:12,bold:true,color:INK,fontFace:'Calibri'});
      sl.addText(`Avg: ${eAvg(emp).toFixed(1)}  ·  Best: ${eBest(emp).label}  ·  Weakest: ${eWorst(emp).label}`,{x:1.25,y:ry+.38,w:5.4,h:.24,fontSize:8,color:MUTED,fontFace:'Calibri'});
      const bx=6.9, bw=5.4;
      sl.addShape(pres.shapes.RECTANGLE,{x:bx,y:ry+.21,w:bw,h:.26,fill:{color:RULE}});
      if(pct>0) sl.addShape(pres.shapes.RECTANGLE,{x:bx,y:ry+.21,w:bw*pct,h:.26,fill:{color:col}});
      sl.addText(String(t),{x:bx+bw+.12,y:ry+.12,w:.85,h:.42,fontSize:14,bold:true,color:col,fontFace:'Calibri'});
    });
    // Specialist row
    const sry=1.14+sorted.length*.8+.1;
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:sry,w:12.7,h:.68,fill:{color:'F0EAFF'},line:{color:'DDD0FF',width:.4}});
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:sry,w:.14,h:.68,fill:{color:SPEC}});
    sl.addText('★',{x:.5,y:sry+.07,w:.64,h:.52,fontSize:15,align:'center',color:SPEC,fontFace:'Calibri'});
    sl.addText(SPECIALIST.name,{x:1.25,y:sry+.06,w:4,h:.28,fontSize:12,bold:true,color:INK,fontFace:'Calibri'});
    const procC=SPEC_DATA.processed||0, totDocC=Object.values(SPEC_DATA.doc_scans).reduce((s,d)=>s+(parseInt(d.docs)||0),0);
    sl.addText(`Specialist — Not Ranked  ·  IT Processed: ${procC}  ·  Docs Scanned: ${totDocC}`,{x:1.25,y:sry+.38,w:8,h:.24,fontSize:8,color:SPEC,fontFace:'Calibri'});

    // Insights slide
    updateMsg('Adding insights…');
    sl=pres.addSlide(); sh(sl,G1,'Performance Insights','Total scores and gap analysis — 6 employees');
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:1.02,w:6.1,h:.44,fill:{color:LT},line:{color:RULE,width:.3}});
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:1.02,w:.12,h:.44,fill:{color:BLUE}});
    sl.addText('Total Score per Employee',{x:.48,y:1.09,w:5.9,h:.3,fontSize:10,bold:true,color:INK,fontFace:'Calibri'});
    sl.addChart(pres.charts.BAR,[{name:'Total',labels:sorted.map(e=>e.name.split(' ')[0]),values:sorted.map(e=>eTotal(e))}],{x:.3,y:1.48,w:6.1,h:5.8,barDir:'col',chartColors:sorted.map(e=>EAC[arr.findIndex(a=>a.id===e.id)%EAC.length]),chartArea:{fill:{color:BG}},catAxisLabelColor:MUTED,valAxisLabelColor:MUTED,valGridLine:{color:RULE,size:.4},catGridLine:{style:'none'},showValue:true,dataLabelColor:INK,dataLabelFontSize:9,showLegend:false});
    const gaps=SERVICES.map(s=>{const v=arr.map(e=>e[s.key]||0);return Math.max(...v)-Math.min(...v);});
    sl.addShape(pres.shapes.RECTANGLE,{x:6.9,y:1.02,w:6.1,h:.44,fill:{color:LT},line:{color:RULE,width:.3}});
    sl.addShape(pres.shapes.RECTANGLE,{x:6.9,y:1.02,w:.12,h:.44,fill:{color:AMBER}});
    sl.addText('Performance Gap (Max − Min)',{x:7.08,y:1.09,w:5.9,h:.3,fontSize:10,bold:true,color:INK,fontFace:'Calibri'});
    sl.addChart(pres.charts.BAR,[{name:'Gap',labels:SERVICES.map(s=>s.label),values:gaps}],{x:6.9,y:1.48,w:6.1,h:5.8,barDir:'bar',chartColors:gaps.map(g=>g>40?RED:g>20?AMBER:G2),chartArea:{fill:{color:BG}},catAxisLabelColor:MUTED,valAxisLabelColor:MUTED,valGridLine:{color:RULE,size:.4},catGridLine:{style:'none'},showValue:true,dataLabelColor:INK,dataLabelFontSize:8,showLegend:false});

    // Team analytics slide
    sl=pres.addSlide(); sh(sl,G2,'Team Analytics','Avg scores and service volume');
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:1.02,w:6.1,h:.44,fill:{color:LT},line:{color:RULE,width:.3}});
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:1.02,w:.12,h:.44,fill:{color:G2}});
    sl.addText('Average Score per Employee',{x:.48,y:1.09,w:5.9,h:.3,fontSize:10,bold:true,color:INK,fontFace:'Calibri'});
    sl.addChart(pres.charts.LINE,[{name:'Avg',labels:arr.map(e=>e.name.split(' ')[0]),values:arr.map(e=>parseFloat(eAvg(e).toFixed(1)))}],{x:.3,y:1.48,w:6.1,h:5.8,lineSize:2.5,lineSmooth:true,chartColors:[G1],chartArea:{fill:{color:BG}},catAxisLabelColor:MUTED,valAxisLabelColor:MUTED,valGridLine:{color:RULE,size:.4},catGridLine:{style:'none'},valAxisMinVal:0,valAxisMaxVal:100,showValue:true,dataLabelColor:INK,dataLabelFontSize:9,showLegend:false});
    sl.addShape(pres.shapes.RECTANGLE,{x:6.9,y:1.02,w:6.1,h:.44,fill:{color:LT},line:{color:RULE,width:.3}});
    sl.addShape(pres.shapes.RECTANGLE,{x:6.9,y:1.02,w:.12,h:.44,fill:{color:PURPLE}});
    sl.addText('Service Volume Distribution',{x:7.08,y:1.09,w:5.9,h:.3,fontSize:10,bold:true,color:INK,fontFace:'Calibri'});
    sl.addChart(pres.charts.PIE,[{name:'Vol',labels:SERVICES.map(s=>s.label),values:SERVICES.map(s=>sTotal(s.key))}],{x:6.9,y:1.48,w:6.1,h:5.8,chartColors:EAC,chartArea:{fill:{color:BG}},showPercent:true,dataLabelColor:INK,dataLabelFontSize:8,legendPos:'r',showLegend:true,legendFontSize:9,legendColor:MUTED});

    // Specialist slide
    updateMsg('Adding specialist data…');
    sl=pres.addSlide(); sh(sl,SPEC,'Other Actions — Specialist Employee',`${SPECIALIST.name} · IT Requests & Documents Scanned`);
    [{l:'IT Processed',v:String(SPEC_DATA.processed||0),c:G1},{l:'Pending',v:String(SPEC_DATA.pending||0),c:AMBER},{l:'Rejected',v:String(SPEC_DATA.rejected||0),c:RED},{l:'Docs Scanned',v:String(totDocC),c:BLUE}].forEach((k,i)=>{
      const x=.5+i*3.2, y=1.08;
      sl.addShape(pres.shapes.RECTANGLE,{x,y,w:3.0,h:1.6,fill:{color:'F0EAFF'},line:{color:'DDD0FF',width:.4}});
      sl.addShape(pres.shapes.RECTANGLE,{x,y,w:3.0,h:.14,fill:{color:k.c}});
      sl.addText(k.l,{x,y:y+.2,w:3.0,h:.3,fontSize:9,color:MUTED,align:'center',fontFace:'Calibri'});
      sl.addText(k.v,{x,y:y+.52,w:3.0,h:.78,fontSize:28,bold:true,color:k.c,align:'center',fontFace:'Calibri'});
    });
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:2.85,w:12.7,h:.42,fill:{color:LT}});
    sl.addShape(pres.shapes.RECTANGLE,{x:.3,y:2.85,w:.12,h:.42,fill:{color:TEAL}});
    sl.addText('Documents Scanned per HR Service',{x:.5,y:2.91,w:12.4,h:.28,fontSize:10,bold:true,color:INK,fontFace:'Calibri'});
    const dsL=SERVICES.map(s=>s.label);
    const dsV=SERVICES.map(s=>parseInt((SPEC_DATA.doc_scans[s.key]||{}).docs)||0);
    sl.addChart(pres.charts.BAR,[{name:'Docs',labels:dsL,values:dsV}],{x:.3,y:3.3,w:12.7,h:3.88,barDir:'col',chartColors:EAC,chartArea:{fill:{color:BG}},catAxisLabelColor:MUTED,valAxisLabelColor:MUTED,valGridLine:{color:RULE,size:.4},catGridLine:{style:'none'},showValue:true,dataLabelColor:INK,dataLabelFontSize:8,showLegend:false});

    await pres.writeFile({ fileName: 'HR_Performance_Dashboard.pptx' });
    hideOverlay();
  } catch(err) { console.error(err); hideOverlay(); alert('PPTX export failed: ' + err.message); }
}
/* ============================================================
   END BLOCK: PPTX EXPORT
   ============================================================ */

/* ============================================================
   BLOCK: EXPORT BOTH (PDF + PPTX sequentially)
   ============================================================ */
async function exportBoth() {
  await exportPDF();
  await new Promise(r => setTimeout(r, 300));
  await exportPPTX();
}
/* ============================================================
   END BLOCK: EXPORT BOTH
   ============================================================ */

/* ============================================================
   BLOCK: INITIALISATION
   Runs once when the page loads.
   Builds the entry form and renders all charts.
   ============================================================ */
buildEntryForm();
renderAll();
/* ============================================================
   END BLOCK: INITIALISATION
   ============================================================ */
</script>
<!-- ============================================================
     END BLOCK: JAVASCRIPT — DATA CONFIG
     ============================================================ -->
</body>
</html>

HR Performance Dashboard
Generating…
Please wait
✓ Scores saved — charts updated
6 Ranked Employees
1 Specialist Employee
14 Services
Grand Total:
IT Processed:
Performance Intelligence — 6 Ranked Employees
🏆 Overall Ranking — 6 Employees
📡Team Average by Service
Individual Radar Profiles
Comparative Analysis
KPI Overview — 6 Ranked Employees
Employee Performance Cards
Service Totals
Service Comparison — 6 Employees
Score Distribution by Service
Specialist Employee — Internal Transfer Focus
?
Specialist Employee
Total IT Requests Processed
0
Enter data via Data Entry tab
Processed
0
Pending
0
Rejected
0
Docs Scanned
0
Total Pages Scanned
0
🗂️Documents Scanned per HR Service
📝
Manual Data Entry
Select an employee from the dropdown, enter their scores for each service (0–100), then click Save & Update Charts. All charts and rankings update instantly. Use the Specialist section at the bottom to enter Internal Transfer and document scan data.
Ranked Employee Scores (0 – 100 per service)
Specialist Employee — Internal Transfer & Document Data
?
Specialist Employee
Internal Transfer Requests
Documents Scanned per Service

Comments

Popular posts from this blog

Step-by-Step Guide to Renewing Your Company’s Civil Defence License in KSA—Fast and Hassle-Free

Civil Defence is a regulatory body tasked by the government to protect lives and properties regionally and internationally. As a company, it is compulsory to get a Civil Defense License before it can operate a business and has to undergo compliance to the Fire Fighting System, Safety and Security - You may refer to step by step guide on how to get around this procedure.   Step 1 Secured the below certificates and other documents to be submitted to Salamah Online, among these are as follows: 1. Facility Contract 2. CR 3. Certificate of Insurance            a.  Comprehensive          b.  Workmen            c.  Employers          d.  Property All Risks 4. Municipal & Professional License 5. CCTV Certification from Police 6. Enjaz / Police Clearance 7. Annual Maintenance Contract—Firefighting Equipment 6. Salama Code Step 2   Share a...

AI Solution Proposal

  ⭐ AI Solution Proposal 1. What HR challenges does your team face? Our unit handles , absorbs , and resolves a high volume of employee concerns across wages, salary advances, education programs, timekeeping, OT, hiring, separation, housing, HOP, medical insurance, Muskan, and home loans. We also manage grievance cases and investigations that demand strict documentation and policy alignment. 2. How is this handled today? HR staff read , sort , search , interpret , draft , and log everything manually—policies, SOPs, records, and past cases. Grievance and investigation cases require HR to collect , verify , and compile facts by hand. 3. What makes it slow, costly, or frustrating? Manual classification drags response time Searching policies and past cases consumes hours Repetitive drafting drains productivity Inconsistent answers trigger rework Investigations stall due to scattered information Tracking cases manually burdens the team 4. Which AI tool or approach could addres...