PageSourceSearch

https://mvs-employee.pages.dev/

html mvs-employee.pages.dev collected 2026-10-03 05:40:35 UTC 318,934 bytes, 2,761 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8"/>
5<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6<title>MVS Foundation — Employee Portal</title>
7<link rel="preconnect" href="https://fonts.googleapis.com">
8<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
9<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tabler-icons/3.34.1/tabler-icons.min.css">
10<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
vendor: 1 bytes, line 10
10
11<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
11
12<style>
13  :root{
14    --bg0:#08080a; --bg1:#111114; --navy:#17171c; --navy2:#25252b;
15    --gold:#c89b3c; --gold-soft:#e8c977; --gold-ink:#e8c977;
16    --txt:#eef2f8; --muted:#a9b6c9; --faint:#7c889a;
17    --line:rgba(255,255,255,.14); --card:rgba(255,255,255,.055); --card-2:rgba(255,255,255,.085);
18    --ok:#3ec77f; --ok-d:#1f9d57; --red:#e0533f; --warn:#e8c977;
19  }
20  body.night{
21    --bg0:#04060a; --bg1:#0a0e16; --navy:#0f0f12; --navy2:#1e1e25;
22    --card:rgba(255,255,255,.05); --card-2:rgba(255,255,255,.075); --line:rgba(255,255,255,.09);
23    background:radial-gradient(1100px 600px at 12% -8%,#0a0a0c 0,transparent 58%),
24      linear-gradient(160deg,#06090f 0,#0a0e16 55%,#000 100%)!important;background-attachment:fixed}
25  body.night::before{opacity:.03}
26  body.night .topbar{background:rgba(0,0,0,.66)}
27  body.night .bpanel,body.night #toast,body.night .lcard,body.night .mcard{background:#0c1119}
28  body.night .login{background:radial-gradient(1100px 600px at 12% -8%,#0a0a0c 0,transparent 58%),linear-gradient(160deg,#06090f,#000)}
29  *{box-sizing:border-box;margin:0;padding:0}
30  body{font-family:'Outfit',sans-serif;color:var(--txt);min-height:100vh;padding-bottom:60px;
31    background:radial-gradient(1100px 600px at 12% -8%,#25252b 0,transparent 58%),
32      radial-gradient(900px 500px at 112% 8%,#08080a 0,transparent 55%),
33      linear-gradient(160deg,#111114 0,#17171c 48%,#08080a 100%);background-attachment:fixed}
34  body::before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.04;z-index:0;
35    background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='6' height='6'><circle cx='1' cy='1' r='.6' fill='white'/></svg>");}
36  .wrap{max-width:1000px;margin:0 auto;padding:0 16px;position:relative;z-index:1}
37  /* topbar */
38  .topbar{position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:13px;padding:13px 20px;
39    background:rgba(11,18,30,.72);backdrop-filter:blur(16px);border-bottom:1px solid var(--line)}
40  .topbar img{width:42px;height:42px;border-radius:11px;object-fit:cover;box-shadow:0 4px 14px -4px rgba(0,0,0,.6)}
41  #topLogo{border-radius:50%!important;transition:border .2s,box-shadow .2s}
42  #topLogo.has-photo{border:2px solid rgba(232,201,119,.55);box-shadow:0 2px 12px -3px rgba(232,201,119,.6)}
43  .tback{color:var(--gold-soft)}
44  .topbar .tt b{font-size:15px;font-weight:600;display:block;line-height:1.15}
45  .topbar .tt span{font-size:11px;color:var(--gold-soft);letter-spacing:.04em}
46  .topbar .sp{flex:1}
47  .tbtn{background:var(--card);border:1px solid var(--line);color:var(--muted);width:40px;height:40px;border-radius:11px;cursor:pointer;font-size:17px;display:grid;place-items:center;transition:.15s;position:relative}
48  .tbtn:hover{color:var(--gold-soft);border-color:rgba(200,155,60,.4)}
49  .bell{background:linear-gradient(180deg,rgba(224,83,63,.22),rgba(224,83,63,.1))!important;border-color:rgba(224,83,63,.5)!important;color:#ffd9d0!important;animation:bg 2.4s ease-in-out infinite}
50  @keyframes bg{0%,100%{box-shadow:0 0 0 0 rgba(224,83,63,.5)}50%{box-shadow:0 0 0 7px rgba(224,83,63,0)}}
51  .bdot{position:absolute;top:-5px;right:-4px;min-width:18px;height:18px;padding:0 4px;border-radius:9px;background:#e0533f;color:#fff;font-size:10.5px;font-weight:700;display:grid;place-items:center;box-shadow:0 0 0 2px #111114}
52  .spin{animation:sp 1s linear infinite}@keyframes sp{to{transform:rotate(360deg)}}
53  /* bell panel */
54  .bpanel{position:absolute;top:60px;right:16px;width:min(360px,92vw);max-height:60vh;overflow:auto;background:#17171c;border:1px solid var(--line);border-radius:14px;box-shadow:0 22px 55px -12px rgba(0,0,0,.8);padding:7px;display:none;z-index:130}
55  .bitem{background:rgba(255,255,255,.06);border:1px solid var(--line);border-radius:10px;padding:10px 12px;margin:6px 4px;font-size:13px;line-height:1.5}
56  .bitem.urgent{border-color:rgba(224,83,63,.5);background:rgba(224,83,63,.1)}
57  .bitem b{display:block;margin-bottom:2px} .bitem .bt{font-size:11px;color:var(--faint);margin-top:4px}
58  .btag{font-size:10px;background:#e0533f;color:#fff;padding:1px 7px;border-radius:6px;margin-left:6px}
59  .ntag-new{display:inline-block;font-size:9.5px;font-weight:800;letter-spacing:.06em;background:linear-gradient(135deg,#2f8f5b,#4ec77f);color:#fff;padding:2px 8px;border-radius:20px;margin-left:8px;vertical-align:middle;animation:blinkNew 1.1s ease-in-out infinite}
60  @keyframes blinkNew{0%,100%{opacity:1;box-shadow:0 0 0 0 rgba(78,199,127,.55)}50%{opacity:.5;box-shadow:0 0 0 5px rgba(78,199,127,0)}}
61  /* CRM strip */
62  .crm{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin:20px 0 6px}
63  @media(max-width:760px){.crm{grid-template-columns:repeat(2,1fr)}.crm .ccard:last-child:nth-child(odd){grid-column:1/-1}}
64  .ccard{background:linear-gradient(160deg,rgba(255,255,255,.075),rgba(255,255,255,.025));border:1px solid rgba(255,255,255,.13);border-radius:16px;padding:16px 18px;position:relative;overflow:hidden;box-shadow:0 10px 30px -16px rgba(0,0,0,.7),inset 0 1px 0 rgba(255,255,255,.05);transition:transform .18s ease,border-color .18s ease}
65  .ccard:hover{border-color:rgba(200,155,60,.5);transform:translateY(-2px)}
66  .ccard::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:linear-gradient(180deg,var(--gold-soft),var(--gold))}
67  .ccard::after{content:"";position:absolute;right:-24px;top:-24px;width:82px;height:82px;border-radius:50%;background:radial-gradient(circle,rgba(200,155,60,.10),transparent 70%);pointer-events:none}
68  .ccard .cl{font-size:11px;color:var(--faint);text-transform:uppercase;letter-spacing:.05em;display:flex;align-items:center;gap:6px}
69  .ccard .cv{font-size:17px;font-weight:700;margin-top:8px;line-height:1.3;letter-spacing:-.1px}
70  .ccard .cv small{font-size:12px;font-weight:500;color:var(--muted)}
71  .stars i{color:#3a3a44;font-size:15px}.stars i.on{color:var(--gold-soft)}
72  /* hero */
73  .ref{background:linear-gradient(150deg,rgba(200,155,60,.14),rgba(92,92,104,.25));border:1px solid rgba(200,155,60,.35);border-radius:18px;padding:24px 26px;margin:14px 0;box-shadow:0 0 40px -18px rgba(200,155,60,.4)}
74  .ref h1{font-size:23px;font-weight:700} .ref h1 .wave{display:inline-block;animation:wv 2.2s ease-in-out infinite;transform-origin:70% 70%}
75  @keyframes wv{0%,60%,100%{transform:rotate(0)}10%,30%{transform:rotate(14deg)}20%{transform:rotate(-8deg)}}
76  .chip{display:inline-flex;align-items:center;gap:7px;margin-top:11px;font-size:13px;color:var(--gold-soft);background:rgba(200,155,60,.12);border:1px solid rgba(200,155,60,.35);padding:6px 13px;border-radius:999px}
77  .ref p{color:var(--muted);font-size:14px;margin-top:12px;line-height:1.6}
78  .ref-top{display:flex;align-items:center;gap:14px}
79  .ref-av{width:58px;height:58px;border-radius:50%;background:linear-gradient(140deg,#f2d98e,var(--gold-soft,#e8c977) 42%,#c2922f);color:#1a1305;display:grid;place-items:center;font-weight:800;font-size:26px;flex:none;letter-spacing:-.5px;box-shadow:0 9px 24px -9px rgba(232,201,119,.7),0 0 0 4px rgba(200,155,60,.13),inset 0 2px 5px rgba(255,255,255,.45),inset 0 -4px 7px rgba(120,85,20,.28)}
80  .ref-hi{flex:1;min-width:0}
81  .ref-hi p{margin-top:6px}
82  .ref-chips{display:flex;flex-wrap:wrap;gap:9px;margin-top:16px}
83  .ref-chips .chip{margin-top:0}
84  /* progress */
85  .card{background:linear-gradient(160deg,rgba(255,255,255,.055),rgba(255,255,255,.02));border:1px solid rgba(255,255,255,.11);border-radius:16px;padding:20px 22px;margin:14px 0;box-shadow:0 8px 28px -18px rgba(0,0,0,.6)}
86  .sectitle{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-soft);margin-bottom:14px;display:flex;align-items:center;gap:9px}
87  .progbar{height:9px;border-radius:6px;background:rgba(255,255,255,.08);overflow:hidden;margin-bottom:14px}
88  .progfill{height:100%;background:linear-gradient(90deg,var(--gold),var(--gold-soft));border-radius:6px;transition:width .6s}
89  .pstep{display:flex;align-items:center;gap:11px;padding:8px 0;font-size:14px}
90  .pi{width:26px;height:26px;border-radius:50%;border:1px solid var(--line);display:grid;place-items:center;font-size:13px;flex:none}
91  .pstep.done .pi{background:var(--ok-d);border-color:transparent;color:#fff} .pstep.done{color:var(--txt)}
92  .pstep.todo{color:var(--warn);font-weight:600} .pstep.todo .pi{border-color:rgba(232,201,119,.6);color:var(--warn);animation:bg 1.3s ease-in-out infinite}
93  .pmsg{margin-top:10px;font-size:13px}
94  /* tabs */
95  .tabs{display:flex;flex-wrap:wrap;gap:8px;padding:4px 0 2px;margin-top:6px}
96  .tab{white-space:nowrap;background:var(--card);border:1px solid var(--line);color:var(--muted);padding:10px 15px;border-radius:11px;cursor:pointer;font-family:inherit;font-size:13.5px;display:flex;align-items:center;gap:7px;transition:.15s}
97  .tab:hover{color:var(--gold-soft)} .tab.active{background:linear-gradient(180deg,var(--gold-soft),var(--gold));
97color:#1a1207;border-color:transparent;font-weight:600}
98  .panel{display:none;animation:fade .3s} .panel.show{display:block}@keyframes fade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
99  /* doc rows */
100  .drow{display:flex;align-items:center;gap:14px;background:var(--card-2);border:1px solid var(--line);border-radius:13px;padding:15px 17px;margin-bottom:11px}
101  .drow .ic{width:42px;height:42px;border-radius:11px;display:grid;place-items:center;font-size:20px;flex:none;background:rgba(200,155,60,.16);color:var(--gold-soft)}
102  .drow .dt{flex:1} .drow .dt b{font-size:14.5px} .drow .dt span{display:block;font-size:12px;color:var(--faint);margin-top:2px}
103  .dl{background:linear-gradient(180deg,var(--gold-soft),var(--gold));color:#1a1207;border:none;border-radius:10px;padding:9px 15px;font-family:inherit;font-weight:600;font-size:13px;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
104  .dl.lock{background:rgba(255,255,255,.07);color:var(--faint);cursor:not-allowed}
105  .dl.ghost{background:var(--card);color:var(--gold-soft);border:1px solid var(--line)}
106  .empty{color:var(--faint);font-size:13.5px;padding:14px;text-align:center}
107  .kv{display:grid;grid-template-columns:auto 1fr;gap:9px 18px;font-size:14px}.kv .k{color:var(--faint)}
108  .kv .k i{color:var(--gold-soft);margin-right:7px;font-size:15px;vertical-align:-1px}
109  .dept-sec{grid-column:1/-1;display:flex;align-items:center;gap:8px;color:var(--gold-soft);font-weight:800;font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;margin:6px 0 2px;padding-bottom:7px;border-bottom:1px solid var(--line)}
110  .dept-sec:not(:first-child){margin-top:14px}
111  .kv .vhi{color:var(--gold-soft);font-weight:800}
112  .dept-block{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:15px;padding:14px 16px;margin-bottom:14px;box-shadow:0 8px 26px -18px rgba(0,0,0,.7)}
113  .dept-block:last-of-type{margin-bottom:0}
114  .dept-bh{display:flex;align-items:center;gap:8px;color:var(--gold-soft);font-weight:800;font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;margin-bottom:8px;padding-bottom:9px;border-bottom:1px solid var(--line)}
115  .dept-bh i{font-size:15px}
116  .dq{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:8px 0;border-bottom:1px dashed rgba(255,255,255,.06)}
117  .dq:last-child{border-bottom:none}
118  .dq-k{color:var(--faint);font-size:13.5px;display:flex;align-items:center;gap:8px;flex-shrink:0}
119  .dq-k i{color:var(--gold-soft);font-size:15px;vertical-align:-1px}
120  .dq-v{font-weight:600;text-align:right;font-size:14px}
121  .dq-v.hi{color:var(--gold-soft);font-weight:800}
122  .dq-long{padding:9px 0;border-bottom:1px dashed rgba(255,255,255,.06)}
123  .dq-long .dq-k{margin-bottom:7px}
124  .dq-longv{font-size:13.5px;line-height:1.62;color:var(--txt);font-weight:500;text-align:left}
125  .dept-brows>.dq:last-child,.dept-brows>.dq-long:last-child{border-bottom:none;padding-bottom:0}
126  @media(max-width:420px){.dq{gap:10px}.dq-k{font-size:12.5px}.dq-v{font-size:13px}.dq-longv{font-size:13px}}
127  .sal-annex{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:16px;padding:16px 16px 14px;margin-bottom:18px}
128  .sa-head{display:flex;align-items:center;gap:9px;font-weight:800;font-size:15px;margin-bottom:14px}
129  .sa-head i{color:var(--gold-soft)}
130  .sa-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
131  @media(max-width:620px){.sa-grid{grid-template-columns:1fr}}
132  .sa-col{background:rgba(0,0,0,.16);border:1px solid var(--line);border-radius:13px;overflow:hidden}
133  .sa-ctitle{font-weight:800;font-size:12px;letter-spacing:.06em;text-transform:uppercase;padding:10px 14px;color:#fff}
134  .sa-ctitle.earn{background:rgba(58,160,110,.16);color:#7fe0ad;border-bottom:1px solid rgba(58,160,110,.22)}
135  .sa-ctitle.ded{background:rgba(224,120,60,.14);color:#ffb488;border-bottom:1px solid rgba(224,120,60,.22)}
136  .sa-row{display:flex;justify-content:space-between;gap:10px;padding:9px 14px;font-size:13.5px;border-bottom:1px solid rgba(255,255,255,.05)}
137  .sa-row span{color:var(--muted)}.sa-row b{color:#fff;font-weight:600}
138  .sa-total{display:flex;justify-content:space-between;gap:10px;padding:11px 14px;font-weight:800;font-size:13.5px}
139  .sa-total.earn{background:rgba(58,160,110,.1);color:#7fe0ad}
140  .sa-total.ded{background:rgba(224,120,60,.1);color:#ffb488}
141  .sa-net{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:12px;background:linear-gradient(135deg,#153a2b,#0f2a1f);border:1px solid rgba(58,160,110,.3);border-left:4px solid #3aa06e;border-radius:13px;padding:13px 16px}
142  .sa-net span{color:#c9e8d8;font-weight:700;font-size:13.5px}
143  .sa-net b{color:#8fe9b5;font-weight:800;font-size:20px}
144  .sa-sub{margin-top:10px;font-size:12px;color:var(--faint);line-height:1.55}
145  .sa-sub b{color:var(--gold-soft)}
146  .inp,textarea,select{width:100%;background:rgba(0,0,0,.2);border:1px solid var(--line);border-radius:11px;padding:11px 13px;color:#fff;font-family:inherit;font-size:14px;margin-top:6px}
147  .inp:focus,textarea:focus,select:focus{outline:none;border-color:var(--gold)}
148  textarea{resize:vertical;min-height:64px} label{font-size:12.5px;color:var(--muted)}
149  .btn{background:linear-gradient(180deg,var(--gold-soft),var(--gold));
149color:#1a1207;border:none;border-radius:11px;padding:12px 18px;font-family:inherit;font-weight:600;font-size:14px;cursor:pointer;margin-top:10px}
150  .btn:disabled{filter:grayscale(.4) brightness(.85)}
151  .stars-in i{font-size:26px;color:#3a3a44;cursor:pointer;transition:.1s}.stars-in i.on{color:var(--gold-soft)}
152  .slip{background:var(--card-2);border:1px solid var(--line);border-radius:13px;padding:15px 17px;margin-bottom:11px;display:flex;align-items:center;gap:13px;flex-wrap:wrap}
153  .slip .m{font-weight:600;font-size:15px;flex:1}
154  .cmp-item{background:var(--card-2);border:1px solid var(--line);border-radius:12px;padding:12px 14px;margin-bottom:9px;font-size:13px}
155  .cmp-item .ctop{display:flex;justify-content:space-between;gap:8px;margin-bottom:6px}
156  .cs{font-size:11px;padding:2px 9px;border-radius:7px}.cs.open{background:rgba(232,201,119,.2);color:var(--gold-soft)}.cs.resolved{background:rgba(62,199,127,.2);color:var(--ok)}
157  .creply{margin-top:7px;padding:8px 11px;background:rgba(62,199,127,.08);border-left:2px solid var(--ok);border-radius:6px;color:#cfeede;font-size:12.5px}
158  .reqmore{margin-top:4px;background:var(--card);border:1px solid var(--line);color:var(--gold-soft);font-family:inherit;font-size:12.5px;font-weight:600;border-radius:9px;padding:8px 14px;cursor:pointer}
159  /* login */
160  .login{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;padding:20px;
161    background:radial-gradient(1100px 600px at 12% -8%,#25252b 0,transparent 58%),radial-gradient(900px 500px at 112% 8%,#08080a 0,transparent 55%),linear-gradient(160deg,#111114 0,#17171c 48%,#08080a 100%)}
162  .login.hidden{display:none}
163  .lcard{width:min(400px,100%);background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid rgba(255,255,255,.12);border-radius:24px;padding:38px 32px 30px;backdrop-filter:blur(16px);box-shadow:0 40px 90px -34px rgba(0,0,0,.8),inset 0 1px 0 rgba(255,255,255,.05);text-align:center;position:relative;overflow:hidden}
164  .lcard::before{content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);width:118px;height:3px;border-radius:0 0 6px 6px;background:linear-gradient(90deg,transparent,var(--gold),transparent)}
165  .lcard img{width:74px;height:74px;border-radius:20px;object-fit:cover;margin:8px auto 16px;display:block;box-shadow:0 12px 34px -10px rgba(0,0,0,.7)}
166  .lcard h2{font-size:22px;font-weight:700;letter-spacing:-.01em} .lcard p{color:var(--faint);font-size:12.5px;margin:6px 0 24px}
167  .lcard .inp{text-align:left;margin-bottom:13px;padding:13px 15px}
168  .lcard .inp:focus{border-color:var(--gold);outline:none}
169  .lrel{position:relative} .ltgl{position:absolute;right:12px;top:55%;transform:translateY(-50%);background:none;border:none;color:#8b98ab;cursor:pointer;font-size:12px;font-family:inherit}
170  .lmsg{color:#ff9c8b;font-size:13px;min-height:18px;margin-top:8px}
171  .lbtn{width:100%;padding:14px;border:none;border-radius:12px;background:linear-gradient(180deg,var(--gold-soft),var(--gold));color:#1a1207;font-family:inherit;font-weight:700;font-size:15px;cursor:pointer}
172  /* toast */
173  #toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:#17171c;border:1px solid var(--line);color:#fff;padding:12px 18px;border-radius:12px;font-size:13.5px;display:none;z-index:200;box-shadow:0 16px 40px -12px rgba(0,0,0,.7)}
174  .pdfstage{position:fixed;left:-3000px;top:0}
175  /* collapsible dropdown for long lists */
176  .acc{border:1px solid var(--line);border-radius:12px;margin-bottom:9px;overflow:hidden;background:var(--card-2)}
177  .acc-h{display:flex;align-items:center;gap:11px;padding:13px 15px;cursor:pointer;user-select:none}
178  .acc-h .ai{width:34px;height:34px;border-radius:9px;display:grid;place-items:center;flex:none;background:rgba(200,155,60,.16);color:var(--gold-soft);font-size:17px}
179  .acc-h .at{flex:1;min-width:0} .acc-h .at b{font-size:14px;display:block} .acc-h .at span{font-size:11.5px;color:var(--faint)}
180  .acc-h .chev{color:var(--faint);transition:.22s;flex:none} .acc.open .acc-h .chev{transform:rotate(180deg)}
181  .acc-b{display:grid;grid-template-rows:0fr;transition:grid-template-rows .24s cubic-bezier(.4,0,.2,1)}
182  .acc.open .acc-b{grid-template-rows:1fr}
183  .acc-bi{overflow:hidden;min-height:0;padding:0 15px}
184  .acc.open .acc-bi{padding:2px 15px 15px}
185  .tabs{display:none!important}
186  #eVeil{position:fixed;inset:0;background:rgba(0,0,0,.55);backdrop-filter:blur(2px);opacity:0;visibility:hidden;transition:.25s;z-index:140}
187  #eVeil.on{opacity:1;visibility:visible}
188  #eSide{position:fixed;top:0;left:0;height:100%;width:290px;max-width:86vw;background:#14141a;border-right:1px solid var(--line);transform:translateX(-102%);transition:transform .28s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;box-shadow:0 0 60px rgba(0,0,0,.5);z-index:141}
189  #eSide.on{transform:none}
190  body.light #eSide{background:#f7f8fa}
191  .eside-h{display:flex;align-items:center;gap:11px;padding:16px 16px 14px;border-bottom:1px solid var(--line)}
192  .eside-logo{width:34px;height:34px;border-radius:9px;background:linear-gradient(135deg,var(--gold-soft),#c99b3c);
192color:#1a1305;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:11.5px;flex:none}
193  .eside-h .et{flex:1;line-height:1.25}
194  .eside-h .et b{font-size:13.5px;display:block}
195  .eside-h .et span{font-size:10px;color:var(--faint);letter-spacing:.6px}
196  .eside-x{background:transparent;border:none;color:var(--muted);cursor:pointer;font-size:24px;line-height:1;padding:2px 6px}
197  .eside-scroll{flex:1;overflow-y:auto;padding:10px 10px 26px}
198  .snav-grp{margin-bottom:3px}
199  .snav-head{display:flex;align-items:center;gap:11px;padding:11px 12px;border-radius:11px;cursor:pointer;user-select:none;color:#e7ecf3;font-weight:600;font-size:13.5px;transition:.15s}
200  body.light .snav-head{color:#1c2230}
201  .snav-head:hover{background:var(--card)}
202  .snav-head .gi{color:var(--gold-soft);font-size:18px;flex:none}
203  .snav-head .gl{flex:1}
204  .snav-head .gc{color:var(--faint);transition:.22s;flex:none;font-size:16px}
205  .snav-grp.open .snav-head .gc{transform:rotate(180deg)}
206  .snav-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .22s cubic-bezier(.4,0,.2,1)}
207  .snav-grp.open .snav-body{grid-template-rows:1fr}
208  .snav-bi{overflow:hidden;min-height:0}
209  .snav-item{display:flex;align-items:center;gap:11px;padding:10px 12px 10px 15px;margin:2px 0 2px 13px;border-left:2px solid var(--line);color:var(--muted);cursor:pointer;font-size:13px;border-radius:0 9px 9px 0;transition:.15s}
210  .snav-item:hover{background:var(--card);color:#fff}
211  body.light .snav-item:hover{color:#111}
212  .snav-item.active{background:linear-gradient(90deg,rgba(232,201,119,.16),transparent);color:var(--gold-soft);border-left-color:var(--gold-soft);font-weight:600}
213  /* dept: equal cols desktop, stack on phone */
214  #deptKv{grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start}
215  @media(max-width:700px){#deptKv{grid-template-columns:1fr}}
216  /* desktop calendar: compact, not stretched */
217  @media(min-width:760px){
218    #attCalendar,.att-stats,.acal-head,.acal-legend{max-width:600px;margin-left:auto;margin-right:auto}
219  }
220  /* mobile topbar compact (footer removed, buttons back in bar) */
221  @media(max-width:560px){
222    .tbtn{width:34px;height:34px;font-size:15px}
223    .topbar{gap:6px;padding:9px 11px}
224    .topbar .tt span{display:none}
225    .topbar img{width:34px;height:34px}
226  }
227  /* premium micro-interactions + panel entry */
228  @keyframes fadeUp{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}
229  .panel{animation:fadeUp .34s cubic-bezier(.4,0,.2,1)}
230  .dept-block{transition:transform .16s ease,border-color .16s ease}
231  .dept-block:hover{transform:translateY(-2px);border-color:rgba(232,201,119,.3)}
232  .snav-item,.snav-head,.snav-dash,.tbtn,.ef-btn,.dl,.btn{transition:all .16s ease}
233  .btn:active,.dl:active,.p2-btn:active{transform:scale(.97)}
234  .snav-dash{margin:2px 0 10px 0;border-left:none;border-radius:11px;padding:11px 13px;font-weight:600;font-size:13.5px;color:#e7ecf3}
235  body.light .snav-dash{color:#1c2230}
236  .snav-dash .ti{font-size:18px;color:var(--gold-soft)}
237  .snav-dash:hover{background:var(--card)}
238  .snav-dash.active{background:linear-gradient(90deg,rgba(232,201,119,.2),rgba(232,201,119,.04));color:var(--gold-soft);border-left:none}
239  /* ===== DESKTOP: permanent sidebar (admin-style) ===== */
240  @media(min-width:900px){
241    .hamb{display:none!important}
242    #eVeil{display:none!important}
243    #eSide{position:fixed;top:60px;height:calc(100vh - 60px);width:266px;max-width:266px;transform:none!important;box-shadow:none;z-index:20}
244    .eside-h{display:none}
245    .eside-scroll{padding-top:16px}
246    .wrap{max-width:1060px;margin-left:max(266px, calc((100vw - 1060px)/2));margin-right:auto;padding-left:34px;padding-right:34px}
247  }
248  /* premium card polish */
249  .panel.card{box-shadow:0 18px 46px -28px rgba(0,0,0,.7)}
250  .sectitle{letter-spacing:.02em}
251  .snav-item .ti{font-size:16px;flex:none}
252  .eside-foot{display:none!important;border-top:1px solid var(--line);padding:12px;display:grid;grid-template-columns:1fr 1fr;gap:8px}
253  .ef-btn{background:var(--card);border:1px solid var(--line);color:var(--muted);border-radius:11px;padding:11px;font-family:inherit;font-size:12.5px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:7px;transition:.15s}
254  .ef-btn:hover{color:#fff;border-color:var(--gold-soft)}
255  .ef-btn.danger{grid-column:1/-1;color:#ff9c8b;border-color:rgba(224,83,63,.3)}
256  .punch2{background:linear-gradient(140deg,rgba(200,155,60,.13),rgba(255,255,255,.02));border:1px solid rgba(200,155,60,.3);border-radius:18px;padding:16px 18px;margin:16px 0;box-shadow:0 14px 38px -20px rgba(200,155,60,.55)}
257  .p2-head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
258  .p2-title{font-size:12px;color:var(--gold-soft);font-weight:800;text-transform:uppercase;letter-spacing:.06em;display:flex;align-items:center;gap:7px}
259  .p2-badge{font-size:11.5px;font-weight:700;padding:5px 11px;border-radius:999px;display:inline-flex;align-items:center;gap:6px}
260  .p2-badge.wait{background:rgba(255,255,255,.06);color:var(--muted)}
261  .p2-badge.live,.p2-badge.done{background:rgba(126,224,168,.14);color:#7fe0a8}
262  .p2-dot{width:7px;height:7px;border-radius:50%;background:#7fe0a8;animation:p2pulse 1.6s infinite}
263  @keyframes p2pulse{0%{box-shadow:0 0 0 0 rgba(127,224,168,.5)}70%{box-shadow:0 0 0 7px rgba(127,224,168,0)}100%{box-shadow:0 0 0 0 rgba(127,224,168,0)}}
264  .p2-times{display:flex;align-items:stretch;gap:9px;margin:14px 0}
265  .p2-time{flex:1;background:rgba(255,255,255,.04);border:1px solid var(--line);border-radius:13px;padding:11px 10px;text-align:center;min-width:0}
266  body.light .p2-time{background:rgba(0,0,0,.03)}
267  .p2-time span{display:block;font-size:9.5px;letter-spacing:.09em;color:var(--faint);font-weight:800}
268  .p2-time b{display:block;font-size:17px;margin-top:3px;font-variant-numeric:tabular-nums}
269  .p2-time.in b{color:#7fe0a8}.p2-time.out b{color:#ff9c8b}.p2-time.worked b{color:var(--gold-soft)}
270  .p2-time.worked.live{background:linear-gradient(140deg,rgba(232,201,119,.14),rgba(255,255,255,.02));border-color:rgba(232,201,119,.4);box-shadow:0 8px 22px -14px rgba(232,201,119,.6)}
271  .p2-time.worked.live b{color:var(--gold-soft);letter-spacing:.6px;font-weight:800;text-shadow:0 0 14px rgba(232,201,119,.5);animation:p2glow 2s ease-in-out infinite}
272  @keyframes p2glow{0%,100%{text-shadow:0 0 10px rgba(232,201,119,.35)}50%{text-shadow:0 0 18px rgba(232,201,119,.65)}}
273  .p2-sep{display:flex;align-items:center;color:var(--faint);font-size:15px}
274  .p2-foot{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap}
275  .p2-month{flex:1;min-width:150px}
276  .p2-mlbl{font-size:12px;color:var(--muted)}.p2-mlbl b{color:#fff}
277  body.light .p2-mlbl b{color:#111}
278  .p2-bar{height:7px;border-radius:20px;background:rgba(255,255,255,.09);margin-top:6px;overflow:hidden}
279  .p2-bar span{display:block;height:100%;
279background:linear-gradient(90deg,var(--gold-soft),#e8c977);border-radius:20px}
280  .p2-btn{border:none;border-radius:12px;padding:12px 22px;font-weight:700;font-size:14.5px;font-family:inherit;cursor:pointer;display:inline-flex;align-items:center;gap:8px}
281  .p2-btn.in{background:linear-gradient(135deg,#7fe0a8,#5bc48a);color:#0c2417}
282  .p2-btn.out{background:linear-gradient(135deg,#ffb38a,#ff8f6b);color:#3a1508}
283  .dir-dep{font-size:11.5px;font-weight:800;color:var(--gold-soft);text-transform:uppercase;letter-spacing:.08em;margin:16px 0 8px;display:flex;align-items:center;gap:8px}
284  .dir-dep span{background:rgba(232,201,119,.15);border-radius:20px;padding:1px 8px;font-size:11px}
285  .dir-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px}
286  .dir-card{display:flex;align-items:center;gap:12px;background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:13px;padding:12px 14px;transition:transform .16s ease,border-color .16s ease}
287  .dir-card:hover{transform:translateY(-2px);border-color:rgba(232,201,119,.3)}
288  .dir-av{width:40px;height:40px;border-radius:11px;background:linear-gradient(135deg,var(--gold-soft),#c99b3c);color:#1a1305;display:grid;place-items:center;font-weight:800;font-size:16px;flex:none}
289  .dir-info{min-width:0}
290  .dir-info b{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
291  .dir-info span{font-size:12px;color:var(--muted)}
292  .org-children{margin-left:20px;padding-left:18px;border-left:2px solid var(--line);margin-top:2px}
293  .org-card{display:flex;align-items:center;gap:11px;background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:13px;padding:11px 14px;margin:5px 0;transition:transform .16s,border-color .16s}
294  .org-card:hover{transform:translateX(2px);border-color:rgba(232,201,119,.3)}
295  .org-av{width:38px;height:38px;border-radius:10px;background:linear-gradient(135deg,var(--gold-soft),#c99b3c);color:#1a1305;display:grid;place-items:center;font-weight:800;font-size:15px;flex:none}
296  .org-info{flex:1;min-width:0}
297  .org-info b{display:block;font-size:14px}
298  .org-info span{font-size:12px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
299  .org-count{background:rgba(232,201,119,.15);color:var(--gold-soft);border-radius:20px;padding:2px 9px;font-size:11.5px;font-weight:700;flex:none}
300  .tl{position:relative;padding-left:8px}
301  .tl-item{position:relative;padding:0 0 20px 40px}
302  .tl-item::before{content:"";position:absolute;left:15px;top:30px;bottom:-2px;width:2px;background:var(--line)}
303  .tl-item:last-child::before{display:none}
304  .tl-dot{position:absolute;left:0;top:2px;width:32px;height:32px;border-radius:10px;display:grid;place-items:center;color:#141419;font-size:16px;box-shadow:0 4px 14px -6px rgba(0,0,0,.6)}
305  .tl-body{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:13px;padding:12px 15px;transition:transform .16s,border-color .16s}
306  .tl-body:hover{transform:translateY(-2px);border-color:rgba(232,201,119,.3)}
307  .tl-top{display:flex;align-items:center;gap:8px}.tl-top b{font-size:14.5px}
308  .tl-pin{color:var(--gold-soft);font-size:13px}
309  .tl-date{font-size:12px;color:var(--faint);margin-top:2px}
310  .tl-desc{font-size:13.5px;color:var(--muted);margin-top:7px;line-height:1.6}
311  .kb-cats{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:14px}
312  .kb-cat{background:var(--card);border:1px solid var(--line);color:var(--muted);border-radius:20px;padding:6px 14px;font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit;transition:.15s}
313  .kb-cat:hover{color:#fff}
314  .kb-cat.on{background:linear-gradient(135deg,var(--gold-soft),#d9b65e);color:#1a1305;border-color:var(--gold-soft)}
315  .kb-acc{margin-bottom:8px}
316  .kb-content{font-size:13.5px;color:var(--muted);line-height:1.7}
317  .kb-tags{margin-top:10px;font-size:12px;color:var(--faint)}
318  .mtg-date{font-size:11.5px;font-weight:800;color:var(--gold-soft);text-transform:uppercase;letter-spacing:.06em;margin:16px 0 8px}
319  .mtg-card{display:flex;gap:12px;background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:13px;padding:12px 14px;margin-bottom:8px;transition:transform .16s,border-color .16s}
320  .mtg-card:hover{transform:translateY(-2px);border-color:rgba(232,201,119,.3)}
321  .mtg-ic{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;color:#141419;font-size:17px;flex:none}
322  .mtg-body{min-width:0;flex:1}.mtg-body b{font-size:14.5px}
323  .mtg-meta{font-size:12.5px;color:var(--muted);margin-top:2px}
324  .mtg-desc{font-size:13px;color:var(--muted);margin-top:6px;line-height:1.5}
325  .mtg-link{display:inline-flex;align-items:center;gap:6px;margin-top:8px;color:var(--gold-soft);font-size:13px;font-weight:600;text-decoration:none}
326  .mtg-cd{display:flex;align-items:center;gap:5px;margin-top:11px;flex-wrap:wrap}
327  .cd-lbl{font-size:10px;color:var(--faint);text-transform:uppercase;letter-spacing:.06em;font-weight:700;margin-right:4px}
328  .cd-b{background:linear-gradient(160deg,rgba(232,201,119,.16),rgba(232,201,119,.04));border:1px solid rgba(232,201,119,.28);border-radius:9px;padding:5px 0;text-align:center;min-width:40px}
329  .cd-b b{display:block;font-size:16px;color:var(--gold-soft);font-variant-numeric:tabular-nums;line-height:1.1;font-weight:800}
330  .cd-b span{font-size:8.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.04em}
331  .cd-s{color:var(--gold-soft);font-weight:800;opacity:.5;font-size:15px;margin-top:-8px}
332  .mtg-live{display:inline-flex;align-items:center;gap:7px;color:#7fe0a8;font-weight:700;font-size:13px;margin-top:10px}
333  .mtg-livedot{width:8px;height:8px;border-radius:50%;background:#7fe0a8;animation:p2pulse 1.5s infinite}
334  #gsOverlay{position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(3px);opacity:0;visibility:hidden;transition:.2s;z-index:160;display:flex;align-items:flex-start;justify-content:center;padding:60px 16px}
335  #gsOverlay.on{opacity:1;visibility:visible}
336  .gs-box{width:100%;max-width:560px;background:#15151b;border:1px solid var(--line);border-radius:16px;box-shadow:0 30px 70px -20px rgba(0,0,0,.7);overflow:hidden;transform:translateY(-10px);transition:.25s}
337  body.light .gs-box{background:#fff}
338  #gsOverlay.on .gs-box{transform:none}
339  .gs-in{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--line)}
340  .gs-in .ti{color:var(--gold-soft);font-size:20px}
341  .gs-in input{flex:1;background:transparent;border:none;outline:none;color:inherit;font-size:15.5px;font-family:inherit}
342  .gs-x{background:transparent;border:none;color:var(--muted);font-size:24px;cursor:pointer;line-height:1}
343  #gsResults{max-height:60vh;overflow-y:auto;padding:8px}
344  .gs-hint{padding:24px 16px;text-align:center;color:var(--faint);font-size:13.5px}
345  .gs-cat{font-size:10.5px;font-weight:800;color:var(--gold-soft);text-transform:uppercase;letter-spacing:.07em;padding:10px 10px 5px}
346  .gs-item{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:11px;cursor:pointer;transition:background .15s}
347  .gs-item:hover{background:var(--card)}
348  .gs-item>.ti:first-child{color:var(--muted);font-size:18px;flex:none}
349  .gs-txt{flex:1;min-width:0}.gs-txt b{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
350  .gs-txt span{font-size:12px;color:var(--faint)}
351  .gs-go{color:var(--faint);font-size:15px;flex:none}
352  .fm-card{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:15px;padding:16px 17px;margin-bottom:14px}
353  .fm-card.done{opacity:.75}
354  .fm-h{display:flex;align-items:center;justify-content:space-between;gap:10px}.fm-h b{font-size:15.5px}
355  .fm-done{color:#7fe0a8;font-size:12.5px;font-weight:700;display:inline-flex;align-items:center;gap:5px}
356  .fm-desc{color:var(--muted);font-size:13px;margin:6px 0 12px;line-height:1.5}
357  .fm-field{margin-bottom:13px}
358  .fm-lbl{display:block;font-size:13px;font-weight:600;margin-bottom:6px}
359  .fm-opts{display:flex;flex-direction:column;gap:7px}
360  .fm-opt{display:flex;align-items:center;gap:8px;font-size:13.5px;color:var(--muted);cursor:pointer}
361  .fm-submit{margin-top:6px}
362  .msg-msgs{min-height:200px;max-height:56vh;overflow-y:auto;padding:6px 2px;display:flex;flex-direction:column}
363  .msg-bub{margin-bottom:10px;max-width:80%}
364  .msg-bub.me{align-self:flex-end;text-align:right}
365  .msg-from{font-size:10.5px;color:var(--gold-soft);font-weight:700;display:block;margin-bottom:2px}
366  .msg-b{display:inline-block;padding:9px 13px;border-radius:14px;font-size:14px;line-height:1.45;text-align:left}
367  .msg-bub.them .msg-b{background:var(--card);border:1px solid var(--line)}
368  .msg-bub.me .msg-b{background:linear-gradient(135deg,var(--gold-soft),#d9b65e);color:#1a1305}
369  .msg-t{font-size:10px;color:var(--faint);margin-top:3px}
370  .msg-in{display:flex;gap:8px;margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
371  .msg-in .inp{flex:1}
372  .msg-day{margin-bottom:6px}
373  .msg-day-h{display:flex;align-items:center;gap:8px;font-size:11.5px;font-weight:800;color:var(--gold-soft);text-transform:uppercase;letter-spacing:.05em;padding:9px 6px;cursor:pointer;user-select:none;border-radius:9px}
374  .msg-day-h:hover{background:var(--card)}
375  .msg-day-chev{transition:.2s;font-size:14px}
376  .msg-day:not(.open) .msg-day-chev{transform:rotate(-90deg)}
377  .msg-day-n{background:rgba(232,201,119,.15);border-radius:20px;padding:1px 9px;font-size:11px;margin-left:auto;color:var(--gold-soft)}
378  .msg-day-b{display:grid;grid-template-rows:0fr;transition:grid-template-rows .24s cubic-bezier(.4,0,.2,1)}
379  .msg-day.open .msg-day-b{grid-template-rows:1fr}
380  .msg-day-bi{overflow:hidden;display:flex;flex-direction:column;padding-top:4px}
381  .net-status{margin-top:12px}
382  .net-ok{display:inline-flex;align-items:center;gap:7px;background:rgba(127,224,168,.13);color:#7fe0a8;border:1px solid rgba(127,224,168,.3);border-radius:20px;padding:5px 13px;font-size:12.5px;font-weight:700}
383  .net-bad{display:inline-flex;align-items:center;gap:7px;background:rgba(255,156,139,.12);color:#ffb0a2;border:1px solid rgba(255,156,139,.3);border-radius:20px;padding:5px 13px;font-size:12.5px;font-weight:700}.net-warn{display:inline-flex;align-items:center;gap:7px;background:rgba(232,201,119,.14);color:#e8c977;border:1px solid rgba(232,201,119,.38);border-radius:20px;padding:5px 13px;font-size:12.5px;font-weight:700}
384  .p2-actions{display:flex;gap:9px;align-items:stretch}
385  .p2-actions .p2-btn{flex:1}
386  .p2-duty{display:inline-flex;align-items:center;gap:6px;justify-content:center;background:var(--card);border:1px solid var(--line);color:var(--muted);border-radius:13px;padding:0 15px;font-family:inherit;font-size:13px;font-weight:700;cursor:pointer;white-space:nowrap;transition:.15s}
387  .p2-duty:hover{border-color:rgba(232,201,119,.4);color:var(--gold-soft)}
388  .p2-duty.on{background:linear-gradient(135deg,var(--gold-soft),#c99b3c);
388color:#1a1305;border-color:transparent}
389  @media(max-width:560px){.p2-actions{flex-direction:column}}
390  /* ===== Dashboard workspace ===== */
391  .snap{background:linear-gradient(135deg,rgba(200,155,60,.15),rgba(255,255,255,.015) 62%);border:1px solid rgba(200,155,60,.24);border-radius:18px;padding:20px 22px;margin:0 0 16px;box-shadow:0 12px 44px -24px rgba(200,155,60,.6),inset 0 1px 0 rgba(255,255,255,.05)}
392  .snap-l{display:flex;align-items:center;gap:16px}
393  .snap-hi{flex:1;min-width:0}
394  .snap-hi h2{font-size:25px;font-weight:800;line-height:1.15;margin:0;color:#fff;letter-spacing:-.015em;min-height:1.15em}
395  #eName.type-cursor::after{content:"";display:inline-block;width:2px;height:.8em;background:var(--gold-soft,#e8c977);margin-left:3px;border-radius:1px;vertical-align:baseline;box-shadow:0 0 6px rgba(232,201,119,.7);animation:blinkCur 1.05s step-end infinite}
396  @keyframes blinkCur{0%,50%{opacity:1}51%,100%{opacity:0}}
397  .snap-eyebrow{display:block;color:var(--gold-soft);font-size:11.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;opacity:.9;margin-bottom:3px}
398  .snap-hi p{color:var(--muted);font-size:12.5px;margin:3px 0 0}
399  .snap-hb{display:inline-flex;align-items:center;gap:7px;background:linear-gradient(180deg,var(--gold-soft),var(--gold));color:#1a1207;border-radius:11px;padding:9px 14px;font-size:12.5px;font-weight:700;text-decoration:none;white-space:nowrap;flex:none;transition:transform .15s}
400  .snap-hb:hover{transform:translateY(-1px)}
401  @media(max-width:560px){.snap-hb{padding:9px 13px}}
402  .snap-chips{display:flex;flex-wrap:wrap;gap:9px;margin-top:16px;padding-top:15px;border-top:1px solid rgba(200,155,60,.14)}
403  .snap-chips .chip{margin-top:0}
404  .dsec-h{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:var(--gold-soft);margin:20px 2px 2px}
405  .dsec-h .ti{font-size:16px}
406  .dash-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:14px;margin-top:10px}
407  .dcard{background:linear-gradient(158deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:16px;padding:15px 17px;min-width:0;transition:border-color .18s,transform .18s}
408  .dcard:hover{border-color:rgba(232,201,119,.22)}
409  .s8{grid-column:span 8}.s6{grid-column:span 6}.s4{grid-column:span 4}
410  @media(max-width:1000px){.s8,.s6,.s4{grid-column:span 6}}
411  @media(max-width:640px){.dash-grid{gap:12px}.s8,.s6,.s4{grid-column:span 12}}
412  .dcard-h{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:12px}
413  .dcard-h>span{display:flex;align-items:center;gap:8px;font-size:13.5px;font-weight:700}
414  .dcard-h .ti{color:var(--gold-soft)}
415  .dcard-all{font-size:11.5px;color:var(--gold-soft);cursor:pointer;display:inline-flex;align-items:center;gap:3px;white-space:nowrap;font-weight:600;opacity:.85}
416  .dcard-all:hover{opacity:1}
417  .dw-task{display:flex;align-items:center;gap:11px;padding:9px 0;border-top:1px solid var(--line)}
418  .dw-task:first-child{border-top:none}
419  .dw-pri{width:8px;height:8px;border-radius:50%;flex:none}
420  .dw-task-b{flex:1;min-width:0}
421  .dw-task-b b{display:block;font-size:13.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
422  .dw-task-m{font-size:11px;color:var(--faint);text-transform:capitalize}
423  .dw-task-go{color:var(--faint);font-size:15px;flex:none}
424  .dw-quick{display:grid;grid-template-columns:1fr 1fr;gap:9px}
425  .qa{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:13px 6px;color:inherit;font-family:inherit;font-size:11.5px;font-weight:600;cursor:pointer;transition:.15s}
426  .qa:hover{border-color:rgba(232,201,119,.4);background:rgba(232,201,119,.08);transform:translateY(-2px)}
427  .qa .ti{font-size:19px;color:var(--gold-soft)}
428  .dw-note{display:flex;gap:10px;padding:9px 0;border-top:1px solid var(--line)}
429  .dw-note:first-child{border-top:none}
430  .dw-note-i{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex:none;font-size:15px;background:rgba(143,182,240,.14);color:#8fb6f0}
431  .dw-note-i.success{background:rgba(127,224,168,.14);color:#7fe0a8}.dw-note-i.warn{background:rgba(255,156,139,.14);color:#ffb0a2}
432  .dw-note-b{flex:1;min-width:0}
433  .dw-note-b b{display:block;font-size:13px}
434  .dw-new{display:inline-block;width:7px;height:7px;border-radius:50%;background:#ff5a4d;margin-left:7px;vertical-align:middle;animation:dwPulse 1.5s ease-out infinite}
435  @keyframes dwPulse{0%{box-shadow:0 0 0 0 rgba(255,90,77,.55)}70%{box-shadow:0 0 0 6px rgba(255,90,77,0)}100%{box-shadow:0 0 0 0 rgba(255,90,77,0)}}
436  .dw-note-b span{display:block;font-size:11.5px;color:var(--muted);line-height:1.4;margin:1px 0}
437  .dw-note-b em{font-size:10px;color:var(--faint);font-style:normal}
438  .dw-up{display:flex;gap:12px;padding:9px 0;border-top:1px solid var(--line);align-items:center}
439  .dw-up:first-child{border-top:none}
440  .dw-up-d{text-align:center;flex:none;min-width:52px;background:rgba(232,201,119,.1);border:1px solid rgba(232,201,119,.2);border-radius:10px;padding:6px 4px}
441  .dw-up-d b{display:block;font-size:11.5px;color:var(--gold-soft)}
442  .dw-up-d span{font-size:11px;color:var(--muted)}
443  .dw-up-b{flex:1;min-width:0}
444  .dw-up-b b{display:block;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
445  .dw-up-b span{font-size:11px;color:var(--faint);display:flex;align-items:center;gap:4px}
446  .dw-lv{margin-bottom:11px}
447  .dw-lv:last-child{margin-bottom:0}
448  .dw-lv-t{display:flex;justify-content:space-between;align-items:baseline;font-size:12.5px;margin-bottom:5px}
449  .dw-lv-t span{color:var(--muted)}
450  .dw-lv-t b{font-weight:800;color:#fff}.dw-lv-t small{color:var(--faint);font-weight:400}
451  .dw-lv-bar{height:7px;border-radius:20px;background:rgba(255,255,255,.07);overflow:hidden}
452  .dw-lv-bar span{display:block;height:100%;border-radius:20px;background:linear-gradient(90deg,var(--gold-soft),#d9b65e);transition:width .5s cubic-bezier(.4,0,.2,1)}
453  .dw-perf-top{display:flex;align-items:center;gap:14px;margin-bottom:12px}
454  .dw-perf-n{font-size:30px;font-weight:800;color:var(--gold-soft);line-height:1}
455  .dw-perf-n small{font-size:14px;color:var(--faint);font-weight:400}
456  .dw-perf-r .stars{font-size:14px}.dw-perf-r span{display:block;font-size:11px;color:var(--faint);margin-top:2px}
457  .dw-empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:20px 10px;text-align:center;color:var(--faint)}
458  .dw-empty .ti{font-size:26px;opacity:.6}
459  .dw-empty span{font-size:12.5px}
460  .dw-sk{height:34px;border-radius:9px;margin-bottom:9px;background:linear-gradient(90deg,rgba(255,255,255,.04),rgba(255,255,255,.09),rgba(255,255,255,.04));background-size:200% 100%;animation:dwsk 1.3s infinite}
461  @keyframes dwsk{0%{background-position:200% 0}100%{background-position:-200% 0}}
462  .dcard-journey{padding:0;overflow:hidden}
463  .dcard-journey #progCard{margin:0!important;border:none;background:transparent}
464  #selfieOverlay{position:fixed;inset:0;background:rgba(0,0,0,.75);backdrop-filter:blur(3px);opacity:0;visibility:hidden;transition:.2s;z-index:170;display:flex;align-items:center;justify-content:center;padding:16px}
465  #selfieOverlay.on{opacity:1;visibility:visible}
466  .selfie-box{width:100%;max-width:400px;background:#15151b;border:1px solid var(--line);border-radius:18px;box-shadow:0 30px 70px -20px rgba(0,0,0,.8);padding:18px}
467  body.light .selfie-box{background:#fff}
468  .selfie-h{display:flex;align-items:center;justify-content:space-between;font-size:15px}
469  .selfie-h .ti{color:var(--gold-soft);margin-right:6px}
470  .selfie-sub{font-size:12.5px;color:var(--muted);margin:4px 0 12px}
471  .selfie-vid{width:100%;border-radius:14px;background:#000;aspect-ratio:4/3;object-fit:cover;transform:scaleX(-1)}
472  .selfie-ph{width:100%;aspect-ratio:4/3;border-radius:14px;border:1.5px dashed var(--line);background:linear-gradient(160deg,rgba(232,201,119,.08),rgba(255,255,255,.02));display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px}
473  body.light .selfie-ph{background:linear-gradient(160deg,rgba(200,155,60,.1),rgba(22,30,48,.02));border-color:rgba(22,30,48,.16)}
474  .selfie-ph-ic{width:66px;height:66px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(232,201,119,.14);color:var(--gold-soft);font-size:32px}
475  .selfie-ph-t{font-size:14.5px;font-weight:700;color:var(--txt)}
476  .selfie-cap{width:100%;margin-top:14px;justify-content:center;font-size:15px;padding:13px}
477  @media(max-width:560px){
478    .hamb{order:-1}
479    .topbar img{width:36px;height:36px;border-radius:10px}
480    .topbar .tt b{font-size:13.5px}.topbar .tt span{font-size:10px}
481    .tbtn{width:38px;height:38px;font-size:16px}
482    .wrap{padding:0 12px 46px}
483    .crm{gap:10px;margin:14px 0 4px}
484    .ccard{border-radius:15px}
485    .ref{border-radius:16px;padding:20px 18px;margin-top:12px}
486    .ref h1{font-size:21px;line-height:1.28}
487    .chip{font-size:12.5px}
488    .card{border-radius:16px}
489    .p2-time b{font-size:15.5px}
490    .p2-btn{flex:1;justify-content:center}
491    .p2-month{flex:1 1 100%}
492  }
493  @media(max-width:400px){
494    .p2-times{flex-wrap:wrap}
494.p2-sep{display:none}
495    .p2-time{flex:1 1 calc(33.33% - 6px)}
496    .p2-time.worked.live b{font-size:14px;letter-spacing:.2px}
497  }
498  @media(max-width:480px){
499    html,body{overflow-x:hidden;max-width:100vw}
500    .wrap{padding-left:13px;padding-right:13px;max-width:100%}
501    .p2-times{flex-wrap:wrap}
502    .p2-sep{display:none}
503    .p2-time{flex:1 1 calc(33.33% - 6px);min-width:0}
504    .topbar{min-width:0}
505    .topbar>*{min-width:0}
506    .crm{grid-template-columns:repeat(2,1fr)}
507    .snap-hb{flex:0 0 auto}
508  }
509  .acc-b .line{display:flex;justify-content:space-between;gap:10px;padding:5px 0;font-size:13.5px;border-bottom:1px solid rgba(255,255,255,.05)}
510  body.light .acc-b .line{border-bottom:1px solid rgba(40,40,48,.06)}
511  .acc-b .ptext{white-space:pre-wrap;font-size:13.5px;color:var(--muted);line-height:1.65}
512  .accwrap-head{font-size:12px;color:var(--faint);text-transform:uppercase;letter-spacing:.4px;margin:14px 0 8px}
513  .monthsel{display:flex;gap:10px;flex-wrap:wrap;align-items:center}.monthsel select{max-width:230px}
514  @keyframes hbpulse{0%,100%{box-shadow:0 0 0 0 rgba(232,201,119,.6)}50%{box-shadow:0 0 0 10px rgba(232,201,119,0)}}
515  #hbBtn{animation:hbpulse 1.5s ease-in-out infinite;font-weight:700;font-size:14px}
516  select option{background:#17171c;color:#fff}
517  body.night select option{background:#0c1119;color:#fff}
518  input[type="date"]{color-scheme:dark}
519  input[type="date"]::-webkit-calendar-picker-indicator{filter:invert(.85);cursor:pointer;opacity:.9}
520  .docwarn{display:flex;align-items:center;gap:8px;background:rgba(224,83,63,.13);border:1px solid rgba(224,83,63,.5);color:#ffb3a6;border-radius:11px;padding:11px 14px;margin-bottom:12px;font-size:13px;animation:warnblink 1.5s ease-in-out infinite}
521  @keyframes warnblink{0%,100%{opacity:1}50%{opacity:.5}}
522  .docok{display:flex;align-items:center;gap:8px;background:rgba(62,199,127,.12);border:1px solid rgba(62,199,127,.4);color:#aef0c8;border-radius:11px;padding:11px 14px;margin-bottom:12px;font-size:13px}
523  .docok-pro{display:flex;align-items:center;gap:13px;background:linear-gradient(150deg,rgba(62,199,127,.16),rgba(62,199,127,.04));border:1px solid rgba(62,199,127,.45);border-radius:14px;padding:14px 16px;margin-bottom:12px;box-shadow:0 8px 26px -16px rgba(62,199,127,.5)}
524  .docok-pro i{font-size:27px;color:var(--ok);flex:none}
525  .docok-pro b{display:block;font-size:14.5px;color:#c6f5da}
526  .docok-pro span{font-size:12px;color:var(--muted)}
527  .sal-yr{display:flex;align-items:center;justify-content:center;gap:16px;margin-bottom:16px}
528  .sal-nav{width:38px;height:38px;border-radius:50%;background:var(--card-2);border:1px solid var(--line);color:var(--gold-soft);cursor:pointer;font-size:16px;display:flex;align-items:center;justify-content:center}
529  .sal-nav:disabled{opacity:.32;cursor:default}
530  .sal-yrlbl{font-size:17px;font-weight:800;color:var(--gold-soft);min-width:64px;text-align:center;background:var(--card-2);border:1px solid var(--line);border-radius:20px;padding:6px 22px}
531  .sal-months{display:grid;gap:10px}
532  .sal-card{display:flex;align-items:center;gap:14px;background:linear-gradient(160deg,rgba(255,255,255,.06),rgba(255,255,255,.02));border:1px solid var(--line);border-left:3px solid var(--gold);border-radius:14px;padding:12px 15px}
533  .sal-card.locked{opacity:.5;border-left-color:var(--faint)}
534  .sal-mbadge{width:52px;text-align:center;background:rgba(200,155,60,.14);border-radius:10px;padding:8px 0;flex:none}
535  .sal-mbadge b{display:block;font-size:16px;color:var(--gold-soft);line-height:1}
536  .sal-mbadge span{font-size:10.5px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
537  .sal-mmain{flex:1;min-width:0}.sal-mmain b{display:block;font-size:14px}.sal-mmain span{font-size:12px;color:var(--faint)}
538  .sal-mbtns{display:flex;gap:8px;align-items:center}
539  .sal-dl{background:linear-gradient(180deg,var(--gold-soft),var(--gold));color:#1a1207;border:none;border-radius:9px;padding:9px 14px;font-family:inherit;font-weight:700;font-size:12.5px;cursor:pointer;display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
540  .sal-rep{background:var(--card-2);border:1px solid var(--line);color:var(--warn);border-radius:9px;padding:9px 11px;cursor:pointer}
541  .sal-na{font-size:11.5px;color:var(--faint);margin-left:auto}
542  .sal-quick{display:flex;align-items:center;gap:9px;flex-wrap:wrap;background:linear-gradient(160deg,rgba(200,155,60,.12),rgba(255,255,255,.02));border:1px solid rgba(200,155,60,.28);border-radius:14px;padding:12px 14px;margin-bottom:16px}
543  .sal-quick>i{color:var(--gold-soft);font-size:18px}
544  .sal-qlbl{font-weight:700;font-size:13px;letter-spacing:.02em}
545  .sal-quick select{flex:1 1 120px;min-width:110px;margin-bottom:0}
546  .sal-quick .sal-dl{white-space:nowrap}
547  .sal-qnote{flex:1;font-size:12px;color:var(--muted)}
548  .sal-empty{text-align:center;padding:34px 18px;border:1px dashed var(--line);border-radius:16px;background:rgba(255,255,255,.02)}
549  .sal-empty>i{font-size:30px;color:var(--gold-soft);opacity:.8}
550  .sal-empty-t{font-size:14.5px;font-weight:700;color:var(--txt);margin:10px 0 4px}
551  .sal-empty-s{font-size:12.5px;color:var(--muted);max-width:340px;margin:0 auto;line-height:1.55}
552  @media(max-width:560px){.sal-quick{gap:8px}.sal-qlbl{flex:1 1 100%}.sal-quick .sal-dl{flex:1 1 100%;justify-content:center}}
553  @media(max-width:560px){.sal-card{flex-wrap:wrap}.sal-mbtns{width:100%}.sal-dl{flex:1;justify-content:center}}
554  @media(max-width:640px){.tabs{flex-wrap:wrap;overflow:visible;padding-bottom:2px;gap:7px}.tab{flex:1 1 calc(50% - 5px);min-width:calc(50% - 5px);justify-content:center;padding:9px 10px;font-size:12px}}
555  @media(max-width:560px){.crm{grid-template-columns:repeat(2,1fr)}.ref{padding:18px 16px}.ref h1{font-size:20px}.wrap{padding:14px 12px}.topbar{padding:10px 12px;gap:8px}.card{padding:16px 15px}.ccard{padding:13px 14px}.ccard .cv{font-size:16px;line-height:1.3}.tab{padding:9px 13px;font-size:12.5px}}
556  @media(max-width:380px){.crm{grid-template-columns:repeat(2,1fr)}.ccard .cl{font-size:10px}.ccard .cv{font-size:15px;line-height:1.3}}
557  @media(max-width:480px){.drow{flex-wrap:wrap;gap:10px 12px;padding:14px 15px}.drow .dt{flex:1 1 calc(100% - 54px);min-width:0}.drow .dt b{font-size:14px}.drow .dt span{white-space:normal;word-break:break-word}.drow .dl{flex:1 1 100%;justify-content:center;padding:11px}}
558  .warncard{background:rgba(224,83,63,.07);border:1.5px solid rgba(224,83,63,.55);border-radius:16px;padding:16px 18px;margin-bottom:16px;animation:warnglow 1.8s ease-in-out infinite}
559  @keyframes warnglow{0%,100%{box-shadow:0 0 0 0 rgba(224,83,63,.35)}50%{box-shadow:0 0 0 7px rgba(224,83,63,0)}}
560  .punch-card{display:flex;align-items:center;gap:16px;justify-content:space-between;flex-wrap:wrap;background:linear-gradient(135deg,rgba(200,155,60,.14),rgba(255,255,255,.02));border:1px solid rgba(200,155,60,.28);border-radius:18px;padding:16px 18px;margin:16px 0}
561  .punch-h{font-size:12px;color:var(--gold-soft);font-weight:800;text-transform:uppercase;letter-spacing:.06em;display:flex;align-items:center;gap:7px}
562  .punch-status{font-size:16px;font-weight:700;margin-top:6px}
563  .punch-meta{font-size:12.5px;color:var(--muted);margin-top:4px}
564  .punch-btn{font-size:15px;padding:12px 22px}
565  .punch-btn.in{background:linear-gradient(180deg,#4ec77f,#2e9d5e);color:#04140b}
566  .punch-btn.out{background:linear-gradient(180deg,#f3a05a,#e07a3f);color:#1a0d02}
567  .punch-done{color:var(--ok);font-weight:700;display:inline-flex;align-items:center;gap:7px;font-size:15px}
568  .onb-card{background:linear-gradient(135deg,rgba(78,199,127,.14),rgba(255,255,255,.02));border:1px solid rgba(78,199,127,.3);border-radius:18px;padding:18px;margin:16px 0}
569  .onb-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
570  .onb-title{font-size:16px;font-weight:800;display:flex;align-items:center;gap:8px}
571  .onb-sub{font-size:12.5px;color:var(--muted);margin-top:3px}
572  .onb-pct{font-size:24px;font-weight:800;color:#7fe0a8;line-height:1}
573  .onb-bar{height:8px;border-radius:6px;background:var(--line);overflow:hidden;margin:12px 0}
574  .onb-bar span{display:block;height:100%;background:linear-gradient(90deg,#4ec77f,#2e9d5e);border-radius:6px}
575  .onb-steps{display:flex;flex-direction:column;gap:7px}
576  .onb-step{display:flex;align-items:center;gap:9px;font-size:13.5px}
577  .onb-step .ti{color:var(--faint);font-size:18px}
578  .onb-step.done{color:var(--muted)}
579  .onb-step.done .ti{color:#4ec77f}
580  .onb-step.done span{text-decoration:line-through}
581  .asset-card{background:linear-gradient(135deg,rgba(143,182,240,.12),rgba(255,255,255,.02));border:1px solid rgba(143,182,240,.28);border-radius:18px;padding:16px 18px;margin:16px 0}
582  .asset-head{font-size:14px;font-weight:800;display:flex;align-items:center;gap:8px;margin-bottom:6px}
583  .asset-head .ti{color:#8fb6f0;font-size:18px}
584  .asset-head span{background:rgba(143,182,240,.2);color:#8fb6f0;border-radius:20px;padding:1px 9px;font-size:12px}
585  .asset-item{display:flex;align-items:center;gap:12px;padding:11px 0;border-top:1px solid var(--line)}
586  .asset-item>.ti{font-size:24px;color:#8fb6f0}
587  .asset-info{flex:1}
588  .asset-tag{font-size:11px;color:var(--faint)}
589  .asset-meta{font-size:12px;color:var(--muted);margin-top:2px}
590  .asset-ack{background:var(--gold-soft);
590color:#1a1305;border:none;border-radius:10px;padding:7px 14px;font-weight:700;font-size:12.5px;cursor:pointer}
591  .asset-ack.done{background:transparent;color:#7fe0a8;display:inline-flex;align-items:center;gap:5px;padding:7px 2px}
592  .crs-card{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.02));border:1px solid var(--line);border-radius:14px;padding:14px;margin-bottom:12px}
593  .crs-meta{font-size:12px;color:var(--muted);margin-top:3px}
594  .crs-desc{font-size:13px;color:var(--muted);margin:6px 0}
595  .crs-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:12px}
596  .crs-edit{display:flex;flex-wrap:wrap;align-items:center;gap:10px;flex:1;min-width:200px}
597  .crs-edit input[type=range]{flex:1;min-width:120px;accent-color:var(--gold-soft)}
598  .crs-edit span{font-size:12px;color:var(--gold-soft);font-weight:700;min-width:38px}
599  .pulse-card{background:linear-gradient(135deg,rgba(200,155,60,.14),rgba(255,255,255,.02));border:1px solid rgba(200,155,60,.28);border-radius:16px;padding:16px;margin-bottom:12px}
600  .pulse-q{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px;margin-bottom:12px}
601  .pulse-q .ti{color:var(--gold-soft)}
602  .pulse-scale{display:flex;gap:10px;flex-wrap:wrap}
603  .pulse-dot{width:44px;height:44px;border-radius:50%;border:1px solid var(--line);background:var(--card);color:var(--muted);font-weight:800;font-size:16px;cursor:pointer;transition:.15s}
604  .pulse-dot:hover,.pulse-dot.on{background:var(--gold-soft);color:#1a1305;border-color:var(--gold-soft)}
605  .poll-card{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.02));border:1px solid var(--line);border-radius:14px;padding:14px;margin-bottom:12px}
606  .poll-q{margin-top:12px}
607  .poll-qt{font-size:13.5px;font-weight:600;margin-bottom:6px}
608  .poll-opt{display:flex;align-items:center;gap:9px;padding:7px 0;font-size:13.5px;color:var(--muted);cursor:pointer}
609  .poll-opt input{width:16px;height:16px}
610  .sug-form{background:rgba(255,255,255,.02);border:1px solid var(--line);border-radius:14px;padding:14px}
611  .sug-item{display:flex;gap:12px;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px;margin-bottom:8px}
612  .sug-vote{display:flex;flex-direction:column;align-items:center;gap:2px;background:transparent;border:1px solid var(--line);border-radius:10px;padding:6px 10px;color:var(--muted);cursor:pointer;min-width:44px}
613  .sug-vote.on{border-color:var(--gold-soft);color:var(--gold-soft)}
614  .sug-vote .ti{font-size:18px}.sug-vote span{font-size:12px;font-weight:700}
615  .sug-body{flex:1}
616  .sug-meta{font-size:12px;color:var(--faint);margin-top:2px}
617  .sug-msg{font-size:13px;color:var(--muted);margin-top:5px}
618  @media(max-width:480px){
619    .pulse-scale{gap:7px}
620    .pulse-dot{width:40px;height:40px;font-size:15px}
621    .crs-edit{min-width:0}
622    .crs-edit input[type=range]{min-width:90px}
623    .crs-actions .btn,.crs-actions .dl{flex:1;justify-content:center}
624    .asset-item{gap:10px}
625    .asset-ack{padding:6px 11px}
626    .sug-item{gap:9px}
627    .sug-vote{min-width:40px;padding:5px 8px}
628  }
629  .acal-head{display:flex;align-items:center;justify-content:center;gap:16px;margin:6px 0 14px}
630  .acal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px}
631  .acal-dow{text-align:center;font-size:11px;color:var(--faint);font-weight:700;padding-bottom:2px}
632  .acal-cell{aspect-ratio:1;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:13.5px;font-weight:700;background:var(--card);border:1px solid var(--line);color:var(--muted);position:relative;transition:transform .12s ease}
633.acal-cell:not(.empty):hover{transform:translateY(-2px)}
634  .acal-cell.empty{background:transparent;border:none}
635  .acal-cell.present{background:linear-gradient(160deg,rgba(78,199,127,.28),rgba(78,199,127,.09));color:#a7edc4;border-color:rgba(78,199,127,.45)}
636  .acal-cell.half{background:linear-gradient(160deg,rgba(243,210,122,.26),rgba(243,210,122,.09));color:#f6dd94;border-color:rgba(243,210,122,.45)}
637  .acal-cell.leave{background:linear-gradient(160deg,rgba(143,182,240,.26),rgba(143,182,240,.09));color:#b3ccf6;border-color:rgba(143,182,240,.45)}
638  .acal-cell.holiday{background:linear-gradient(160deg,rgba(198,163,255,.22),rgba(198,163,255,.07));color:#d5bcff;border-color:rgba(198,163,255,.4)}
639  .acal-cell.absent{background:linear-gradient(160deg,rgba(224,83,63,.2),rgba(224,83,63,.06));color:#ffb0a2;border-color:rgba(224,83,63,.4)}
640  .acal-cell.weekoff{opacity:.45}.acal-cell.future{opacity:.32}
641  .acal-cell:not(.empty){cursor:pointer}
642  .attd-modal{position:fixed;inset:0;background:rgba(6,9,15,.72);backdrop-filter:blur(3px);display:none;align-items:center;justify-content:center;z-index:120;padding:20px}
643  .attd-modal.open{display:flex}
644  .attd-card{width:100%;max-width:360px;background:linear-gradient(165deg,#171d29,#10151f);border:1px solid var(--line);border-radius:18px;box-shadow:0 24px 60px -20px rgba(0,0,0,.7);overflow:hidden}
645  .attd-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:16px 18px 12px}
646  .attd-date{font-size:15px;font-weight:800;color:var(--txt);margin-bottom:6px}
647  .attd-pill{display:inline-block;font-size:11px;font-weight:700;padding:2px 10px;border-radius:20px;border:1px solid}
648  .attd-x{background:none;border:none;color:var(--faint);cursor:pointer;font-size:18px;line-height:1;padding:2px}
649  .attd-body{padding:4px 18px 18px}
650  .attd-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0;border-top:1px solid var(--line)}
651  .attd-row span{font-size:12.5px;color:var(--muted)}
652  .attd-row b{font-size:13.5px;color:var(--txt);font-weight:700}
653  .attd-empty{padding:14px 0 6px;font-size:12.5px;color:var(--muted);text-align:center}
654  .attd-sub{font-size:12px;color:var(--muted);font-weight:600}
655  .acal-reg{position:absolute;bottom:5px;right:5px;width:7px;height:7px;border-radius:50%;display:block}
656  .acal-reg.pending{background:#f3b04f;box-shadow:0 0 0 2px rgba(243,176,79,.22)}
657  .acal-reg.approved{background:#7fe0a8}
658  .acal-reg.rejected{background:#ff9c8b}
659  .reg-status{margin-top:12px;border:1px solid;border-radius:13px;padding:11px 13px;background:rgba(255,255,255,.02)}
660  .reg-status-h{font-size:12.5px;font-weight:700;color:var(--txt);display:flex;align-items:center;gap:6px}
661  .reg-status-b{font-size:12px;color:var(--muted);margin-top:6px;line-height:1.5}
662  .reg-reason{color:var(--faint);font-style:italic}
663  .reg-addbtn{margin-top:13px;width:100%;background:linear-gradient(135deg,#2b7de0,#1e63c4);border:none;color:#fff;font-weight:700;font-size:13.5px;border-radius:12px;padding:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:7px;box-shadow:0 8px 22px -10px rgba(43,125,224,.8)}
664  .reg-addbtn:active{transform:scale(.98)}
665  .reg-note{font-size:11.8px;color:var(--muted);line-height:1.5;background:rgba(43,125,224,.08);border:1px solid rgba(43,125,224,.2);border-radius:11px;padding:10px 12px;margin-bottom:14px;display:flex;gap:7px}
666  .reg-lbl{display:block;font-size:12px;font-weight:700;color:var(--muted);margin:12px 0 6px}
667  .reg-opt{font-weight:500;color:var(--faint)}
668  .reg-inp{width:100%;box-sizing:border-box;background:var(--card);border:1px solid var(--line);border-radius:11px;padding:11px 13px;color:var(--txt);font-size:14px;font-family:inherit}
669  .reg-inp:focus{outline:none;border-color:#2b7de0}
670  .reg-actions{display:flex;gap:10px;margin-top:18px}
671  .reg-cancel{flex:1;background:var(--card);border:1px solid var(--line);color:var(--muted);font-weight:700;font-size:13.5px;border-radius:12px;padding:12px;cursor:pointer}
672  .reg-submit{flex:2;background:linear-gradient(135deg,#2b7de0,#1e63c4);border:none;color:#fff;font-weight:700;font-size:13.5px;border-radius:12px;padding:12px;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:7px}
673  .reg-submit:disabled{opacity:.6}
674  .acal-cell.tdy{outline:2px solid var(--gold-soft);outline-offset:2px;box-shadow:0 0 0 4px rgba(232,201,119,.12),0 8px 24px -10px rgba(232,201,119,.55);z-index:1}
675  .acal-legend{display:flex;flex-wrap:wrap;gap:12px;margin-top:14px;font-size:12px;color:var(--muted)}
676  .acal-legend span{display:inline-flex;align-items:center;gap:6px}
677  .acal-dot{width:11px;height:11px;border-radius:3px;display:inline-block}
678  .acal-dot.present{background:#4ec77f}.acal-dot.half{background:#f3d27a}.acal-dot.leave{background:#8fb6f0}.acal-dot.holiday{background:#c6a3ff}.acal-dot.absent{background:#e0533f}
679  /* forgot-to-punch-out: blinking amber caution on the day + popup treatment */
680  .acal-cell.incomplete{border-color:rgba(243,144,47,.7)!important;box-shadow:inset 0 0 0 1.5px rgba(243,144,47,.4)}
681  .acal-warn{position:absolute;top:3px;right:3px;min-width:16px;height:16px;padding:0 2px;border-radius:50%;display:grid;place-items:center;background:linear-gradient(180deg,#ffb44a,#f3902f);color:#3a1f00;font-size:11px;font-weight:900;line-height:1;box-shadow:0 2px 6px -1px rgba(243,144,47,.75);animation:acalWarnBlink 1.05s ease-in-out infinite;z-index:2}
682  @keyframes acalWarnBlink{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.32;transform:scale(.8)}}
683  .acal-dot.warn{background:#f3902f}
684  .attd-warn{display:flex;align-items:flex-start;gap:11px;margin:2px 0 6px;padding:12px 13px;border-radius:13px;background:linear-gradient(160deg,rgba(243,144,47,.18),rgba(243,144,47,.06));border:1px solid rgba(243,144,47,.5)}
685  .attd-warn>i{color:#ffb44a;font-size:21px;flex:none;margin-top:1px;animation:acalWarnBlink 1.2s ease-in-out infinite}
686  .attd-warn b{display:block;color:#ffc879;font-size:13.5px;font-weight:800;margin-bottom:2px}
687  .attd-warn span{display:block;color:var(--muted);font-size:12.5px;line-height:1.45}
688  .reg-addbtn.warn{background:linear-gradient(180deg,#ffb44a,#f3902f);color:#3a1f00;box-shadow:0 8px 22px -9px rgba(243,144,47,.85)}
689  @media(prefers-reduced-motion:reduce){.acal-warn,.attd-warn>i{animation:none}}
690  .att-sum{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px}
691  .att-chip{font-size:12px;font-weight:700;padding:6px 12px;border-radius:20px;background:var(--card);border:1px solid var(--line)}
692  .att-chip.present{color:#7fe0a8}.att-chip.half{color:#f3d27a}.att-chip.leave{color:#8fb6f0}.att-chip.late{color:var(--warn)}
693  .att-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:9px;margin-bottom:16px}
694  .att-stat{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.015));border:1px solid var(--line);border-radius:14px;padding:12px 8px;text-align:center}
695  .att-stat b{display:block;font-size:22px;font-weight:800;line-height:1}
696  .att-stat span{font-size:10.5px;color:var(--faint);text-transform:uppercase;letter-spacing:.05em;font-weight:700;margin-top:4px;display:block}
697  .att-stat.present b{color:#7fe0a8}.att-stat.half b{color:#f3d27a}.att-stat.leave b{color:#8fb6f0}.att-stat.late b{color:#ff9c8b}
698  @media(max-width:400px){.att-stats{gap:6px}.att-stat{padding:10px 5px}.att-stat b{font-size:18px}.att-stat span{font-size:9px}}
699  .lv-bals{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-bottom:8px}
700  .lv-bal{background:linear-gradient(160deg,rgba(255,255,255,.06),rgba(255,255,255,.02));border:1px solid var(--line);border-radius:14px;padding:14px}
701  .lv-bal-k{font-size:11px;color:var(--faint);text-transform:uppercase;letter-spacing:.04em;font-weight:700}
702  .lv-bal-v{font-size:26px;font-weight:800;color:var(--gold-soft);line-height:1;margin:6px 0 2px}
703  .lv-bal-s{font-size:11px;color:var(--muted)}
704  .lv-tag{display:inline-block;font-size:8.5px;font-weight:800;letter-spacing:.05em;padding:1px 6px;border-radius:20px;vertical-align:middle;margin-left:5px}
705  .lv-tag.paid{background:rgba(78,199,127,.18);color:#7fe0a8}
706  .lv-tag.unpaid{background:rgba(232,170,110,.16);color:#e9b487}
707  .hol-note{font-size:12px;color:var(--muted);background:rgba(198,163,255,.08);border:1px solid rgba(198,163,255,.22);border-radius:12px;padding:9px 12px;margin:8px 0 12px;line-height:1.5}
708  .hol-note i{color:#c6a3ff}
709  .hol-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px}
710  .hol-card{display:flex;align-items:center;gap:12px;background:linear-gradient(160deg,rgba(198,163,255,.1),rgba(255,255,255,.02));border:1px solid rgba(198,163,255,.22);border-radius:14px;padding:11px 13px}
711  .hol-card.past{opacity:.5}
712  .hol-date{flex:none;width:46px;text-align:center;background:rgba(198,163,255,.16);border-radius:10px;padding:6px 0}
713  .hol-date b{display:block;font-size:19px;font-weight:800;color:#d5bcff;line-height:1}
714  .hol-date span{font-size:10px;text-transform:uppercase;letter-spacing:.05em;
714color:#b79ff0}
715  .hol-info{min-width:0}
716  .hol-info b{display:block;font-size:13.5px;color:var(--txt);font-weight:700}
717  .hol-info span{font-size:11px;color:var(--muted)}
718  .lv-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px}
719  .lv-form label,.lv-half{font-size:12px;color:var(--muted);display:block;margin-bottom:4px}
720  .lv-half{display:flex;align-items:center;gap:8px;margin:12px 0}
721  .lv-half input{width:16px;height:16px}
722  .sal-net-badge{display:inline-block;margin-top:5px;font-size:11px;font-weight:700;padding:2px 9px;border-radius:20px;width:fit-content}
723  .sal-net-badge.paid{background:rgba(78,199,127,.2);color:#7fe0a8}
724  .sal-net-badge.fin{background:rgba(232,201,119,.18);color:var(--gold-soft)}
725  .reim-file{display:inline-flex;align-items:center;gap:7px;cursor:pointer;font-size:13px;color:var(--gold-soft);background:var(--card);border:1px solid var(--line);border-radius:10px;padding:9px 14px;margin-top:2px}
726  .goal-card{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.02));border:1px solid var(--line);border-radius:14px;padding:14px;margin-bottom:10px}
727  .goal-bar{height:8px;border-radius:6px;background:var(--line);overflow:hidden;margin-top:10px}
728  .goal-bar span{display:block;height:100%;
728background:linear-gradient(90deg,var(--gold-soft),#e8c977);border-radius:6px}
729  .goal-edit{display:flex;align-items:center;gap:10px;margin-top:10px}
730  .goal-edit input[type=range]{flex:1;accent-color:var(--gold-soft)}
731  .goal-edit span{font-size:12px;color:var(--gold-soft);font-weight:700;min-width:40px}
732  .kb{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
733  .kb-col{background:rgba(255,255,255,.02);border:1px solid var(--line);border-radius:12px;padding:10px;min-height:70px}
734  .kb-h{font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin-bottom:8px;display:flex;justify-content:space-between}
735  .kb-h span{background:var(--line);border-radius:10px;padding:0 7px;color:var(--faint)}
736  .kb-card{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:10px;margin-bottom:8px}
737  @media(max-width:720px){.kb{grid-template-columns:1fr 1fr}}
738  @media(max-width:460px){.kb{grid-template-columns:1fr}}
739  .perf-card{background:linear-gradient(160deg,rgba(255,255,255,.05),rgba(255,255,255,.02));border:1px solid var(--line);border-radius:16px;padding:16px;margin-bottom:12px}
740  .perf-top{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
741  .perf-period{font-size:15px;font-weight:800}
742  .perf-overall{text-align:right;line-height:1}
743  .perf-overall b{font-size:30px;color:var(--gold-soft)}
744  .perf-overall span{font-size:13px;color:var(--muted)}
745  .perf-chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
746  .perf-chip{font-size:12px;background:var(--card);border:1px solid var(--line);border-radius:20px;padding:5px 11px;color:var(--muted)}
747  .perf-fb{margin-top:12px}
748  .perf-fb b{font-size:12px;color:var(--gold-soft);text-transform:uppercase;letter-spacing:.04em}
749  .perf-fb div{margin-top:3px;color:var(--muted);font-size:13.5px}
750  .rw-hero{display:flex;justify-content:space-between;align-items:center;background:linear-gradient(135deg,rgba(200,155,60,.16),rgba(255,255,255,.02));border:1px solid rgba(200,155,60,.3);border-radius:18px;padding:20px}
751  .rw-hero .ti{font-size:44px;color:var(--gold-soft);opacity:.55}
752  .rw-pts{font-size:38px;font-weight:800;color:var(--gold-soft);line-height:1}
753  .rw-lbl{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-top:4px}
754  .rw-item{display:flex;align-items:center;gap:12px;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px;margin-bottom:8px}
755  .rw-item .ti{font-size:22px;color:var(--gold-soft)}
756  .rw-item>div{flex:1}
757  .rw-badge{background:rgba(232,201,119,.18);color:var(--gold-soft);font-weight:700;font-size:12px;padding:3px 10px;border-radius:20px}
758  .lb-row{display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:10px;margin-bottom:5px;border:1px solid transparent}
759  .lb-row.me{background:rgba(200,155,60,.1);border-color:rgba(200,155,60,.3)}
760  .lb-rank{font-weight:800;color:var(--faint);min-width:22px}
761  .lb-name{flex:1}
762  .lb-pts{font-size:13px;font-weight:700;color:var(--gold-soft)}
763</style>
764<style id="empPremium">
765/* Premium surface polish (dark) — lift cards clearly off the near-black background */
766body:not(.light) .card,
767body:not(.light) .dcard,
768body:not(.light) .dept-block,
769body:not(.light) .ref{
770  background:linear-gradient(158deg,#1b1d25 0%,#141519 100%);
771  border-color:rgba(255,255,255,.09);
772  box-shadow:inset 0 1px 0 rgba(255,255,255,.05),0 20px 44px -26px rgba(0,0,0,.92);
773}
774body:not(.light) .ccard{
775  background:linear-gradient(158deg,#1c1e27 0%,#151620 100%);
776  border-color:rgba(255,255,255,.10);
777}
778body:not(.light) .drow,
779body:not(.light) .qa{
780  background:linear-gradient(158deg,#1a1c23 0%,#141519 100%);
781  border-color:rgba(255,255,255,.09);
782}
783body:not(.light) .tab{ background:#171820; border-color:rgba(255,255,255,.10); }
784/* Tactile hover / active — premium feel on interaction (both themes) */
785.dcard,.qa,.drow{transition:transform .18s ease,border-color .18s ease,box-shadow .18s ease}
786.dcard:hover{transform:translateY(-2px);border-color:rgba(232,201,119,.32);box-shadow:inset 0 1px 0 rgba(255,255,255,.06),0 24px 50px -24px rgba(0,0,0,.95)}
787.qa:hover{transform:translateY(-2px);border-color:rgba(232,201,119,.42);box-shadow:0 16px 34px -20px rgba(0,0,0,.92),0 0 0 1px rgba(232,201,119,.14)}
788.drow:hover{transform:translateY(-1px);border-color:rgba(232,201,119,.28)}
789.tab:hover{border-color:rgba(232,201,119,.45);color:var(--gold-soft)}
790.tab.active{box-shadow:0 8px 20px -10px rgba(200,155,60,.55)}
791</style>
792</head>
793<body>
794  <!-- LOGIN -->
795  <div class="login" id="login">
796    <div class="lcard">
797      <img src="data:image/jpeg;base64,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" alt="MVS"/>
798      <h2>Employee Portal</h2>
799      <p>MVS Foundation — sign in to your dashboard</p>
800      <input class="inp" id="liId" placeholder="Employee ID (e.g. MVSE123456)" onkeydown="if(event.key==='Enter')doLogin()">
801      <div class="lrel"><input class="inp" id="liPw" type="password" placeholder="Password" onkeydown="if(event.key==='Enter')doLogin()"><button class="ltgl" onclick="tgl()">SHOW</button></div>
802      <button class="lbtn" onclick="doLogin()" id="liBtn">Login</button>
803      <div class="lmsg" id="liMsg"></div>
804    </div>
805  </div>
806
807  <div id="app" style="display:none">
808    <div class="topbar">
809      <img src="data:image/jpeg;base64,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" alt="MVS" id="topLogo"/>
810      <button class="tbtn hamb" onclick="eSideOpen()" title="Menu" aria-label="Menu"><i class="ti ti-menu-2"></i></button>
811      <button class="tbtn tback" id="backBtn" onclick="dashGo('dashboard')" title="Back to dashboard" style="display:none"><i class="ti ti-arrow-left"></i></button>
812      <div class="tt"><b id="topName">Employee Portal</b><span id="topSub">MVS FOUNDATION</span></div>
813      <div class="sp"></div>
814      <button class="tbtn tb-desk" id="themeBtn" onclick="toggleTheme()" title="Theme"><i class="ti ti-sun"></i></button>
815      <button class="tbtn" onclick="gsOpen()" title="Search" aria-label="Search"><i class="ti ti-search"></i></button>
816      <button class="tbtn bell" id="bellBtn" onclick="toggleBell()"><i class="ti ti-bell"></i><span class="bdot" id="bdot" style="display:none">0</span></button>
817      <button class="tbtn tb-desk" id="refBtn" onclick="refresh()" title="Refresh"><i class="ti ti-refresh"></i></button>
818      <button class="tbtn tb-desk" onclick="logout()" title="Logout"><i class="ti ti-logout"></i></button>
819    </div>
820    <div id="eVeil" onclick="eSideClose()"></div>
821    <aside id="eSide">
822      <div class="eside-h"><div class="eside-logo" id="esideAv">MVS</div><div class="et"><b id="topName2">Employee Portal</b><span id="topSub2">MVS FOUNDATION</span></div><button class="eside-x" onclick="eSideClose()" aria-label="Close">&times;</button></div>
823      <div class="eside-scroll">
824        <div class="snav-item snav-dash active" data-tab="dashboard" onclick="eGo('dashboard',this)"><i class="ti ti-layout-dashboard"></i> Dashboard</div>
825        <div class="snav-grp"><div class="snav-head" onclick="eGrp(this)"><i class="ti ti-user-circle gi"></i><span class="gl">Me</span><i class="ti ti-chevron-down gc"></i></div><div class="snav-body"><div class="snav-bi"><div class="snav-item" data-tab="letters" data-mod="documents" onclick="eGo('letters',this)"><i class="ti ti-file-certificate"></i> HR Letters</div><div class="snav-item" data-tab="docs" data-mod="documents" onclick="eGo('docs',this)"><i class="ti ti-files"></i> Documents</div><div class="snav-item" data-tab="dept" data-mod="profile" onclick="eGo('dept',this)"><i class="ti ti-building"></i> Department</div><div class="snav-item" data-tab="salary" data-mod="salary" onclick="eGo('salary',this)"><i class="ti ti-receipt-2"></i> Salary</div></div></div></div>
826        <div class="snav-grp"><div class="snav-head" onclick="eGrp(this)"><i class="ti ti-clock-hour-4 gi"></i><span class="gl">Time &amp; Leave</span><i class="ti ti-chevron-down gc"></i></div><div class="snav-body"><div class="snav-bi"><div class="snav-item" data-tab="attendance" data-mod="attendance" onclick="eGo('attendance',this)"><i class="ti ti-clock-check"></i> Attendance</div><div class="snav-item" data-tab="leave" data-mod="leave" onclick="eGo('leave',this)"><i class="ti ti-calendar-off"></i> Leave</div><div class="snav-item" data-tab="meetings" data-mod="attendance" onclick="eGo('meetings',this)"><i class="ti ti-calendar-event"></i> Meetings</div></div></div></div>
827        <div class="snav-grp"><div class="snav-head" onclick="eGrp(this)"><i class="ti ti-trending-up gi"></i><span class="gl">Growth</span><i class="ti ti-chevron-down gc"></i></div><div class="snav-body"><div class="snav-bi"><div class="snav-item" data-tab="goals" data-mod="goals" onclick="eGo('goals',this)"><i class="ti ti-target"></i> Goals</div><div class="snav-item" data-tab="tasks" data-mod="tasks" onclick="eGo('tasks',this)"><i class="ti ti-checkbox"></i> Tasks</div><div class="snav-item" data-tab="performance" data-mod="performance" onclick="eGo('performance',this)"><i class="ti ti-chart-line"></i> Performance</div><div class="snav-item" data-tab="rewards" data-mod="rewards" onclick="eGo('rewards',this)"><i class="ti ti-award"></i> Rewards</div><div class="snav-item" data-tab="learning" data-mod="learning" onclick="eGo('learning',this)"><i class="ti ti-school"></i> Learning</div></div></div></div>
828        <div class="snav-grp"><div class="snav-head" onclick="eGrp(this)"><i class="ti ti-building-community gi"></i><span class="gl">Workplace</span><i class="ti ti-chevron-down gc"></i></div><div class="snav-body"><div class="snav-bi"><div class="snav-item" data-tab="engagement" data-mod="engagement" onclick="eGo('engagement',this)"><i class="ti ti-message-2-heart"></i> Engagement</div><div class="snav-item" data-tab="directory" data-mod="directory" onclick="eGo('directory',this)"><i class="ti ti-address-book"></i> Directory</div><div class="snav-item" data-tab="orgchart" data-mod="directory" onclick="eGo('orgchart',this)"><i class="ti ti-sitemap"></i> Org Chart</div><div class="snav-item" data-tab="timeline" data-mod="notices" onclick="eGo('timeline',this)"><i class="ti ti-timeline"></i> Timeline</div><div class="snav-item" data-tab="forms" data-mod="engagement" onclick="eGo('forms',this)"><i class="ti ti-forms"></i> Forms</div><div class="snav-item" data-tab="notice" data-mod="notices" onclick="eGo('notice',this)"><i class="ti ti-speakerphone"></i> Notice</div><div class="snav-item" data-tab="policies" data-mod="policies" onclick="eGo('policies',this)"><i class="ti ti-book"></i> Policies</div></div></div></div>
829        <div class="snav-grp"><div class="snav-head" onclick="eGrp(this)"><i class="ti ti-headset gi"></i><span class="gl">Support</span><i class="ti ti-chevron-down gc"></i></div><div class="snav-body"><div class="snav-bi"><div class="snav-item" data-tab="help" data-mod="helpdesk" onclick="eGo('help',this)"><i class="ti ti-headset"></i> Help &amp; Feedback</div><div class="snav-item" data-tab="messages" data-mod="helpdesk" onclick="eGo('messages',this)"><i class="ti ti-messages"></i> Messages</div><div class="snav-item" data-tab="kb" data-mod="helpdesk" onclick="eGo('kb',this)"><i class="ti ti-book-2"></i> Knowledge Base</div></div></div></div>
830      </div>
831      <div class="eside-foot">
832        <button class="ef-btn" onclick="toggleTheme();eSideClose()"><i class="ti ti-moon"></i> Theme</button>
833        <button class="ef-btn" onclick="refresh();eSideClose()"><i class="ti ti-refresh"></i> Refresh</button>
834        <button class="ef-btn danger" onclick="logout()"><i class="ti ti-logout"></i> Logout</button>
835      </div>
836    </aside>
837    <div class="bpanel" id="bpanel"></div>
838    <div id="selfieOverlay">
839      <div class="selfie-box">
840        <div class="selfie-h"><b id="selfieTitle"><i class="ti ti-camera-selfie"></i> Verify it’s you</b><button class="gs-x" onclick="closeSelfie()" aria-label="Close">&times;</button></div>
841        <div class="selfie-sub" id="selfieSub">Take a quick live selfie to mark your attendance.</div>
842        <video id="selfieVid" class="selfie-vid" playsinline autoplay muted></video>
843        <div id="selfiePh" class="selfie-ph" style="display:none"><div class="selfie-ph-ic"><i class="ti ti-camera-selfie"></i></div><div class="selfie-ph-t">Take a quick selfie</div></div>
844        <div id="selfieState" style="font-size:12.5px;min-height:16px;margin:7px 2px 3px;text-align:center;color:#ffb0a2"></div>
845        <button class="btn selfie-cap" id="selfieCapBtn" onclick="captureSelfie()"><i class="ti ti-camera"></i> Capture &amp; Punch</button>
846        <input type="file" accept="image/*" capture="user" id="selfieFileInput" style="display:none" onchange="selfieFilePicked(this)">
847        <button id="selfieNativeBtn" onclick="selfieNativePick()" style="margin-top:9px;width:100%;background:none;border:1px solid rgba(255,255,255,.22);color:var(--muted);padding:11px;border-radius:12px;font-size:13px;font-weight:700;cursor:pointer"><i class="ti ti-camera-plus"></i> Camera not opening? Take photo instead</button>
848      </div>
849    </div>
850    <div id="gsOverlay" onclick="if(event.target===this)gsClose()">
851      <div class="gs-box">
852        <div class="gs-in"><i class="ti ti-search"></i><input id="gsInput" placeholder="Search everything…" oninput="gsSearch()" autocomplete="off"><button class="gs-x" onclick="gsClose()" aria-label="Close">&times;</button></div>
853        <div id="gsResults"><div class="gs-hint">Search letters, colleagues, meetings, articles & more…</div></div>
854      </div>
855    </div>
856
857    <div class="wrap">
858      <section class="panel show" id="dashboard">
859      <section class="snap">
860        <div class="snap-l">
861          <div class="snap-hi">
862            <span class="snap-eyebrow" id="greetPrefix">Welcome</span>
863            <h2 id="eName">Employee</h2>
864          </div>
865          <a id="hbBtn" class="snap-hb" target="_blank" title="Employee Handbook"><i class="ti ti-book-2"></i><span>Handbook</span></a>
866        </div>
867        <div class="snap-chips">
868          <div class="chip"><i class="ti ti-id-badge-2"></i> ID: <b id="eId" style="color:#fff;margin-left:4px">—</b></div>
869          <div class="chip" id="mgrChip" style="display:none"><i class="ti ti-user-star"></i> Manager: <b id="eMgr" style="color:#fff;margin-left:4px"></b></div>
870          <span class="chip" id="eRoleBadge" style="display:none"><i class="ti ti-shield-check"></i> <b id="eRoleTxt" style="color:#fff;margin-left:4px"></b></span>
871          <div class="chip"><i class="ti ti-user-check"></i> Profile: <b id="eProfilePct" style="color:#fff;margin-left:4px">—</b></div>
872        </div>
873      </section>
874
875      <div id="punchCard"></div>
876      <div class="crm" id="crm"></div>
877      <div id="onboardCard"></div>
878      <div id="assetsCard"></div>
879
880      <div id="warnTop"></div>
881
882      <div class="dsec-h"><i class="ti ti-layout-grid"></i> Today’s Workspace</div>
883      <div class="dash-grid">
884        <div class="dcard s8"><div class="dcard-h"><span><i class="ti ti-checklist"></i> My Tasks</span><a class="dcard-all" onclick="dashGo('tasks')">View all <i class="ti ti-arrow-right"></i></a></div><div id="dwTasks"><div class="dw-sk"></div><div class="dw-sk"></div><div class="dw-sk"></div></div></div>
885        <div class="dcard s4"><div class="dcard-h"><span><i class="ti ti-bolt"></i> Quick Actions</span></div>
886          <div class="dw-quick">
887            <button class="qa" onclick="dashGo('leave')"><i class="ti ti-calendar-plus"></i><span>Apply Leave</span></button>
888            <button class="qa" onclick="dashGo('salary')"><i class="ti ti-cash"></i><span>Salary Slip</span></button>
889            <button class="qa" onclick="dashGo('docs')"><i class="ti ti-files"></i><span>Documents</span></button>
890            <button class="qa" onclick="dashGo('attendance')"><i class="ti ti-clock-check"></i><span>Attendance</span></button>
891            <button class="qa" onclick="dashGo('letters')"><i class="ti ti-file-text"></i><span>HR Letters</span></button>
892            <button class="qa" onclick="dashGo('help')"><i class="ti ti-headset"></i><span>Raise Ticket</span></button>
893          </div>
894        </div>
895        <div class="dcard s6"><div class="dcard-h"><span><i class="ti ti-speakerphone"></i> Company Updates</span><a class="dcard-all" onclick="dashGo('notice')">View all <i class="ti ti-arrow-right"></i></a></div><div id="dwUpdates"><div class="dw-sk"></div><div class="dw-sk"></div></div></div>
896        <div class="dcard s6"><div class="dcard-h"><span><i class="ti ti-calendar-event"></i> Upcoming</span><a class="dcard-all" onclick="dashGo('meetings')">View all <i class="ti ti-arrow-right"></i></a></div><div id="dwUpcoming"><div class="dw-sk"></div><div class="dw-sk"></div></div></div>
897        <div class="dcard s4"><div class="dcard-h"><span><i class="ti ti-calendar-stats"></i> Leave Balance</span><a class="dcard-all" onclick="dashGo('leave')">Apply <i class="ti ti-arrow-right"></i></a></div><div id="dwLeave"><div class="dw-sk"></div><div class="dw-sk"></div></div></div>
898        <div class="dcard s4"><div class="dcard-h"><span><i class="ti ti-chart-line"></i> Performance</span><a class="dcard-all" onclick="dashGo('performance')">Details <i class="ti ti-arrow-right"></i></a></div><div id="dwPerf"><div class="dw-sk"></div></div></div>
899        <div class="dcard s4 dcard-journey"><section class="acc" id="progCard" style="margin:14px 0">
900        <div class="acc-h" onclick="toggleAcc(this)"><div class="ai"><i class="ti ti-rocket"></i></div><div class="at"><b>Your Journey Progress</b><span id="progSub">Loading…</span></div><i class="ti ti-chevron-down chev"></i></div>
901        <div class="acc-b"><div class="acc-bi">
902          <div class="progbar" style="margin-top:6px"><div class="progfill" id="progFill" style="width:0%"></div></div>
903          <div id="progSteps"></div>
904          <div class="pmsg" id="progMsg"></div>
905        </div></div>
906      </section></div>
907      </div>
908      </section>
909
910      <nav class="tabs">
911        <button class="tab active" data-tab="dashboard" onclick="showTab('dashboard',this)"><i class="ti ti-layout-dashboard"></i> Dashboard</button>
912        <button class="tab" data-tab="letters" data-mod="documents" onclick="showTab('letters',this)"><i class="ti ti-file-certificate"></i> HR Letters</button>
913        <button class="tab" data-tab="docs" data-mod="documents" onclick="showTab('docs',this)"><i class="ti ti-files"></i> Documents</button>
914        <button class="tab" data-tab="dept" data-mod="profile" onclick="showTab('dept',this)"><i class="ti ti-building"></i> Department</button>
915        <button class="tab" data-tab="salary" data-mod="salary" onclick="showTab('salary',this)"><i class="ti ti-receipt-2"></i> Salary</button>
916        <button class="tab" data-tab="attendance" data-mod="attendance" onclick="showTab('attendance',this)"><i class="ti ti-clock-check"></i> Attendance</button>
917        <button class="tab" data-tab="leave" data-mod="leave" onclick="showTab('leave',this)"><i class="ti ti-calendar-off"></i> Leave</button>
918        <button class="tab" data-tab="goals" data-mod="goals" onclick="showTab('goals',this)"><i class="ti ti-target"></i> Goals</button>
919        <button class="tab" data-tab="tasks" data-mod="tasks" onclick="showTab('tasks',this)"><i class="ti ti-checkbox"></i> Tasks</button>
920        <button class="tab" data-tab="performance" data-mod="performance" onclick="showTab('performance',this)"><i class="ti ti-chart-line"></i> Performance</button>
921        <button class="tab" data-tab="rewards" data-mod="rewards" onclick="showTab('rewards',this)"><i class="ti ti-award"></i> Rewards</button>
922        <button class="tab" data-tab="learning" data-mod="learning" onclick="showTab('learning',this)"><i class="ti ti-school"></i> Learning</button>
923        <button class="tab" data-tab="engagement" data-mod="engagement" onclick="showTab('engagement',this)"><i class="ti ti-message-2-heart"></i> Engagement</button>
924        <button class="tab" data-tab="directory" data-mod="directory" onclick="showTab('directory',this)"><i class="ti ti-address-book"></i> Directory</button>
925        <button class="tab" data-tab="orgchart" data-mod="directory" onclick="showTab('orgchart',this)"><i class="ti ti-sitemap"></i> Org Chart</button>
926        <button class="tab" data-tab="timeline" data-mod="notices" onclick="showTab('timeline',this)"><i class="ti ti-timeline"></i> Timeline</button>
927        <button class="tab" data-tab="kb" data-mod="helpdesk" onclick="showTab('kb',this)"><i class="ti ti-book-2"></i> Knowledge Base</button>
928        <button class="tab" data-tab="meetings" data-mod="attendance" onclick="showTab('meetings',this)"><i class="ti ti-calendar-event"></i> Meetings</button>
929        <button class="tab" data-tab="forms" data-mod="engagement" onclick="showTab('forms',this)"><i class="ti ti-forms"></i> Forms</button>
930        <button class="tab" data-tab="messages" data-mod="helpdesk" onclick="showTab('messages',this)"><i class="ti ti-messages"></i> Messages</button>
931        <button class="tab" data-tab="notice" data-mod="notices" onclick="showTab('notice',this)"><i class="ti ti-speakerphone"></i> Notice</button>
932        <button class="tab" data-tab="policies" data-mod="policies" onclick="showTab('policies',this)"><i class="ti ti-book"></i> Policies</button>
933        <button class="tab" data-tab="help" data-mod="helpdesk" onclick="showTab('help',this)"><i class="ti ti-headset"></i> Help &amp; Feedback</button>
934      </nav>
935
936      <section class="panel card" id="letters">
937        <div class="sectitle"><i class="ti ti-file-certificate"></i> HR Letters</div>
938        <p style="color:var(--muted);font-size:13px;margin:-4px 0 14px">Letters &amp; documents issued to you by HR. Download anytime.</p>
939        <div id="myLettersList"><div class="empty">Loading…</div></div>
940      </section>
941
942      <section class="panel card" id="docs">
943        <div class="sectitle"><i class="ti ti-files"></i> My Documents</div>
944        <div id="docsList"></div>
945      </section>
946
947      <section class="panel card" id="dept">
948        <div class="sectitle"><i class="ti ti-building"></i> Department &amp; Role</div>
949        <div class="kv" id="deptKv"></div>
950        <div style="margin-top:18px;display:flex;gap:10px;flex-wrap:wrap">
951          <button class="btn" onclick="openKRA()"><i class="ti ti-target"></i> View My KRA</button>
952          <a class="dl ghost" onclick="genKRA()" style="cursor:pointer"><i class="ti ti-download"></i> Download KRA</a>
953        </div>
954      </section>
955
956      <section class="panel card" id="salary">
957        <div class="sectitle"><i class="ti ti-receipt-2"></i> Salary</div>
958        <p style="color:var(--muted);font-size:13px;margin-bottom:14px">Review each month's slip. Any discrepancy? Use "Report issue".</p>
959        <div id="salAnnex"></div>
960        <div id="slipList"></div>
961        <div id="issueBox" style="display:none;margin-top:6px">
962          <label>Describe the issue for <b id="issueMonth"></b></label>
963          <textarea id="issueText" placeholder="Explain the salary issue..."></textarea>
964          <button class="btn" onclick="submitIssue()">Submit Issue</button>
965        </div>
966        <div id="myIssues" style="margin-top:16px"></div>
967        <div id="salRevisions"></div>
968        <div class="sectitle" style="margin-top:24px"><i class="ti ti-receipt"></i> Reimbursements</div>
969        <p style="color:var(--muted);font-size:13px;margin:-4px 0 12px">Claim work expenses. Once approved by Accounts, they're paid with your salary.</p>
970        <div class="lv-form">
971          <div><label>Category</label><select class="inp" id="reimCat"><option value="travel">Travel</option><option value="food">
971Food</option><option value="hotel">Hotel</option><option value="petrol">Petrol</option><option value="internet">Internet</option><option value="medical">Medical</option><option value="other">Other</option></select></div>
972          <div><label>Amount (₹)</label><input class="inp" type="number" id="reimAmt"></div>
973          <div><label>Date</label><input class="inp" type="date" id="reimDate"></div>
974        </div>
975        <label style="font-size:12px;color:var(--muted);display:block;margin-bottom:4px">Description</label>
976        <textarea id="reimDesc" placeholder="What is this expense for?"></textarea>
977        <label class="reim-file"><i class="ti ti-paperclip"></i> Attach bill (optional)<input type="file" id="reimFile" accept="image/*,application/pdf" style="display:none"></label>
978        <button class="btn" style="margin-top:10px" onclick="submitReim()"><i class="ti ti-send"></i> Submit claim</button>
979        <div id="myReims" style="margin-top:16px"></div>
980      </section>
981
982      <section class="panel card" id="attendance">
983        <div class="sectitle"><i class="ti ti-clock-check"></i> Attendance</div>
984        <div class="acal-head"><button class="sal-nav" onclick="attMonth(-1)"><i class="ti ti-chevron-left"></i></button><span id="acalLabel" class="sal-yrlbl"></span><button class="sal-nav" onclick="attMonth(1)"><i class="ti ti-chevron-right"></i></button></div>
985        <div id="attSummary"></div>
986        <div id="attCalendar"></div>
987        <div class="acal-legend"><span><i class="acal-dot present"></i> Present</span><span><i class="acal-dot half"></i> Half</span><span><i class="acal-dot leave"></i> Leave</span><span><i class="acal-dot holiday"></i> Holiday</span><span><i class="acal-dot absent"></i> Absent</span><span><i class="acal-dot warn"></i> Missing punch-out</span></div>
988      </section>
989
990      <section class="panel card" id="leave">
991        <div class="sectitle"><i class="ti ti-calendar-off"></i> Leave</div>
992        <div id="leaveBalances"></div>
993        <div id="leaveHolidays"></div>
994        <div class="accwrap-head" style="margin-top:14px">Apply for leave</div>
995        <div class="lv-form">
996          <div><label>Type</label><select class="inp" id="lvType"></select></div>
997          <div><label>From</label><input class="inp" type="date" id="lvFrom"></div>
998          <div><label>To</label><input class="inp" type="date" id="lvTo"></div>
999        </div>
1000        <label class="lv-half"><input type="checkbox" id="lvHalf"> Half day</label>
1001        <textarea id="lvReason" placeholder="Reason for leave…"></textarea>
1002        <button class="btn" onclick="applyLeave()"><i class="ti ti-send"></i> Apply for Leave</button>
1003        <div id="myLeaves" style="margin-top:16px"></div>
1004      </section>
1005
1006      <section class="panel card" id="goals">
1007        <div class="sectitle"><i class="ti ti-target"></i> Goals</div>
1008        <div id="goalsWrap"><div class="empty">Loading…</div></div>
1009      </section>
1010
1011      <section class="panel card" id="tasks">
1012        <div class="sectitle"><i class="ti ti-checkbox"></i> My Tasks</div>
1013        <div id="tasksWrap"><div class="empty">Loading…</div></div>
1014      </section>
1015
1016      <section class="panel card" id="performance">
1017        <div class="sectitle"><i class="ti ti-chart-line"></i> My Performance</div>
1018        <div id="perfWrap"><div class="empty">Loading…</div></div>
1019      </section>
1020
1021      <section class="panel card" id="rewards">
1022        <div class="sectitle"><i class="ti ti-award"></i> Rewards &amp; Recognition</div>
1023        <div id="rewardsWrap"><div class="empty">Loading…</div></div>
1024      </section>
1025
1026      <section class="panel card" id="learning">
1027        <div class="sectitle"><i class="ti ti-school"></i> Learning &amp; Training</div>
1028        <div id="coursesWrap"><div class="empty">Loading…</div></div>
1029      </section>
1030
1031      <section class="panel card" id="engagement">
1032        <div class="sectitle"><i class="ti ti-message-2-heart"></i> Engagement</div>
1033        <div id="engageWrap"><div class="empty">Loading…</div></div>
1034      </section>
1035
1036      <section class="panel card" id="directory">
1037        <div class="sectitle"><i class="ti ti-address-book"></i> Team Directory</div>
1038        <input class="inp" id="dirSearch" placeholder="Search by name, department, designation…" oninput="renderDirectory()" style="margin-bottom:14px">
1039        <div id="dirList"><div class="empty">Loading…</div></div>
1040      </section>
1041
1042      <section class="panel card" id="orgchart">
1043        <div class="sectitle"><i class="ti ti-sitemap"></i> Organization Chart</div>
1044        <p style="color:var(--muted);font-size:13px;margin-bottom:14px">Reporting structure across the team.</p>
1045        <div id="orgWrap"><div class="empty">Loading…</div></div>
1046      </section>
1047
1048      <section class="panel card" id="timeline">
1049        <div class="sectitle"><i class="ti ti-timeline"></i> Company Timeline</div>
1050        <p style="color:var(--muted);font-size:13px;margin-bottom:14px">Milestones & moments at MVS Foundation.</p>
1051        <div id="tlWrap"><div class="empty">Loading…</div></div>
1052      </section>
1053
1054      <section class="panel card" id="kb">
1055        <div class="sectitle"><i class="ti ti-book-2"></i> Knowledge Base</div>
1056        <input class="inp" id="kbSearch" placeholder="Search articles, FAQs…" oninput="renderKB()" style="margin-bottom:10px">
1057        <div class="kb-cats" id="kbCats"></div>
1058        <div id="kbList"><div class="empty">Loading…</div></div>
1059      </section>
1060
1061      <section class="panel card" id="meetings">
1062        <div class="sectitle"><i class="ti ti-calendar-event"></i> Meetings & Events</div>
1063        <div class="kb-cats" id="mtgTabs"><button class="kb-cat on" onclick="mtgView('upcoming')">Upcoming</button><button class="kb-cat" onclick="mtgView('past')">Past</button></div>
1064        <div id="mtgList"><div class="empty">Loading…</div></div>
1065      </section>
1066
1067      <section class="panel card" id="forms">
1068        <div class="sectitle"><i class="ti ti-forms"></i> Forms</div>
1069        <div id="formsList"><div class="empty">Loading…</div></div>
1070      </section>
1071
1072      <section class="panel card" id="messages">
1073        <div class="sectitle"><i class="ti ti-messages"></i> Messages with HR</div>
1074        <div class="msg-msgs" id="msgMsgs"><div class="empty">Loading…</div></div>
1075        <div class="msg-in"><input class="inp" id="msgInput" placeholder="Type a message to HR…" onkeydown="if(event.key==='Enter')sendMsg()"><button class="btn" onclick="sendMsg()"><i class="ti ti-send"></i></button></div>
1076      </section>
1077
1078      <section class="panel card" id="notice">
1079        <div class="sectitle"><i class="ti ti-speakerphone"></i> Notices</div>
1080        <div id="noticeList"></div>
1081      </section>
1082
1083      <section class="panel card" id="policies">
1084        <div class="sectitle"><i class="ti ti-book"></i> Company Policies</div>
1085        <div id="policyList"></div>
1086      </section>
1087
1088      <section class="panel card" id="help">
1089        <div class="sectitle"><i class="ti ti-headset"></i> Need Help? Raise a Request</div>
1090        <textarea id="hlpText" placeholder="Describe your query or problem..."></textarea>
1091        <button class="btn" onclick="submitComplaint()">Submit Request</button>
1092        <div id="myComplaints" style="margin-top:16px"></div>
1093        <div id="myApprovals"></div>
1094        <div class="sectitle" style="margin-top:24px">
1094<i class="ti ti-door-exit"></i> Resignation / Exit</div>
1095        <div id="resignBox"></div>
1096      </section>
1097    </div>
1098  </div>
1099  <div id="kraModal" onclick="if(event.target===this)this.style.display='none'" style="display:none;position:fixed;inset:0;z-index:80;background:rgba(5,10,18,.72);backdrop-filter:blur(4px);align-items:center;justify-content:center;padding:20px">
1100    <div style="width:min(560px,100%);background:#141418;border:1px solid var(--line);border-radius:18px;padding:24px;max-height:80vh;overflow:auto;box-shadow:0 30px 80px -20px rgba(0,0,0,.8)">
1101      <div style="display:flex;align-items:center;gap:10px;margin-bottom:14px"><i class="ti ti-target" style="color:var(--gold-soft);font-size:22px"></i><b style="font-size:18px;flex:1">Key Responsibility Areas (KRA)</b><button onclick="document.getElementById('kraModal').style.display='none'" style="background:none;border:none;color:var(--muted);font-size:24px;cursor:pointer;line-height:1">×</button></div>
1102      <div id="kraBody" style="font-size:14px;line-height:1.8;color:var(--txt);white-space:pre-wrap"></div>
1103    </div>
1104  </div>
1105  <div id="toast"></div>
1106  <div class="pdfstage" id="pdfstage"></div>
1107
1108<script>
1109  const API_URL = "https://qqbpgfbyyvjuqogardbv.supabase.co/functions/v1/api";
1110  try{ fetch(API_URL,{method:"POST",body:new URLSearchParams({action:"warmup"}),keepalive:true}).catch(function(){}); }catch(_e){}  // warm the edge before login
1111  let BUNDLE=null, currentId='', rateVal=0, cmpAll=false, _myLettersLoaded=false;
1112
1113  /* ---------- helpers ---------- */
1114  /* Browser = session-only login (clears on close); installed MVS app = persists across app close
1115     (WebView clears sessionStorage on process death). Detected via the app's UA suffix. */
1116  var _INAPP = / MVSPortalApp\//.test(navigator.userAgent);
1117  var _SS = window.localStorage; // persist login in BOTH browser + app (staff enter password once, stay logged in)
1118  function ssGet(k){try{return _SS.getItem(k)||''}catch(e){return''}}
1119  function ssSet(k,v){try{_SS.setItem(k,v)}catch(e){}}
1120  function ssDel(k){try{_SS.removeItem(k); localStorage.removeItem(k);}catch(e){}}
1121  /* Bridge to the installed Android app for the background red badge (in-app + launcher icon). No-op in a browser. */
1122  function _mvsSaveSession(id,tok,name){try{ if(window.MvsNative && MvsNative.saveSession) MvsNative.saveSession(String(id||''),String(tok||''),String(name||'')); }catch(e){}}
1123  function _mvsClearSession(){try{ if(window.MvsNative && MvsNative.clearSession) MvsNative.clearSession(); }catch(e){}}
1124  function lsGet(k){try{return localStorage.getItem(k)||''}catch(e){return''}}
1125  function lsSet(k,v){try{localStorage.setItem(k,v)}catch(e){}}
1126  function applyTheme(t){ document.body.classList.toggle('night', t==='night'); const b=document.getElementById('themeBtn'); if(b) b.innerHTML='<i class="ti '+(t==='night'?'ti-moon-stars':'ti-moon')+'"></i>'; }
1127  function toggleTheme(){ const t=document.body.classList.contains('night')?'dark':'night'; lsSet('ems_theme',t); applyTheme(t); }
1128  applyTheme(lsGet('ems_theme')||'dark');
1129  document.addEventListener('click',function(ev){var t=ev.target; if(t&&t.tagName==='INPUT'&&t.type==='date'){try{t.showPicker();}catch(e){}}});
1130  function toggleAcc(h){ h.parentElement.classList.toggle('open'); }
1131  function eSideOpen(){ var d=document.getElementById('eSide'),v=document.getElementById('eVeil'); if(d)d.classList.add('on'); if(v)v.classList.add('on'); document.body.style.overflow='hidden'; }
1132  function eSideClose(){ var d=document.getElementById('eSide'),v=document.getElementById('eVeil'); if(d)d.classList.remove('on'); if(v)v.classList.remove('on'); document.body.style.overflow=''; }
1133  function eGrp(head){ head.parentElement.classList.toggle('open'); }
1134  function eGo(id,el){ var btn=document.querySelector('.tab[data-tab="'+id+'"]'); if(btn) showTab(id,btn); document.querySelectorAll('#eSide .snav-item').forEach(function(n){n.classList.remove('active');}); if(el){ el.classList.add('active'); var g=el.closest('.snav-grp'); if(g&&!g.classList.contains('open')) g.classList.add('open'); } eSideClose(); }
1135  // Show/hide the whole attendance system (punch card + Attendance & Leave nav items, tabs and sections) based
1136  // on whether attendance is enabled for this employee. Docs/letters-only staff get everything else as normal.
1137  function _applyAttEnabled(){
1138    var on = window._attEnabled !== false;
1139    var HIDE=['attendance','leave'];
1140    try{
1141      var _pc=document.getElementById('punchCard'); if(_pc){ _pc.style.display=on?'':'none'; if(!on) _pc.innerHTML=''; }
1142      HIDE.forEach(function(tab){ document.querySelectorAll('[data-tab="'+tab+'"]').forEach(function(x){ x.style.display=on?'':'none'; }); });
1143      if(!on){
1144        // If the person is currently on a now-hidden section, send them back to the dashboard.
1145        var cur=window._curTab||'';
1146        if(HIDE.indexOf(cur)>=0){ var db=document.querySelector('.snav-item[data-tab="dashboard"]'); if(db){ try{ eGo('dashboard',db); }catch(_){} } }
1147        HIDE.forEach(function(id){ var pn=document.getElementById(id); if(pn) pn.classList.remove('show'); });
1148      }
1149    }catch(_){}
1150  }
1151  function eSyncActive(){ var sh=document.querySelector('.panel.show'); var id=sh?sh.id:'dashboard'; var it=document.querySelector('#eSide .snav-item[data-tab="'+id+'"]'); document.querySelectorAll('#eSide .snav-item').forEach(function(n){n.classList.remove('active');}); if(it){ it.classList.add('active'); var g=it.closest('.snav-grp'); if(g) g.classList.add('open'); } }
1152  /* ---- Global Search ---- */
1153  var _gsT;
1154  function gsOpen(){ var o=document.getElementById('gsOverlay'); if(o){ o.classList.add('on'); setTimeout(function(){var i=document.getElementById('gsInput'); if(i)i.focus();},60); } }
1155  function gsClose(){ var o=document.getElementById('gsOverlay'); if(o) o.classList.remove('on'); }
1156  function gsSearch(){ clearTimeout(_gsT); _gsT=setTimeout(gsDo,250); }
1157  async function gsDo(){
1158    const el=document.getElementById('gsResults'); if(!el) return;
1159    const q=((document.getElementById('gsInput')||{}).value||'');
1160    if(q.trim().length<2){ el.innerHTML='<div class="gs-hint">Type at least 2 characters…</div>'; return; }
1161    el.innerHTML='<div class="gs-hint">Searching…</div>';
1162    const id=currentId||ssGet('ems_id');
1163    const d=await api({action:'emsGlobalSearch', employeeId:id, q:q});
1164    const rows=(d&&d.rows)||[];
1165    if(!rows.length){ el.innerHTML='<div class="gs-hint">No results for "'+esc(q)+'"</div>'; return; }
1166    const groups={}; rows.forEach(function(r){ (groups[r.type]=groups[r.type]||[]).push(r); });
1167    const ico={'Knowledge Base':'ti-book-2','Meeting':'ti-calendar-event','Timeline':'ti-timeline','Colleague':'ti-user','Notice':'ti-speakerphone','HR Letter':'ti-file-certificate'};
1168    let h='';
1168 Object.keys(groups).forEach(function(t){ h+='<div class="gs-cat">'+esc(t)+'</div>'; h+=groups[t].map(function(r){ return '<div class="gs-item" onclick="gsGo(\''+esc(r.tab)+'\')"><i class="ti '+(ico[t]||'ti-point')+'"></i><div class="gs-txt"><b>'+esc(r.title||'')+'</b>'+(r.subtitle?'<span>'+esc(r.subtitle)+'</span>':'')+'</div><i class="ti ti-arrow-right gs-go"></i></div>'; }).join(''); });
1169    el.innerHTML=h;
1170  }
1171  function gsGo(tab){ gsClose(); var it=document.querySelector('#eSide .snav-item[data-tab="'+tab+'"]'); if(it){ eGo(tab,it); } else { var btn=document.querySelector('.tab[data-tab="'+tab+'"]'); if(btn) showTab(tab,btn); } }
1172  function accItem(icon, title, sub, bodyHtml, urgent){
1173    return '<div class="acc"><div class="acc-h" onclick="toggleAcc(this)"><div class="ai"'+(urgent?' style="background:rgba(224,83,63,.16);color:#ff9c8b"':'')+'><i class="ti '+icon+'"></i></div><div class="at"><b>'+title+'</b>'+(sub?'<span>'+sub+'</span>':'')+'</div><i class="ti ti-chevron-down chev"></i></div><div class="acc-b"><div class="acc-bi">'+bodyHtml+'</div></div></div>';
1174  }
1175  function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]))}
1176  function rupee(v){const n=Number(String(v==null?'':v).replace(/[^0-9.]/g,''))||0;return '\u20B9'+n.toLocaleString('en-IN')}
1177  function numv(v){return Number(String(v==null?'':v).replace(/[^0-9.]/g,''))||0}
1178  function toast(m){const t=document.getElementById('toast');t.textContent=m;t.style.display='block';clearTimeout(t._t);t._t=setTimeout(()=>t.style.display='none',2600)}
1179  function fmt12(t){ t=String(t||""); if(t.indexOf(":")<0) return t; var pp=t.split(":"); var h=parseInt(pp[0],10), m=pp[1]; if(isNaN(h)) return t; var ap=h>=12?"PM":"AM"; var h12=(h%12)||12; return h12+":"+m+" "+ap; }
1180  function fmtDate(v){if(!v)return'';let d=new Date(String(v).indexOf('T')<0?String(v).replace(' ','T'):String(v));if(isNaN(d))return String(v);try{return d.toLocaleDateString('en-IN',{day:'numeric',month:'long',year:'numeric',timeZone:'Asia/Kolkata'})}catch(e){return String(v)}}
1181  function fmtDT(v){if(!v)return'';let d=new Date(String(v).indexOf('T')<0?String(v).replace(' ','T'):String(v));if(isNaN(d))return String(v);try{return d.toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'numeric',minute:'2-digit',hour12:true,timeZone:'Asia/Kolkata'}).replace(/\u202f/g,' ')}catch(e){return String(v)}}
1182  function tgl(){const p=document.getElementById('liPw');p.type=p.type==='password'?'text':'password'}
1183  async function api(params){const fd=new URLSearchParams();Object.keys(params).forEach(k=>fd.append(k,params[k]));const r=await fetch(API_URL,{method:'POST',body:fd});const _t=await r.text();return JSON.parse(_t.replace(/https?:\/\/[a-z0-9-]+\.supabase\.co\/storage\/v1\/object\/public\/(uploads|media)\//gi,"https://files.mediamvs.com/$1/"));}
1184
1185  /* ---------- login ---------- */
1186  async function doLogin(){
1187    const id=document.getElementById('liId').value.trim(), pw=document.getElementById('liPw').value.trim();
1188    const msg=document.getElementById('liMsg'); msg.textContent='';
1189    if(!id||!pw){msg.textContent='Enter Employee ID and password.';return}
1190    const b=document.getElementById('liBtn'); b.disabled=true; b.textContent='Signing in…';
1191    try{ const d=await api({action:'emsLogin', employeeId:id, password:pw});
1192      if(d.status==='success'){ currentId=id; ssSet('ems_id',id); ssSet('ems_tok',d.token||''); _mvsSaveSession(id, d.token||'', (d.name||d.employeeName||id)); load(d); document.getElementById('login').classList.add('hidden'); document.getElementById('app').style.display='block'; try{ window._curTab='dashboard'; ping('dashboard'); }catch(_){} }
1193      else msg.textContent=d.message||'Login failed.';
1194    }catch(e){ msg.textContent='Could not connect to server.'; }
1195    b.disabled=false; b.textContent='Login';
1196  }
1197  function logout(){ _mvsClearSession(); ssDel('ems_id'); ssDel('ems_tok'); currentId=''; location.reload(); }
1198
1199  /* ---------- load + render ---------- */
1200  function load(d){
1201    // A lite background refresh omits the rarely-changing global arrays to save data — keep the copies the
1202    // app already has so the Policies / Company Docs / Performers tabs never go blank between full loads.
1203    if(BUNDLE){ ['policies','companyDocs','performers'].forEach(function(k){ if(d[k]===undefined && BUNDLE[k]!==undefined) d[k]=BUNDLE[k]; }); }
1204    BUNDLE=d; const e=d.employee||{};
1205    // Attendance system shows only when it's enabled for this employee. Docs/letters-only staff never see
1206    // punch in/out, the attendance calendar, or Leave — everything attendance-related is hidden for them.
1207    window._attEnabled = (d.attendanceEnabled !== false);
1208    try{ _applyAttEnabled(); }catch(_ae){}
1209    try{ var _tn=document.getElementById('topName'); if(_tn)_tn.textContent=e.name||'Employee'; var _ts=document.getElementById('topSub'); if(_ts)_ts.textContent=e.designation||e.department||'MVS Foundation'; var _tl=document.getElementById('topLogo'); if(_tl && e.photo){ _tl.src=e.photo; _tl.classList.add('has-photo'); } var _tn2=document.getElementById('topName2'); if(_tn2)_tn2.textContent=e.name||'Employee'; var _ts2=document.getElementById('topSub2'); if(_ts2)_ts2.textContent=e.designation||e.department||'MVS Foundation'; var _ea=document.getElementById('esideAv'); if(_ea){ if(e.photo){ _ea.style.backgroundImage='url('+e.photo+')'; _ea.style.backgroundSize='cover'; _ea.style.backgroundPosition='center'; _ea.textContent=''; _ea.style.borderRadius='50%'; } else { _ea.textContent=(String(e.name||'?').trim()[0]||'M').toUpperCase(); } } }catch(_th){}
1210    window._empName=e.name||'Employee'; typeName(window._empName);
1211    try{ var _av=document.getElementById('eAvatar'); if(_av){ _av.style.backgroundImage=''; _av.textContent=(String(e.name||'?').trim()[0]||'?').toUpperCase(); } }catch(_a){}
1212    try{ var _fk='ems_greeted_'+(e.employeeId||'x'), _first=!localStorage.getItem(_fk); if(_first) localStorage.setItem(_fk,'1'); var _hr=new Date().getHours(); var _gp=(_hr<12?'Good morning':(_hr<17?'Good afternoon':(_hr<21?'Good evening':'Good night'))); var _ge=document.getElementById('greetPrefix'); if(_ge) _ge.textContent=_gp; }catch(_g){}
1213    document.getElementById('eId').textContent=e.employeeId||'—';
1214    var mgr=d.reportingManager||e.reportingManager||''; var mc=document.getElementById('mgrChip'); if(mc){ if(mgr){ document.getElementById('eMgr').textContent=mgr; mc.style.display='inline-flex'; } else mc.style.display='none'; }
1215    const hb=(d.config||{}).handbookLink; const hbBtn=document.getElementById('hbBtn'); if(hbBtn){ hbBtn.style.display='inline-flex'; if(hb){ hbBtn.href=hb; hbBtn.onclick=null; } else { hbBtn.removeAttribute('href'); hbBtn.onclick=function(ev){ev.preventDefault(); toast('Employee handbook will be available soon.');}; } }
1216    renderCrm(d); renderProgress(d); renderDocs(d); renderDept(d);
1217    renderSalary(d); renderNotices(d); renderComplaints(d.complaints||[]); renderBell(d);
1218    renderPolicies(d); renderWarnings(d); renderResignation(d);
1219    requestAnimationFrame(function(){ emsFoundationLoad(); renderPunchCard(); renderOnboardCard(); renderAssetsCard(); setTimeout(eSyncActive,60); setTimeout(loadDashboard,120); });
1220  }
1221  function svcBreak(doj){
1222    const d=new Date(String(doj||'').replace(' ','T')); if(isNaN(d)) return null;
1223    const now=new Date(); let y=now.getFullYear()-d.getFullYear(), mo=now.getMonth()-d.getMonth(), day=now.getDate()-d.getDate();
1224    if(day<0){ mo--; day+=new Date(now.getFullYear(),now.getMonth(),0).getDate(); }
1225    if(mo<0){ y--; mo+=12; }
1226    return {y:y,mo:mo,d:day};
1227  }
1228  function svcVal(doj, totalDays){
1229    const dd=new Date(String(doj||'').replace(' ','T'));
1230    if(isNaN(dd)) return '<span>'+(totalDays||0)+'</span> days';
1231    const now=new Date();
1232    if(dd.getTime() > now.getTime()){
1233      const inDays=Math.max(0, Math.ceil((dd.getTime()-now.getTime())/86400000));
1234      const dLabel=dd.toLocaleDateString('en-IN',{day:'numeric',month:'short',year:'numeric'});
1235      const sub='Joins '+dLabel+(inDays?(' \u00b7 in '+inDays+' day'+(inDays>1?'s':'')):'');
1236      return '<span>Not started</span><small style="display:block;margin-top:5px;color:var(--faint);font-weight:500">'+sub+'</small>';
1237    }
1238    /* LIVE tenure: whole calendar days since joining — joining day = 0, next day = 1, and so on (independent of time of day). */
1239    const a0=new Date(dd.getFullYear(),dd.getMonth(),dd.getDate());
1240    const b0=new Date(now.getFullYear(),now.getMonth(),now.getDate());
1241    const total=Math.max(0, Math.floor((b0-a0)/86400000));
1242    if(total<=0) return '<span>Today</span><small style="display:block;margin-top:5px;color:var(--faint);font-weight:500">Joined today</small>';
1243    const b=svcBreak(doj);
1244    const parts=[]; if(b&&b.y>0) parts.push(b.y+'y'); if(b&&b.mo>0) parts.push(b.mo+'mo'); parts.push(Math.max(0,(b?b.d:total))+'d');
1245    const main=parts.join(' ');
1246    return '<span>'+main+'</span><small style="display:block;margin-top:5px;color:var(--faint);font-weight:500">'+total+' day'+(total===1?'':'s')+' total</small>';
1247  }
1248  function renderCrm(d){
1249    const e=d.employee||{}, m=d.metrics||{};
1250    const rating=Math.round(numv(e.rating));
1251    const stars=[1,2,3,4,5].map(i=>'<i class="ti ti-star-filled'+(i<=rating?' on':'')+'"></i>').join('');
1252    const drate=Math.round(numv(e.disciplineRating));
1253    const dstars=[1,2,3,4,5].map(i=>'<i class="ti ti-star-filled'+(i<=drate?' on':'')+'"></i>').join('');
1254    const cards=[
1255      ['ti-calendar','Days in Company', svcVal(e.doj, m.daysInCompany)],
1256      ['ti-building','Department', esc(e.department||'—')],
1257      ['ti-briefcase','Designation', esc(e.designation||'—')],
1258      ['ti-star','Performance Rating','<span class="stars">'+stars+'</span>'],
1259      ['ti-shield-check','Discipline Rating','<span class="stars">'+dstars+'</span>']
1260    ];
1261    document.getElementById('crm').innerHTML=cards.map(c=>'<div class="ccard"><div class="cl"><i class="ti '+c[0]+'"></i> '+c[1]+'</div><div class="cv">'+c[2]+'</div></div>').join('');
1262  }
1263  function probationEnd(e){ const d=new Date(String(e.doj||'').replace(' ','T')); if(isNaN(d))return null; const m=parseInt(e.probationMonths||'6',10)||6; d.setMonth(d.getMonth()+m); return d; }
1264  function isConfirmed(e){ if(String(e.status).toLowerCase()==='confirmed')return true; const pe=probationEnd(e); return pe? Date.now()>=pe.getTime() : false; }
1265  function docStats(d){
1266    const uploaded=d.documents||[]; const cfg=d.config||{};
1267    const mand=String(cfg.mandatoryDocs||'').split(',').map(s=>
1267s.trim()).filter(Boolean);
1268    const byType={}; uploaded.forEach(u=>{ if(u.link) byType[String(u.docType).trim().toLowerCase()]=u; });
1269    const done=mand.filter(n=>byType[n.toLowerCase()]).length;
1270    return {mand:mand, done:done, total:mand.length, all: mand.length>0 && done>=mand.length, byType:byType};
1271  }
1272  function renderProgress(d){
1273    const e=d.employee||{}; const ds=docStats(d);
1274    const steps=[
1275      {t:'Offer Accepted & Joined', d:true},
1276      {t:'Documents Submitted ('+ds.done+'/'+ds.total+')', d:ds.all},
1277      {t:'Probation Cleared / Confirmed', d:isConfirmed(e)},
1278      {t:'Profile Complete', d: !!(e.photo && e.pan && e.bankAccount)}
1279    ];
1280    const done=steps.filter(s=>s.d).length, pct=Math.round(done/steps.length*100);
1281    document.getElementById('progFill').style.width=pct+'%';
1282    const all=done===steps.length;
1283    var _ps=document.getElementById('progSub'); if(_ps) _ps.textContent = all ? 'All steps complete' : (done+' of '+steps.length+' steps complete');
1284    document.getElementById('progMsg').innerHTML= all
1285      ? '<span style="color:var(--ok);font-weight:600"><i class="ti ti-circle-check"></i> All set — your profile is complete! 🎉</span>'
1286      : '<span style="color:var(--warn);font-weight:600"><i class="ti ti-alert-circle"></i> Setup incomplete — complete the highlighted steps.</span> &nbsp;'+done+'/'+steps.length;
1287    document.getElementById('progSteps').innerHTML=steps.map(s=>'<div class="pstep'+(s.d?' done':' todo')+'"><span class="pi"><i class="ti '+(s.d?'ti-check':'ti-alert-triangle')+'"></i></span>'+s.t+'</div>').join('');
1288  }
1289  /* ---- Documents ---- */
1290  function renderDocs(d){
1291    const ds=docStats(d); const list=ds.mand.map(name=>{ const u=ds.byType[name.toLowerCase()]; return {name:name, link:u?u.link:''}; });
1292    (d.documents||[]).forEach(u=>{ if(u.link && ds.mand.findIndex(m=>m.toLowerCase()===String(u.docType).trim().toLowerCase())<0) list.push({name:u.docType, link:u.link}); });
1293    window.__docList=list;
1294    if(!list.length){ document.getElementById('docsList').innerHTML='<div class="empty">No document list configured.</div>'; return; }
1295    const warn = ds.all ? '<div class="docok-pro"><i class="ti ti-rosette-discount-check"></i><div><b>All documents submitted</b><span>Your document file is complete and up to date — thank you.</span></div></div>'
1296      : '<div class="docwarn"><i class="ti ti-alert-triangle"></i><div><b>'+(ds.total-ds.done)+' document(s) still pending.</b> Please upload all documents — incomplete files may delay your salary credit.</div></div>';
1297    const body=list.map((x,i)=>{ const done=!!x.link;
1298      return '<div class="line"><span>'+(done?'<i class="ti ti-circle-check" style="color:var(--ok)"></i> ':'<i class="ti ti-circle" style="color:var(--faint)"></i> ')+esc(x.name)+'</span>'+(done?'<a class="dl ghost" href="'+esc(x.link)+'" target="_blank" style="padding:5px 11px"><i class="ti ti-eye"></i> View</a>':'<button class="dl" style="padding:6px 12px" onclick="uploadMyDoc('+i+')"><i class="ti ti-upload"></i> Upload</button>')+'</div>';
1299    }).join('');
1300    document.getElementById('docsList').innerHTML = warn + accItem('ti-files','My Documents', ds.done+' of '+ds.total+' uploaded', body);
1301  }
1302  function uploadMyDoc(i){
1303    const item=(window.__docList||[])[i]; if(!item)return; const docType=item.name;
1304    const inp=document.createElement('input'); inp.type='file'; inp.accept='image/*,application/pdf';
1305    inp.onchange=async()=>{ const f=inp.files[0]; if(!f)return; toast('Uploading '+docType+'…');
1306      let data=await new Promise(r=>{const rd=new FileReader();rd.onload=e=>r(e.target.result);rd.readAsDataURL(f);});
1307      if(f.type.indexOf('image')===0){ data=await new Promise(res=>{const img=new Image();img.onload=()=>{let w=img.width,h=img.height;const mx=1500;if(w>mx||h>mx){const s=Math.min(mx/w,mx/h);w=Math.round(w*s);h=Math.round(h*s);}const c=document.createElement('canvas');c.width=w;c.height=h;c.getContext('2d').drawImage(img,0,0,w,h);res(c.toDataURL('image/jpeg',0.78));};img.onerror=()=>res(data);img.src=data;}); }
1308      const dd=await api({action:'emsUploadDoc', employeeId:currentId, docType:docType, file:data});
1309      if(dd.status==='success'){ toast(docType+' uploaded ✓'); refresh(true); } else toast(dd.message||'Upload failed');
1310    };
1311    inp.click();
1312  }
1313  /* ---- Department ---- */
1314  function ministars(v){ const r=Math.round(numv(v)); return v?('<span class="stars">'+[1,2,3,4,5].map(i=>'<i class="ti ti-star-filled'+(i<=r?' on':'')+'"></i>').join('')+'</span>'):'—'; }
1315  function renderDept(d){
1316    const e=d.employee||{}, cfg=d.config||{};
1317    const sr=salaryRows(e); const hasSal=sr.gross>0;
1318    const monthly = hasSal ? rupee(sr.gross)+' / month' : '—';
1319    const annual = e.ctc ? rupee(e.ctc) : (hasSal ? rupee(sr.gross*12) : 'As per salary structure');
1320    const pm = parseInt(e.probationMonths||'',10); const prob = pm ? (pm+' Month'+(pm>1?'s':'')) : '—';
1321    const row=function(icon,label,val,hi){ return '<div class="dq"><span class="dq-k"><i class="ti '+icon+'"></i>'+label+'</span><span class="dq-v'+(hi?' hi':'')+'">'+(val==null||val===''?'—':val)+'</span></div>'; };
1322    const longrow=function(icon,label,val){ return '<div class="dq-long"><div class="dq-k"><i class="ti '+icon+'"></i>'+label+'</div><div class="dq-longv">'+(val==null||val===''?'—':val)+'</div></div>'; };
1323    const block=function(icon,title,rowsHtml){ return '<div class="dept-block"><div class="dept-bh"><i class="ti '+icon+'"></i>'+title+'</div><div class="dept-brows">'+rowsHtml+'</div></div>'; };
1324    let h='';
1325    h+=block('ti-briefcase','Role &amp; Joining Details',
1326      row('ti-user-star','Designation / Role', esc(e.designation), true)+
1327      row('ti-building','Department', esc(e.department))+
1328      row('ti-user-check','Reporting To', esc(d.reportingManager||e.reportingManager))+
1329      row('ti-map-pin','Work Location', esc(e.workLocation))+
1330      row('ti-calendar-week','Working Days', esc(e.workingDays||cfg.workingDays||'Monday to Saturday'))+
1331      row('ti-clock-hour-9','Office Timing', esc(e.officeTiming||cfg.officeTiming||'9:30 AM to 6:30 PM'))+
1332      row('ti-id-badge-2','Employment Type', esc(e.employmentType||'Full-time'))+
1333      row('ti-hourglass-high','Probation Period', esc(prob))+
1334      row('ti-calendar-event','Joining Date', esc(fmtDate(e.doj)))
1335    );
1336    h+=block('ti-cash-banknote','Compensation',
1337      row('ti-coin-rupee','Monthly Salary', monthly, hasSal)+
1338      row('ti-wallet','Annual CTC', annual, !!e.ctc||hasSal)
1339    );
1340    if(e.assignedWork){ h+=block('ti-clipboard-list','Assigned Work', longrow('ti-checklist','Re
1340sponsibilities', esc(e.assignedWork))); }
1341    const anyRating=numv(e.rating)+numv(e.disciplineRating)+numv(e.professionalism)+numv(e.behaviour)+numv(e.punctuality)+numv(e.teamwork)>0;
1342    if(anyRating||e.conductNote){
1343      let rr='';
1344      if(anyRating){ rr+=row('ti-chart-line','Performance', ministars(e.rating))+
1345        row('ti-shield-check','Discipline', ministars(e.disciplineRating))+
1346        row('ti-briefcase','Professionalism', ministars(e.professionalism))+
1347        row('ti-mood-smile','Behaviour', ministars(e.behaviour))+
1348        row('ti-clock-check','Punctuality', ministars(e.punctuality))+
1349        row('ti-users-group','Teamwork', ministars(e.teamwork)); }
1350      if(e.conductNote) rr+=longrow('ti-note','HR Note', esc(e.conductNote));
1351      h+=block('ti-award','Performance &amp; Conduct', rr);
1352    }
1353    document.getElementById('deptKv').innerHTML=h;
1354  }
1355  async function submitWork(){
1356    const t=document.getElementById('workTask').value.trim(); if(!t){toast('Enter your work update');return}
1357    await api({action:'emsAddWork', employeeId:currentId, name:(BUNDLE.employee||{}).name||'', task:t});
1358    document.getElementById('workTask').value=''; toast('Work update submitted ✓');
1359  }
1360  /* ---- Salary slips (last 6 months) ---- */
1361  function lastMonths(n){ const out=[]; const d=new Date(); for(let i=0;i<n;i++){ const m=new Date(d.getFullYear(), d.getMonth()-i, 1); out.push({key:m.toLocaleString('en-IN',{month:'long',year:'numeric'}), date:m}); } return out; }
1362  var _salYear=0;
1363  function fmtDay(d){ return ('0'+d.getDate()).slice(-2)+' '+d.toLocaleString('en-IN',{month:'short'})+' '+d.getFullYear(); }
1364  function renderSalAnnex(e){
1365    const box=document.getElementById('salAnnex'); if(!box) return;
1366    const s=salaryRows(e); if(s.gross<=0){ box.innerHTML=''; return; }
1367    const rrow=function(n,v){ return '<div class="sa-row"><span>'+esc(n)+'</span><b>'+rupee(v)+'</b></div>'; };
1368    const earn=s.earn.filter(function(r){return r[1]>0;}); const ded=s.ded.filter(function(r){return r[1]>0;});
1369    let words=''; try{ words=inWords(s.net); }catch(_){ words=''; }
1370    let h='<div class="acc"><div class="acc-h" onclick="toggleAcc(this)"><div class="ai"><i class="ti ti-file-invoice"></i></div><div class="at"><b>Salary Annexure</b><span>Net '+rupee(s.net)+'/mo \u00b7 tap for full breakdown</span></div><i class="ti ti-chevron-down chev"></i></div><div class="acc-b"><div class="acc-bi"><div class="sa-grid">';
1371    h+='<div class="sa-col"><div class="sa-ctitle earn">Earnings</div>'+(earn.length?earn.map(function(r){return rrow(r[0],r[1]);}).join(''):'<div class="sa-row"><span>—</span><b>'+rupee(0)+'</b></div>')+'<div class="sa-total earn"><span>Total Earnings (Gross)</span><b>'+rupee(s.gross)+'</b></div></div>';
1372    h+='<div class="sa-col"><div class="sa-ctitle ded">Deductions</div>'+(ded.length?ded.map(function(r){return rrow(r[0],r[1]);}).join(''):'<div class="sa-row"><span>No deductions</span><b>'+rupee(0)+'</b></div>')+'<div class="sa-total ded"><span>Total Deductions</span><b>'+rupee(s.totded)+'</b></div></div>';
1373    h+='</div><div class="sa-net"><span>Net Take-Home (Monthly)</span><b>'+rupee(s.net)+'</b></div>';
1374    h+='<div class="sa-sub">Annual CTC: <b>'+rupee(e.ctc?numv(e.ctc):s.gross*12)+'</b>'+(words?('  &middot;  In words: '+esc(words)+' only.'):'')+'</div></div></div></div>';
1375    box.innerHTML=h;
1376  }
1377  async function renderSalary(d){
1378    const e=d.employee||{}; const has=salaryRows(e).gross>0;
1379    renderSalAnnex(e);
1380    try{ const pd=await api({action:'emsMyPayslips', employeeId:currentId||ssGet('ems_id')}); if(pd&&pd.status==='success'){ window.__myPayslips=pd.rows||[]; window.__myRevisions=pd.revisions||[]; } }catch(_){}
1381    renderRevisions(window.__myRevisions||[]); loadReimbursements();
1382    if(!has){ document.getElementById('slipList').innerHTML='<div class="empty">Salary details not set yet. HR will update them.</div>'; renderMyIssues(d.salaryIssues||[]); return; }
1383    if(!_salYear) _salYear=new Date().getFullYear();
1384    salRenderMonths(); renderMyIssues(d.salaryIssues||[]);
1385  }
1386  function salYear(dir){ _salYear+=dir; salRenderMonths(); }
1387  function fileToDataURL(f){ return new Promise(function(res){ const r=new FileReader(); r.onload=function(e){res(e.target.result);}; r.onerror=function(){res('');}; r.readAsDataURL(f); }); }
1388  function renderRevisions(rows){
1389    const el=document.getElementById('salRevisions'); if(!el) return;
1390    if(!rows||!rows.length){ el.innerHTML=''; return; }
1391    el.innerHTML='<div class="sectitle" style="margin-top:24px"><i class="ti ti-history"></i> Salary Revision History</div>'+rows.map(function(r){
1392      const g=Number(r.basic||0)+Number(r.hra||0)+Number(r.allowance||0);
1393      return '<div class="cmp-item"><div class="ctop"><b>Effective '+esc(fmtDate(r.effective_from)||r.effective_from||'')+'</b><span class="cs">'+rupee(g)+' / mo</span></div>'+(r.reason?'<div style="margin-top:4px;color:var(--muted)">'+esc(r.reason)+'</div>':'')+'</div>';
1394    }).join('');
1395  }
1396  async function loadReimbursements(){
1397    const id=currentId||ssGet('ems_id'); if(!id) return;
1398    const dt=document.getElementById('reimDate'); if(dt&&!dt.value) dt.value=_todayISO();
1399    try{ const d=await api({action:'emsExpenseMine', employeeId:id}); if(d&&d.status==='success') renderReims(d.rows||[]); }catch(_){}
1400  }
1401  async function submitReim(){
1402    const id=currentId||ssGet('ems_id');
1403    const cat=document.getElementById('reimCat').value, amt=document.getElementById('reimAmt').value, date=document.getElementById('reimDate').value, desc=document.getElementById('reimDesc').value.trim();
1404    if(!(Number(amt)>0)){ toast('Enter a valid amount'); return; }
1405    const params={action:'emsExpenseSubmit', employeeId:id, token:ssGet('ems_tok'), category:cat, amount:amt, claimDate:date, description:desc};
1406    const f=document.getElementById('reimFile'); if(f&&f.files&&f.files[0]){ toast('Uploading…'); params.file=await fileToDataURL(f.files[0]); }
1407    try{
1408      const d=await api(params);
1409      if(d&&d.status==='success'){ toast('Claim submitted ✓'); document.getElementById('reimAmt').value=''; document.getElementById('reimDesc').value=''; if(f) f.value=''; loadReimbursements(); }
1410      else toast((d&&d.message)||'Submit failed');
1411    }catch(e){ toast('Network issue — please try again.'); }
1412  }
1413  async function cancelReim(cid){
1414    if(!confirm('Cancel this claim?')) return;
1415    const id=currentId||ssGet('ems_id');
1416    const d=await api({action:'emsExpenseCancel', employeeId:id, id:cid});
1417    if(d&&d.status==='success'){ toast('Cancelled'); loadReimbursements(); } else toast((d&&d.message)||'Failed');
1418  }
1419  function renderReims(rows){
1420    const el=document.getElementById('myReims'); if(!el) return;
1421    if(!rows.length){ el.innerHTML='<div class="accwrap-head">My claims</div><div class="empty">No claims yet.</div>'; return; }
1422    el.innerHTML='<div class="accwrap-head">My claims</div>'+rows.map(function(r){ const s=String(r.status||'pending'); const badge=(s==='paid'||s==='approved')?'<span class="cs resolved">'+(s==='paid'?'Paid':'Approved')+'</span>':(s==='rejected'?'<span class="cs" style="background:rgba(224,83,63,.15);color:#ff9c8b">Rejected</span>':(s==='cancelled'?'<span class="cs">Cancelled</span>':'<span class="cs open">Pending</span>')); const cancel=(s==='pending')?'<button class="dl ghost" style="margin-top:8px;padding:6px 12px" onclick="cancelReim(\''+esc(r.id)+'\')"><i class="ti ti-x"></i> Cancel</button>':'';
1422 const bill=r.bill_link?' \u00b7 <a href="'+esc(r.bill_link)+'" target="_blank" style="color:var(--gold-soft)">bill</a>':''; const dn=r.decision_note?'<div class="creply"><b>Accounts:</b> '+esc(r.decision_note)+'</div>':''; const cat=(r.category||'expense'); return '<div class="cmp-item"><div class="ctop"><b>'+esc(cat.charAt(0).toUpperCase()+cat.slice(1))+' \u00b7 '+rupee(r.amount)+'</b>'+badge+'</div><div style="font-size:12.5px;color:var(--muted)">'+esc(r.claim_date||'')+bill+'</div>'+(r.description?'<div style="margin-top:4px">'+esc(r.description)+'</div>':'')+dn+cancel+'</div>'; }).join('');
1423  }
1424  function salFinalizedSlips(){ return (window.__myPayslips||[]).filter(function(ps){ return String(ps.status)==='finalized'||String(ps.status)==='paid'; }); }
1425  function salQuickBar(){
1426    const mn=['January','February','March','April','May','June','July','August','September','October','November','December'];
1427    const slips=salFinalizedSlips().slice().sort(function(a,b){ return String(b.month).localeCompare(String(a.month)); });
1428    if(!slips.length){ return '<div class="sal-quick"><i class="ti ti-file-download"></i><span class="sal-qlbl">Quick payslip</span><span class="sal-qnote">Available once HR finalizes a month’s payroll.</span></div>'; }
1429    let opts=slips.map(function(ps){ const y=String(ps.month).slice(0,4), mi=parseInt(String(ps.month).slice(5,7),10)-1; return '<option value="'+esc(ps.month)+'">'+mn[mi]+' '+y+'</option>'; }).join('');
1430    return '<div class="sal-quick"><i class="ti ti-file-download"></i><span class="sal-qlbl">Quick payslip</span><select class="inp sm" id="salQM">'+opts+'</select><button class="sal-dl" onclick="salQuickDL()"><i class="ti ti-download"></i> Download</button></div>';
1431  }
1432  function salQuickDL(){
1433    const el=document.getElementById('salQM'); const v=el?el.value:''; if(!v){ toast('No finalized payslip yet'); return; }
1434    genSlip(parseInt(v.slice(0,4),10), parseInt(v.slice(5,7),10)-1);
1435  }
1436  function salRenderMonths(){
1437    const now=new Date(), cy=now.getFullYear();
1438    const doj=new Date(String((BUNDLE.employee||{}).doj||'').replace(' ','T'));
1439    const dojY=isNaN(doj)?2000:doj.getFullYear();
1440    const canPrev=_salYear>dojY, canNext=_salYear<cy;
1441    let h=salQuickBar()+'<div class="sal-yr"><button class="sal-nav"'+(canPrev?'':' disabled')+' onclick="salYear(-1)"><i class="ti ti-chevron-left"></i></button><span class="sal-yrlbl">'+_salYear+'</span><button class="sal-nav"'+(canNext?'':' disabled')+' onclick="salYear(1)"><i class="ti ti-chevron-right"></i></button></div>';
1442    // Premium history: only months whose payroll HR has finalized appear here.
1443    const yearSlips=salFinalizedSlips().filter(function(ps){ return String(ps.month||'').slice(0,4)===String(_salYear); }).sort(function(a,b){ return String(b.month).localeCompare(String(a.month)); });
1444    if(!yearSlips.length){ document.getElementById('slipList').innerHTML=h+'<div class="sal-empty"><i class="ti ti-file-invoice"></i><div class="sal-empty-t">No payslips for '+_salYear+' yet</div><div class="sal-empty-s">Your payslip for a month appears here once HR finalizes that month’s payroll.</div></div>'; return; }
1445    h+='<div class="sal-months">'+yearSlips.map(function(ps){
1446      const mIdx=parseInt(String(ps.month).slice(5,7),10)-1;
1447      const first=new Date(_salYear,mIdx,1), last=new Date(_salYear,mIdx+1,0);
1448      const mName=first.toLocaleString('en-IN',{month:'short'});
1449      const range=fmtDay(first)+' – '+fmtDay(last);
1450      const paid=String(ps.status)==='paid';
1451      const badge='<span class="sal-net-badge '+(paid?'paid':'fin')+'">'+(paid?'Paid':'Ready')+' · '+rupee(ps.net)+'</span>';
1452      return '<div class="sal-card"><div class="sal-mbadge"><b>01</b><span>'+mName+'</span></div><div class="sal-mmain"><b>Payslip</b><span>'+range+'</span>'+badge+'</div><div class="sal-mbtns"><button class="sal-dl" onclick="genSlip('+_salYear+','+mIdx+')"><i class="ti ti-download"></i> Download</button><button class="sal-rep" title="Report issue" onclick="openIssue(\''+mName+' '+_salYear+'\')"><i class="ti ti-alert-triangle"></i></button></div></div>';
1453    }).join('')+'</div>';
1454    document.getElementById('slipList').innerHTML=h;
1455  }
1456  function openIssue(m){ document.getElementById('issueBox').style.display='block'; document.getElementById('issueMonth').textContent=m; document.getElementById('issueText').focus(); document.getElementById('issueBox').dataset.m=m; }
1457  async function submitIssue(){
1458    const m=document.getElementById('issueBox').dataset.m||''; const t=document.getElementById('issueText').value.trim();
1459    if(!t){toast('Describe the issue');return}
1460    try{
1461      const d=await api({action:'emsSalaryIssue', employeeId:currentId, name:(BUNDLE.employee||{}).name||'', month:m, message:t});
1462      if(d&&d.status==='success'){ document.getElementById('issueText').value=''; document.getElementById('issueBox').style.display='none'; toast('Issue submitted ✓'); refresh(true); }
1463      else toast((d&&d.message)||'Could not submit — please try again');
1464    }catch(e){ toast('Network issue — please try again.'); }
1465  }
1466  function renderMyIssues(list){
1467    if(!list||!list.length){document.getElementById('myIssues').innerHTML='';return}
1468    document.getElementById('myIssues').innerHTML='<div class="accwrap-head">Your reported issues</div>'+list.slice().reverse().map(c=>{
1469      const r=String(c.status).toLowerCase()==='resolved'||String(c.status).toLowerCase()==='closed';
1470      const badge=r?'<span class="cs resolved">Resolved</span>':'<span class="cs open">Open</span>';
1471      const body='<div style="margin-top:4px">'+esc(c.message)+'</div>'+(c.reply?'<div class="creply"><b>HR:</b> '+esc(c.reply)+'</div>':'');
1472      return accItem('ti-cash', esc(c.month||'Salary issue')+' &nbsp;'+badge, esc(fmtDT(c.time)), body);
1473    }).join('');
1474  }
1475  /* ---- Notices ---- */
1476  function _noticeSeen(){ try{ return JSON.parse(localStorage.getItem('ems_notice_seen')||'[]'); }catch(_){ return []; } }
1477  function _noticeMarkSeen(){ try{ var s=_noticeSeen(); (window._noticeIds||[]).forEach(function(id){ if(s.indexOf(id)<0)s.push(id); }); if(s.length>300)s=s.slice(-300); localStorage.setItem('ems_notice_seen',JSON.stringify(s)); }catch(_){} }
1478  function _noticeId(n){ return String((n&&n.id)||(((n&&n.subject)||'')+'|'+((n&&n.time)||''))); }
1479  function renderNotices(d){
1480    const list=d.notices||[];
1481    if(!list.length){document.getElementById('noticeList').innerHTML='<div class="empty">No notices yet.</div>';return}
1482    var _seen=_noticeSeen(); var _rev=list.slice().reverse(); window._noticeIds=_rev.map(_noticeId);
1483    document.getElementById('noticeList').innerHTML=_rev.map(n=>{
1484      const urgent=String(n.type).toLowerCase()==='urgent';
1485      const isNew=_seen.indexOf(_noticeId(n))<0;
1486      const btn=n.pdfLink?'<a class="dl" href="'+esc(n.pdfLink)+'" target="_blank"><i class="ti ti-download"></i> Download PDF</a>':'<button class="dl ghost" onclick=\'genNotice('+JSON.stringify(JSON.stringify({s:n.subject,m:n.message,t:n.time}))+')\'><i class="ti ti-download"></i> Download PDF</button>';
1487      const cfg=BUNDLE.config||{}; const stK='stamp'+(n.stampType||''); const stamp=(n.stampType&&cfg[stK])?'<div style="margin-top:12px"><img src="'+esc(cfg[stK])+'" style="max-height:72px"><div style="font-size:11px;color:var(--faint)">'+esc(n.stampType)+' — Authorised</div></div>':'';
1488      const body='<div class="ptext">'+esc(n.message||'')+'</div>'+stamp+'<div style="margin-top:10px">'+btn+'</div>';
1489      return accItem('ti-speakerphone', esc(n.subject||'Notice')+(isNew?'<span class="ntag-new">NEW</span>':'')+(urgent?'<span class="btag">URGENT</span>':''), esc(fmtDT(n.time)), body, (urgent||isNew));
1490    }).join('');
1491  }
1492  /* ---- Complaints ---- */
1493  function renderComplaints(list){
1494    const el=document.getElementById('myComplaints');
1495    if(!list.length){el.innerHTML='';return}
1496    const shown=cmpAll?list:list.slice(0,3);
1497    el.innerHTML='<div class="accwrap-head">Your Requests</div>'+shown.map(c=>{
1498      const r=String(c.status).toLowerCase()==='resolved'||String(c.status).toLowerCase()==='closed';
1499      const badge=r?'<span class="cs resolved">Resolved</span>':'<span class="cs open">Open</span>';
1500      const body='<div style="margin-top:4px">'+esc(c.message)+'</div>'+(c.reply?'<div class="creply"><b>HR:</b> '+esc(c.reply)+'</div>':'');
1501      return accItem('ti-headset', 'Request &nbsp;'+badge, esc(fmtDT(c.time)), body);
1502    }).join('')+(list.length>3?'<button class="reqmore" onclick="cmpAll=!cmpAll;renderComplaints(window.__c||[])">'+(cmpAll?'Show less':'Show all ('+list.length+')')+'</button>':'');
1503    window.__c=list;
1504  }
1505  async function submitComplaint(){
1506    const t=document.getElementById('hlpText').value.trim(); if(!t){toast('Describe your request');return}
1507    try{
1508      const d=await api({action:'emsAddComplaint', employeeId:currentId, name:(BUNDLE.employee||{}).name||'', message:t});
1509      if(d&&d.status==='success'){ document.getElementById('hlpText').value=''; toast('Request submitted ✓'); refresh(true); }
1510      else toast((d&&d.message)||'Could not submit — please try again');
1511    }catch(e){ toast('Network issue — please try again.'); }
1512  }
1513  var _rsEl=document.getElementById('rateStars'); if(_rsEl) _rsEl.addEventListener('click',e=>{ const v=e.target.dataset.v; if(!v)return; rateVal=+v;
1513 [...document.querySelectorAll('#rateStars i')].forEach(i=>i.classList.toggle('on', +i.dataset.v<=rateVal)); });
1514  async function submitFeedback(){
1515    if(!rateVal){toast('Tap stars to rate');return}
1516    try{
1517      const d=await api({action:'emsAddFeedback', employeeId:currentId, name:(BUNDLE.employee||{}).name||'', rating:rateVal, message:document.getElementById('fbText').value.trim()});
1518      if(!(d&&d.status==='success')){ toast((d&&d.message)||'Could not submit — please try again'); return; }
1519    }catch(e){ toast('Network issue — please try again.'); return; }
1520    document.getElementById('fbText').value=''; toast('Thanks for your feedback ✓');
1521  }
1522  /* ---- Bell ---- */
1523  function renderBell(d){
1524    d=d||BUNDLE||{};
1525    const notices=(d.notices||[]).map(n=>({id:'n'+n.id, subject:n.subject||'Notice', message:n.message||'', time:n.time, type:(String(n.type).toLowerCase()==='urgent'?'urgent':'')}));
1526    const compReplies=(d.complaints||[]).filter(c=>c.reply).map(c=>({id:'c'+(c.id||c.time)+String(c.reply).length, subject:'HR replied to your request', message:c.reply, time:c.replyTime||c.time, type:''}));
1527    const salReplies=(d.salaryIssues||[]).filter(c=>c.reply).map(c=>({id:'si'+(c.id||c.time)+String(c.reply).length, subject:'HR replied · '+(c.month||'Salary issue'), message:c.reply, time:c.replyTime||c.time, type:''}));
1528    const resigns=(d.resignations||[]).filter(r=>String(r.status||'').toLowerCase()!=='pending' || r.settlement).map(r=>({id:'r'+(r.id||r.time)+String(r.status||'')+String(r.settlement||'').length, subject:'Resignation update: '+(r.status||'Updated'), message:(r.settlement?r.settlement:('Your resignation status is now “'+(r.status||'updated')+'”.')), time:r.updatedAt||r.time, type:''}));
1529    const unified=(window.__unifiedNotifs||[]).map(n=>({id:'u'+n.id, subject:n.title||'Notification', message:n.message||'', time:n.time, type:(String(n.type).toLowerCase()==='urgent'?'urgent':'')}));
1530    let list=notices.concat(compReplies, salReplies, resigns, unified);
1531    list.sort((a,b)=> new Date(String(b.time||'').replace(' ','T')) - new Date(String(a.time||'').replace(' ','T')) );
1532    const unreadKey='ems_read_'+currentId; const read=lsGet(unreadKey).split(',');
1533    const unread=list.filter(n=>read.indexOf(String(n.id))<0).length;
1534    const dot=document.getElementById('bdot'); if(unread>0){dot.style.display='grid';dot.textContent=unread}else dot.style.display='none';
1535    window.__bellIds=list.map(n=>String(n.id));
1536    document.getElementById('bpanel').innerHTML = list.length? list.map(n=>'<div class="bitem'+(n.type==='urgent'?' urgent':'')+'"><b>'+esc(n.subject)+(n.type==='urgent'?'<span class="btag">URGENT</span>':'')+'</b>'+esc(n.message)+'<div class="bt">'+esc(fmtDT(n.time))+'</div></div>').join('') : '<div class="empty">No notifications</div>';
1537  }
1538  function toggleBell(){
1539    const p=document.getElementById('bpanel'); p.style.display=p.style.display==='block'?'none':'block';
1540    if(p.style.display==='block'){ const ids=(window.__bellIds||[]); lsSet('ems_read_'+currentId, ids.join(',')); document.getElementById('bdot').style.display='none'; }
1541  }
1542  document.addEventListener('click',e=>{ if(!e.target.closest('#bpanel')&&!e.target.closest('#bellBtn')) document.getElementById('bpanel').style.display='none'; });
1543
1544  /* ---- Phase 0 foundation: role-aware UI + unified notifications + my approvals ---- */
1545  async function emsFoundationLoad(){
1546    const id=currentId||ssGet('ems_id'); if(!id) return;
1547    try{ const pm=await api({action:'emsMyPerms', employeeId:id});
1548      if(pm&&pm.status==='success'){ window.__role=pm.role||'Employee'; window.__mods=pm.modules||[]; applyRoleVisibility();
1549        const rt=document.getElementById('eRoleTxt'); if(rt){ rt.textContent=window.__role; const rb=document.getElementById('eRoleBadge'); if(rb) rb.style.display='inline-flex'; }
1550      }
1551    }catch(e){}
1552    try{ const nl=await api({action:'emsNotifyList', employeeId:id});
1553      if(nl&&nl.status==='success'){ window.__unifiedNotifs=nl.rows||[]; if(BUNDLE) renderBell(BUNDLE); try{ if(typeof dwUpdates==='function') dwUpdates(window.__unifiedNotifs); }catch(_u){} }
1554    }catch(e){}
1555    try{ const ma=await api({action:'emsApprMine', employeeId:id});
1556      if(ma&&ma.status==='success'){ renderMyApprovals(ma.rows||[]); }
1557    }catch(e){}
1558  }
1559  function applyRoleVisibility(){
1560    const mods=window.__mods; if(!mods||!mods.length) return;
1561    let firstVisible=null, activeHidden=false;
1562    document.querySelectorAll('.tab[data-mod]').forEach(function(t){
1563      const ok=mods.indexOf(t.getAttribute('data-mod'))>=0;
1564      t.style.display=ok?'':'none';
1565      if(ok&&!firstVisible) firstVisible=t;
1566      if(!ok&&t.classList.contains('active')) activeHidden=true;
1567    });
1568    document.querySelectorAll('#eSide .snav-item[data-mod]').forEach(function(n){ n.style.display=mods.indexOf(n.getAttribute('data-mod'))>=0?'':'none'; });
1569    document.querySelectorAll('#eSide .snav-grp').forEach(function(g){ var any=false; g.querySelectorAll('.snav-item').forEach(function(n){ if(n.style.display!=='none') any=true; }); g.style.display=any?'':'none'; });
1570    if(activeHidden&&firstVisible) firstVisible.click();
1571    eSyncActive();
1572  }
1573  function renderMyApprovals(rows){
1574    const el=document.getElementById('myApprovals'); if(!el) return;
1575    if(!rows||!rows.length){ el.innerHTML=''; return; }
1576    el.innerHTML='<div class="sectitle" style="margin-top:24px"><i class="ti ti-checklist"></i> My Requests</div>'+rows.map(function(r){
1577      const s=String(r.status||'pending').toLowerCase();
1578      const badge=s==='approved'?'<span class="cs resolved">Approved</span>':(s==='rejected'?'<span class="cs" style="background:rgba(224,83,63,.15);color:#ff9c8b">Rejected</span>':'<span class="cs open">Pending</span>');
1579      const et=String(r.entity_type||'request'); const etT=et.charAt(0).toUpperCase()+et.slice(1);
1580      const dn=r.decision_note?'<div class="creply"><b>HR:</b> '+esc(r.decision_note)+'</div>':'';
1581      return '<div class="cmp-item"><div class="ctop"><b>'+esc(etT)+'</b>'+badge+'</div>'+(r.title?'<div style="margin-top:4px">'+esc(r.title)+'</div>':'')+'<div style="font-size:11px;color:var(--faint);margin-top:4px">'+esc(fmtDT(r.time))+'</div>'+dn+'</div>';
1582    }).join('');
1583  }
1584
1585  /* ---- Phase 3: Goals / Tasks / Performance / Rewards (employee) ---- */
1586  async function loadGoals(){
1587    const id=currentId||ssGet('ems_id'); if(!id) return;
1588    const d=await api({action:'emsMyGoals', employeeId:id});
1589    const el=document.getElementById('goalsWrap'); if(!el) return;
1590    const rows=(d&&d.rows)||[];
1591    if(!rows.length){ el.innerHTML='<div class="empty">No goals assigned yet.</div>'; return; }
1592    const groups={company:[],department:[],personal:[]};
1593    rows.forEach(function(g){ (groups[g.scope]||groups.company).push(g); });
1594    let h='';
1595    [['personal','My Goals'],['department','Department Goals'],['company','Company Goals']].forEach(function(gp){
1596      const list=groups[gp[0]]; if(!list||!list.length) return;
1597      h+='<div class="accwrap-head" style="margin-top:14px">'+gp[1]+'</div>'+list.map(function(g){
1598        const pct=Math.max(0,Math.min(100,Number(g.progress)||0)); const editable=(gp[0]==='personal');
1599        const edit=editable?'<div class="goal-edit"><input type="range" min="0" max="100" value="'+pct+'" id="gs_'+esc(g.id)+'" oninput="document.getElementById(\'gv_'+esc(g.id)+'\').textContent=this.value+\'%\'"><span id="gv_'+esc(g.id)+'">'+pct+'%</span><button class="dl ghost" style="padding:6px 12px" onclick="updateGoalProgress(\''+esc(g.id)+'\')">Save</button></div>':('<div style="font-size:12px;color:var(--muted);margin-top:6px">'+pct+'%'+(g.due_date?(' · due '+esc(g.due_date)):'')+'</div>');
1600        return '<div class="goal-card"><div class="ctop"><b>'+esc(g.title||'')+'</b><span class="cs '+(String(g.status)==='done'?'resolved':'open')+'">'+esc(String(g.status||'open').replace('_',' '))+'</span></div>'+(g.description?'<div style="color:var(--muted);font-size:13px;margin:4px 0">'+esc(g.description)+'</div>':'')+(g.target?'<div style="font-size:12px;color:var(--faint)">Target: '+esc(g.target)+'</div>':'')+'<div class="goal-bar"><span style="width:'+pct+'%"></span></div>'+edit+'</div>';
1601      }).join('');
1602    });
1603    el.innerHTML=h;
1604  }
1605  /* ---- Org Chart ---- */
1606  async function loadOrgChart(){
1607    const id=currentId||ssGet('ems_id'); if(!id) return;
1608    if(!_dir.length){ const d=await api({action:'emsDirectory', employeeId:id}); _dir=(d&&d.rows)||[]; }
1609    renderOrgChart();
1610  }
1611  function renderOrgChart(){
1612    const el=document.getElementById('orgWrap'); if(!el) return;
1613    const emps=_dir||[]; if(!emps.length){ el.innerHTML='<div class="empty">No data.</div>'; return; }
1614    const byName={}; emps.forEach(function(e){ byName[String(e.name||'').trim().toLowerCase()]=e; });
1615    const kids={}, roots=[];
1616    emps.forEach(function(e){ const m=String(e.reportingManager||'').trim().toLowerCase(); if(m && byName[m] && byName[m]!==e){ (kids[m]=kids[m]||[]).push(e); } else roots.push(e); });
1617    const seen={};
1618    function node(e){
1619      const nm=String(e.name||'').trim().toLowerCase(); if(seen[nm]) return ''; seen[nm]=1;
1620      const ini=(String(e.name||'?').trim()[0]||'?').toUpperCase(); const reports=kids[nm]||[];
1621      let h='<div class="org-node"><div class="org-card"><div class="org-av">'+esc(ini)+'</div><div class="org-info"><b>'+esc(e.name||'')+'</b><span>'+esc(e.designation||'\u2014')+(e.department?(' \u00b7 '+esc(e.department)):'')+'</span></div>'+(reports.length?'<span class="org-count">'+reports.length+'</span>':'')+'</div>';
1622      if(reports.length){ h+='<div class="org-children">'+reports.map(node).join('')+'</div>'; }
1623      return h+'</div>';
1624    }
1625    roots.sort(function(a,b){ return (kids[String(b.name||'').trim().toLowerCase()]||[]).length-(kids[String(a.name||'').trim().toLowerCase()]||[]).length; });
1626    el.innerHTML=roots.map(node).join('') || '<div class="empty">No reporting structure set.</div>';
1627  }
1628  /* ---- Company Timeline ---- */
1629  async function loadTimeline(){
1630    const id=currentId||ssGet('ems_id'); if(!id) return;
1631    const d=await api({action:'emsTimeline', employeeId:id}); const rows=(d&&d.rows)||[];
1632    const el=document.getElementById('tlWrap'); if(!el) return;
1633    if(!rows.length){ el.innerHTML='<div class="empty">No milestones yet.</div>'; return; }
1634    const ico={milestone:'ti-flag',achievement:'ti-trophy',announcement:'ti-speakerphone',celebration:'ti-confetti',event:'ti-calendar-event',other:'ti-point'};
1635    const col={milestone:'#e8c977',achievement:'#7fe0a8',announcement:'#8fb6f0',celebration:'#c6a3ff',event:'#f3d27a',other:'#a9b6c9'};
1636    el.innerHTML='<div class="tl">'+rows.map(function(t){ const c=String(t.category||'milestone'); const pin=(t.pinned===true||String(t.pinned)==='true'); const dt=fmtDate(t.event_date)||t.event_date||''; return '<div class="tl-item"><div class="tl-dot" style="background:'+(col[c]||'#a9b6c9')+'"><i class="ti '+(ico[c]||'ti-point')+'"></i></div><div class="tl-body"><div class="tl-top"><b>'+esc(t.title||'')+'</b>'+(pin?' <span class="tl-pin"><i class="ti ti-pin"></i></span>':'')+'</div><div class="tl-date">'+esc(dt)+' · '+esc(c.charAt(0).toUpperCase()+c.slice(1))+'</div>'+(t.description?'<div class="tl-desc">'+esc(t.description)+'</div>':'')+'</div></div>'; }).join('')+'</div>';
1637  }
1638  /* ---- Knowledge Base ---- */
1639  var _kb=[], _kbCat='all';
1640  async function loadKB(){
1641    const id=currentId||ssGet('ems_id'); if(!id) return;
1642    const d=await api({action:'emsKB', employeeId:id}); _kb=(d&&d.rows)||[]; _kbCat='all'; renderKBCats(); renderKB();
1643  }
1644  function renderKBCats(){
1645    const el=document.getElementById('kbCats'); if(!el) return;
1646    const cats=['all'].concat(Array.from(new Set(_kb.map(function(k){return String(k.category||'general');}))).sort());
1647    el.innerHTML=cats.map(function(c){ return '<button class="kb-cat'+(c===_kbCat?' on':'')+'" onclick="kbPick(\''+c+'\')">'+esc(c==='all'?'All':(c.charAt(0).toUpperCase()+c.slice(1)))+'</button>'; }).join('');
1648  }
1649  function kbPick(c){ _kbCat=c; renderKBCats(); renderKB(); }
1650  function renderKB(){
1651    const el=document.getElementById('kbList'); if(!el) return;
1652    const ql=((document.getElementById('kbSearch')||{}).value||'').trim().toLowerCase();
1653    let rows=_kb.filter(function(k){ return _kbCat==='all'||String(k.category)===_kbCat; });
1654    if(ql) rows=rows.filter(function(k){ return (String(k.title)+' '+String(k.content)+' '+String(k.tags)).toLowerCase().indexOf(ql)>=0; });
1655    if(!rows.length){ el.innerHTML='<div class="empty">No articles found.</div>'; return; }
1656    el.innerHTML=rows.map(function(k){ const pin=(k.pinned===true||String(k.pinned)==='true'); return '<div class="acc kb-acc"><div class="acc-h" onclick="toggleAcc(this)"><div class="ai"><i class="ti ti-help-circle"></i></div><div class="at"><b>'+esc(k.title||'')+(pin?' <i class="ti ti-pin" style="color:var(--gold-soft);font-size:12px"></i>':'')+'</b><span>'+esc((k.category||'general').charAt(0).toUpperCase()+(k.category||'general').slice(1))+'</span></div><i class="ti ti-chevron-down chev"></i></div><div class="acc-b"><div class="acc-bi"><div class="kb-content">'+esc(k.content||'').replace(/\n/g,'<br>')+'</div>'+(k.tags?'<div class="kb-tags">#'+esc(String(k.tags).split(',').map(function(x){return x.trim();}
1656).filter(Boolean).join(' #'))+'</div>':'')+'</div></div></div>'; }).join('');
1657  }
1658  /* ---- Meetings & Calendar ---- */
1659  var _mtg=[], _mtgView='upcoming';
1660  async function loadMeetings(){
1661    const id=currentId||ssGet('ems_id'); if(!id) return;
1662    const d=await api({action:'emsMyMeetings', employeeId:id}); _mtg=(d&&d.rows)||[]; renderMeetings();
1663    if(!window._mtgTimer){ window._mtgTimer=setInterval(mtgTick,1000); } mtgTick();
1664  }
1665  function mtgTargetTs(m){ const d=String(m.meeting_date||''); if(!d) return 0; let t=String(m.start_time||'00:00'); if(t.length<5) t='00:00'; const v=new Date(d+'T'+t.slice(0,5)+':00').getTime(); return isNaN(v)?0:v; }
1666  function mtgTick(){
1667    const mp=document.getElementById('meetings'); if(!mp||!mp.classList.contains('show')) return;
1668    const now=Date.now();
1669    document.querySelectorAll('.mtg-cd').forEach(function(el){
1670      const ts=Number(el.getAttribute('data-ts'))||0; let diff=ts-now;
1671      if(!ts){ el.innerHTML=''; return; }
1672      if(diff<=0){ el.innerHTML='<span class="mtg-live"><span class="mtg-livedot"></span> Happening now</span>'; return; }
1673      const dd=Math.floor(diff/86400000); diff-=dd*86400000;
1674      const hh=Math.floor(diff/3600000); diff-=hh*3600000;
1675      const mm=Math.floor(diff/60000); diff-=mm*60000;
1676      const ss=Math.floor(diff/1000); const pad=function(n){return String(n).padStart(2,'0');};
1677      el.innerHTML='<span class="cd-lbl">Starts in</span><div class="cd-b"><b>'+dd+'</b><span>days</span></div><div class="cd-s">:</div><div class="cd-b"><b>'+pad(hh)+'</b><span>hrs</span></div><div class="cd-s">:</div><div class="cd-b"><b>'+pad(mm)+'</b><span>min</span></div><div class="cd-s">:</div><div class="cd-b"><b>'+pad(ss)+'</b><span>sec</span></div>';
1678    });
1679  }
1680  /* ---- Dynamic Forms ---- */
1681  var _forms=[];
1682  async function loadForms(){
1683    const id=currentId||ssGet('ems_id'); if(!id) return;
1684    const d=await api({action:'emsMyForms', employeeId:id}); _forms=(d&&d.rows)||[]; renderForms();
1685  }
1686  function renderForms(){
1687    const el=document.getElementById('formsList'); if(!el) return;
1688    if(!_forms.length){ el.innerHTML='<div class="empty">No forms to fill right now.</div>'; return; }
1689    el.innerHTML=_forms.map(function(f,fi){
1690      if(f.responded) return '<div class="fm-card done"><div class="fm-h"><b>'+esc(f.title||'')+'</b><span class="fm-done"><i class="ti ti-circle-check-filled"></i> Submitted</span></div>'+(f.description?'<p class="fm-desc">'+esc(f.description)+'</p>':'')+'</div>';
1691      var body=(f.fields||[]).map(function(fld,i){ return '<div class="fm-field"><label class="fm-lbl">'+esc(fld.label||'')+(fld.required?' <span style="color:#ff9c8b">*</span>':'')+'</label>'+fmFieldInput(fld,fi,i)+'</div>'; }).join('');
1692      return '<div class="fm-card"><div class="fm-h"><b>'+esc(f.title||'')+'</b></div>'+(f.description?'<p class="fm-desc">'+esc(f.description)+'</p>':'')+body+'<button class="btn fm-submit" onclick="submitForm('+fi+')"><i class="ti ti-send"></i> Submit</button></div>';
1693    }).join('');
1694  }
1695  function fmFieldInput(fld,fi,i){
1696    var n='fm_'+fi+'_'+i; var t=fld.type||'text';
1697    if(t==='textarea') return '<textarea class="inp" id="'+n+'" rows="3"></textarea>';
1698    if(t==='number') return '<input class="inp" type="number" id="'+n+'">';
1699    if(t==='date') return '<input class="inp" type="date" id="'+n+'">';
1700    if(t==='select'){ return '<select class="inp" id="'+n+'"><option value="">— Select —</option>'+(fld.options||[]).map(function(o){return '<option>'+esc(o)+'</option>';}).join('')+'</select>'; }
1701    if(t==='radio'){ return '<div class="fm-opts" id="'+n+'">'+(fld.options||[]).map(function(o,oi){return '<label class="fm-opt"><input type="radio" name="'+n+'" value="'+esc(o)+'"> '+esc(o)+'</label>';}).join('')+'</div>'; }
1702    if(t==='checkbox'){ return '<div class="fm-opts" id="'+n+'">'+(fld.options||[]).map(function(o){return '<label class="fm-opt"><input type="checkbox" value="'+esc(o)+'"> '+esc(o)+'</label>';}).join('')+'</div>'; }
1703    return '<input class="inp" id="'+n+'">';
1704  }
1705  async function submitForm(fi){
1706    const f=_forms[fi]; if(!f) return; const answers=[];
1707    for(var i=0;i<(f.fields||[]).length;i++){ var fld=f.fields[i]; var n='fm_'+fi+'_'+i; var t=fld.type||'text'; var val;
1708      if(t==='radio'){ var c=document.querySelector('input[name="'+n+'"]:checked'); val=c?c.value:''; }
1709      else if(t==='checkbox'){ val=Array.prototype.map.call(document.querySelectorAll('#'+n+' input:checked'),function(x){return x.value;}); }
1710      else { var el=document.getElementById(n); val=el?el.value:''; }
1711      if(fld.required && (val===''||val==null||(Array.isArray(val)&&!val.length))){ toast('Please fill: '+fld.label); return; }
1712      answers.push(val);
1713    }
1714    const id=currentId||ssGet('ems_id');
1715    const d=await api({action:'emsFormSubmit', employeeId:id, formId:f.id, answers:JSON.stringify(answers)});
1716    if(d&&d.status==='success'){ toast('Submitted ✓'); loadForms(); } else toast((d&&d.message)||'Failed');
1717  }
1718  /* ---- Messaging (employee <-> HR) ---- */
1719  async function loadMessages(){
1720    const id=currentId||ssGet('ems_id'); if(!id) return;
1721    const d=await api({action:'emsMyMessages', employeeId:id}); const rows=(d&&d.rows)||[];
1722    const el=document.getElementById('msgMsgs'); if(!el) return;
1723    if(!rows.length){ el.innerHTML='<div class="empty">No messages yet. Say hello to HR 👋</div>'; }
1724    else {
1725      var groups={}, order=[]; rows.forEach(function(m){ var dt=String(m.time||'').slice(0,10); if(!groups[dt]){groups[dt]=[];order.push(dt);} groups[dt].push(m); });
1726      var todayStr=_todayISO();
1727      el.innerHTML=order.map(function(dt,gi){
1728        var last=(gi===order.length-1);
1729        var label=(dt===todayStr)?'Today':(fmtDate(dt)||dt);
1730        var msgs=groups[dt].map(function(m){ var mine=String(m.sender)==='employee'; return '<div class="msg-bub '+(mine?'me':'them')+'">'+(!mine?'<span class="msg-from">'+esc(m.sender_name||'HR')+'</span>':'')+'<div class="msg-b">'+esc(m.body||'')+'</div><div class="msg-t">'+esc(String(m.time||'').slice(11,16))+'</div></div>'; }).join('');
1731        return '<div class="msg-day'+(last?' open':'')+'"><div class="msg-day-h" onclick="msgDayToggle(this)"><i class="ti ti-chevron-down msg-day-chev"></i> '+esc(label)+' <span class="msg-day-n">'+groups[dt].length+'</span></div><div class="msg-day-b"><div class="msg-day-bi">'+msgs+'</div></div></div>';
1732      }).join('');
1733      el.scrollTop=el.scrollHeight;
1734    }
1735    if(!window._msgTimer){ window._msgTimer=setInterval(function(){ var pn=document.getElementById('messages'); if(pn&&pn.classList.contains('show')) loadMessages(); },15000); }
1736  }
1737  async function sendMsg(){
1738    const el=document.getElementById('msgInput'); const t=el?el.value:''; if(!t||!t.trim()) return;
1739    const id=currentId||ssGet('ems_id'); if(el) el.value='';
1740    const d=await api({action:'emsSendMessage', employeeId:id, body:t});
1741    if(d&&d.status==='success'){ loadMessages(); } else { toast('Failed'); if(el) el.value=t; }
1742  }
1743  function msgDayToggle(h){ h.parentElement.classList.toggle('open'); }
1744  /* ================= DASHBOARD WORKSPACE ================= */
1745  function dashGo(t){ var b=document.querySelector('.tab[data-tab="'+t+'"]'); if(b) b.click(); }
1746  function dwEmpty(msg,icon){ return '<div class="dw-empty"><i class="ti '+(icon||'ti-checkbox')+'"></i><span>'+esc(msg)+'</span></div>'; }
1747  function _todayISO(){ try{ return new Date().toLocaleDateString('en-CA',{timeZone:'Asia/Kolkata'}); }catch(_){ return new Date().toISOString().slice(0,10); } }
1748  async function loadDashboard(){
1749    const id=currentId||ssGet('ems_id'); if(!id) return;
1750    try{ var e=(typeof BUNDLE!=='undefined'&&BUNDLE&&BUNDLE.employee)||{}; var fs=['name','email','phone','department','designation','doj','dob','address','reportingManager']; var fl=fs.filter(function(f){return e[f]&&String(e[f]).trim();}).length; var pe=document.getElementById('eProfilePct'); if(pe)pe.textContent=Math.round(fl/fs.length*100)+'%'; }catch(_p){}
1751    // Each widget renders from its own result — one failing endpoint never blanks the whole dashboard.
1752    var rr=await Promise.allSettled([
1753      api({action:'emsMyTasks',employeeId:id}),
1754      api({action:'emsMyMeetings',employeeId:id}),
1755      api({action:'emsLeaveInit',employeeId:id}),
1756      api({action:'emsMyReviews',employeeId:id})
1757    ]);
1758    var _v=function(x){ return (x&&x.status==='fulfilled'&&x.value)?x.value:null; };
1759    var r0=_v(rr[0]),r1=_v(rr[1]),r2=_v(rr[2]),r3=_v(rr[3]);
1760    try{ dwTasks((r0&&r0.rows)||[]); }catch(_a){}
1761    try{ dwUpcoming((r1&&r1.rows)||[]); }catch(_b){}
1762    try{ dwLeave(r2||{}); }catch(_c){}
1763    try{ dwPerf((r3&&r3.rows)||[]); }catch(_e){}
1764    try{ dwUpdates(window.__unifiedNotifs||[]); }catch(_f){}
1765  }
1766  function dwTasks(rows){ var el=document.getElementById('dwTasks'); if(!el)return; var pend=(rows||[]).filter(function(t){return String(t.status)!=='done';}).slice(0,5); if(!pend.length){ el.innerHTML=dwEmpty('No pending tasks \u2014 you\u2019re all caught up!','ti-checks'); return; } var pc={high:'#ff9c8b',med:'var(--gold-soft)',low:'var(--faint)'}; el.innerHTML=pend.map(function(t){ var pr=String(t.priority||'low'); var st=String(t.status||'todo').replace('_',' '); return '<div class="dw-task"><span class="dw-pri" style="background:'+(pc[pr]||pc.low)+'"></span><div class="dw-task-b"><b>'+esc(t.title||'Untitled task')+'</b><span class="dw-task-m">'+esc(st)+(pr?' \u00b7 '+esc(pr)+' priority':'')+'</span></div><i class="ti ti-chevron-right dw-task-go"></i></div>'; }).join(''); }
1767  function dwUpcoming(rows){ var el=document.getElementById('dwUpcoming'); if(!el)return; var base=new Date(new Date().toDateString()); var up=(rows||[]).filter(function(m){ var d=new Date(String(m.meeting_date||'')+'T00:00:00'); return d>=base; }).sort(function(a,b){return String(a.meeting_date+((a.start_time)||'')).localeCompare(String(b.meeting_date+((b.start_time)||'')));}).slice(0,4); if(!up.length){ el.innerHTML=dwEmpty('No upcoming meetings or events.','ti-calendar'); return; } var td=_todayISO(); el.innerHTML=up.map(function(m){ var dt=String(m.meeting_date||''); var lbl=(dt===td)?'Today':(fmtDate(dt)||dt); return '<div class="dw-up"><div class="dw-up-d"><b>'+esc(lbl)+'</b><span>'+esc(String(m.start_time||'').slice(0,5)||'\u2014')+'</span></div><div class="dw-up-b"><b>'+esc(m.title||'Event')+'</b>'+(m.location?'<span><i class="ti ti-map-pin"></i> '+esc(m.location)+'</span>':(m.link?'<span><i class="ti ti-video"></i> Online</span>':''))+'</div></div>'; }).join(''); }
1768  function dwUpdates(rows){ var el=document.getElementById('dwUpdates'); if(!el)return; var today=_todayISO(); var seen={}, list=[]; (rows||[]).forEach(function(n){ var key=(String(n.title||'')+'|'+String(n.message||'')).toLowerCase().slice(0,120); if(seen[key])return; seen[key]=1; list.push(n); }); var r=list.slice(0,4); if(!r.length){ el.innerHTML=dwEmpty('No new updates right now.','ti-bell'); return; } el.innerHTML=r.map(function(n){ var ic=(n.type==='success'?'ti-circle-check':(n.type==='warn'?'ti-alert-triangle':'ti-info-circle')); var isNew=String(n.time||'').slice(0,10)===today; var dot=isNew?'<span class="dw-new" title="New today"></span>':''; return '<div class="dw-note"><div class="dw-note-i '+(n.type||'info')+'"><i class="ti '+ic+'"></i></div><div class="dw-note-b"><b>
1768'+esc(n.title||'Update')+dot+'</b>'+(n.message?'<span>'+esc(String(n.message).slice(0,90))+'</span>':'')+'<em>'+esc(String(n.time||'').slice(0,16))+'</em></div></div>'; }).join(''); }
1769  function dwLeave(d){ var el=document.getElementById('dwLeave'); if(!el)return; var types=(d&&d.types)||[], bals=(d&&d.balances)||[]; var by={}; bals.forEach(function(b){by[b.type_code]=b;}); if(!types.length){ el.innerHTML=dwEmpty('Leave balance not set yet.','ti-calendar-off'); return; } el.innerHTML=types.slice(0,4).map(function(t){ var b=by[t.code]||{}; var al=Number(b.allotted!=null?b.allotted:(t.annual_quota||0))||0; var us=Number(b.used||0)+Number(b.pending||0); var rem=Math.max(0,al-us); var pct=al>0?Math.min(100,Math.round(us/al*100)):0; var paid=(String(t.paid)!=='false'); var tag='<span class="lv-tag '+(paid?'paid':'unpaid')+'">'+(paid?'PAID':'UNPAID')+'</span>'; var val=(al>0)?(rem+'<small>/'+al+'</small>'):'∞'; return '<div class="dw-lv"><div class="dw-lv-t"><span>'+esc(t.name||t.code)+tag+'</span><b>'+val+'</b></div><div class="dw-lv-bar"><span style="width:'+pct+'%"></span></div></div>'; }).join(''); }
1770  function dwPerf(rows){ var el=document.getElementById('dwPerf'); if(!el)return; if(!(rows&&rows.length)){ el.innerHTML=dwEmpty('No performance review shared yet.','ti-chart-line'); return; } var r=rows[0]; var ov=Number(r.overall||0); var stars=[1,2,3,4,5].map(function(i){return '<i class="ti ti-star-filled'+(i<=Math.round(ov)?' on':'')+'"></i>';}).join(''); var br=[['Attendance',r.attendance_score],['Quality',r.quality],['Discipline',r.discipline],['Tasks',r.task_score]].filter(function(x){return x[1]!=null&&x[1]!=='';}); el.innerHTML='<div class="dw-perf-top"><div class="dw-perf-n">'+(ov?ov.toFixed(1):'\u2014')+'<small>/5</small></div><div class="dw-perf-r"><div class="stars">'+stars+'</div><span>'+esc(r.period||'Latest review')+'</span></div></div>'+(br.length?'<div class="dw-perf-br">'+br.map(function(x){var v=Number(x[1])||0;return '<div class="dw-lv"><div class="dw-lv-t"><span>'+x[0]+'</span><b>'+v.toFixed(1)+'</b></div><div class="dw-lv-bar"><span style="width:'+(v/5*100)+'%"></span></div></div>';}).join('')+'</div>':''); }
1771  function mtgView(v){ _mtgView=v; document.querySelectorAll('#mtgTabs .kb-cat').forEach(function(b){ b.classList.toggle('on', b.textContent.toLowerCase()===v); }); renderMeetings(); }
1772  function renderMeetings(){
1773    const el=document.getElementById('mtgList'); if(!el) return;
1774    const today=_todayISO();
1775    let rows=_mtg.filter(function(m){ return _mtgView==='upcoming' ? String(m.meeting_date||'')>=today : String(m.meeting_date||'')<today; });
1776    if(_mtgView==='past') rows=rows.reverse();
1777    if(!rows.length){ el.innerHTML='<div class="empty">No '+_mtgView+' meetings.</div>'; return; }
1778    const ico={meeting:'ti-users',event:'ti-confetti',training:'ti-school',holiday:'ti-beach'};
1779    const col={meeting:'#8fb6f0',event:'#e8c977',training:'#7fe0a8',holiday:'#c6a3ff'};
1780    const groups={}; rows.forEach(function(m){ const d=m.meeting_date||''; (groups[d]=groups[d]||[]).push(m); });
1781    let h=''; Object.keys(groups).forEach(function(dt){
1782      h+='<div class="mtg-date">'+esc(fmtDate(dt)||dt)+'</div>';
1783      h+=groups[dt].map(function(m){ const t=String(m.mtype||'meeting'); return '<div class="mtg-card"><div class="mtg-ic" style="background:'+(col[t]||'#8fb6f0')+'"><i class="ti '+(ico[t]||'ti-users')+'"></i></div><div class="mtg-body"><b>'+esc(m.title||'')+'</b><div class="mtg-meta">'+(m.start_time?esc(m.start_time)+(m.end_time?('–'+esc(m.end_time)):''):'All day')+(m.location?(' · '+esc(m.location)):'')+'</div>'+(_mtgView==='upcoming'?'<div class="mtg-cd" data-ts="'+mtgTargetTs(m)+'"></div>':'')+(m.description?'<div class="mtg-desc">'+esc(m.description)+'</div>':'')+(m.link?'<a class="mtg-link" href="'+esc(m.link)+'" target="_blank"><i class="ti ti-video"></i> Join</a>':'')+'</div></div>'; }).join('');
1784    });
1785    el.innerHTML=h;
1786  }
1787  async function updateGoalProgress(gid){
1788    const id=currentId||ssGet('ems_id'); const el=document.getElementById('gs_'+gid); if(!el) return;
1789    const d=await api({action:'emsGoalProgress', employeeId:id, id:gid, progress:el.value});
1790    if(d&&d.status==='success'){ toast('Progress updated ✓'); loadGoals(); } else toast((d&&d.message)||'Failed');
1791  }
1792  var _myTasks=[];
1793  async function loadTasks(){
1794    const id=currentId||ssGet('ems_id'); if(!id) return;
1795    const d=await api({action:'emsMyTasks', employeeId:id}); _myTasks=(d&&d.rows)||[]; renderTasks();
1796  }
1797  function renderTasks(){
1798    const el=document.getElementById('tasksWrap'); if(!el) return;
1799    if(!_myTasks.length){ el.innerHTML='<div class="empty">No tasks assigned yet.</div>'; return; }
1800    const cols=[['todo','To-do'],['in_progress','In Progress'],['review','Review'],['done','Done']];
1801    el.innerHTML='<div class="kb">'+cols.map(function(c){ const items=_myTasks.filter(function(t){return String(t.status)===c[0];}); return '<div class="kb-col"><div class="kb-h">'+c[1]+' <span>'+items.length+'</span></div>'+items.map(taskCard).join('')+'</div>'; }).join('')+'</div>';
1802  }
1803  function taskCard(t){
1804    const pri={high:'#ff9c8b',med:'var(--gold-soft)',low:'var(--faint)'}[String(t.priority)]||'var(--faint)';
1805    const opts=['todo','in_progress','review','done'].map(function(s){ return '<option value="'+s+'"'+(String(t.status)===s?' selected':'')+'>'+s.replace('_',' ')+'</option>'; }).join('');
1806    return '<div class="kb-card"><div style="border-left:3px solid '+pri+';padding-left:8px"><b>'+esc(t.title||'')+'</b>'+(t.description?'<div style="font-size:12px;color:var(--muted);margin-top:2px">'+esc(t.description)+'</div>':'')+(t.due_date?'<div style="font-size:11px;color:var(--faint);margin-top:2px">Due '+esc(t.due_date)+'</div>
1806':'')+'</div><select class="inp" style="margin-top:8px;margin-bottom:0" onchange="taskSetStatus(\''+esc(t.id)+'\',this.value)">'+opts+'</select></div>';
1807  }
1808  async function taskSetStatus(tid,status){
1809    const id=currentId||ssGet('ems_id');
1810    const params={action:'emsTaskUpdate', employeeId:id, id:tid, status:status}; if(status==='done') params.progress=100;
1811    const d=await api(params);
1812    if(d&&d.status==='success'){ toast('Updated ✓'); loadTasks(); } else toast((d&&d.message)||'Failed');
1813  }
1814  async function loadPerformance(){
1815    const id=currentId||ssGet('ems_id'); if(!id) return;
1816    const d=await api({action:'emsMyReviews', employeeId:id});
1817    const el=document.getElementById('perfWrap'); if(!el) return;
1818    const rows=(d&&d.rows)||[];
1819    if(!rows.length){ el.innerHTML='<div class="empty">No performance reviews shared yet.</div>'; return; }
1820    el.innerHTML=rows.map(function(r){
1821      const chips=[['Attendance',r.attendance_score,'%'],['Tasks',r.task_score,'%'],['Quality',r.quality,'/5'],['Communication',r.communication,'/5'],['Leadership',r.leadership,'/5'],['Innovation',r.innovation,'/5'],['Discipline',r.discipline,'/5']];
1822      return '<div class="perf-card"><div class="perf-top"><div><div class="perf-period">'+esc(r.period||'')+'</div>'+(r.promotion_eligible?'<span class="cs resolved" style="margin-top:6px;display:inline-block">Promotion eligible</span>':'')+'</div><div class="perf-overall"><b>'+esc(String(r.overall||0))+'</b><span>/5</span></div></div><div class="perf-chips">'+chips.map(function(c){ return '<span class="perf-chip">'+c[0]+' <b>'+esc(String(c[1]||0))+c[2]+'</b></span>'; }).join('')+'</div>'+(r.manager_feedback?'<div class="perf-fb"><b>Manager Feedback</b><div>'+esc(r.manager_feedback)+'</div></div>':'')+(r.improvement?'<div class="perf-fb"><b>Areas to Improve</b><div>'+esc(r.improvement)+'</div></div>':'')+'</div>';
1823    }).join('');
1824  }
1825  async function loadRewards(){
1826    const id=currentId||ssGet('ems_id'); if(!id) return;
1827    const res=await Promise.all([api({action:'emsMyRewards', employeeId:id}), api({action:'emsRewardsLeaderboard', employeeId:id})]);
1828    const mine=res[0]||{}, lb=res[1]||{}; const el=document.getElementById('rewardsWrap'); if(!el) return;
1829    const rows=mine.rows||[], points=mine.points||0;
1830    let h='<div class="rw-hero"><div><div class="rw-pts">'+points+'</div><div class="rw-lbl">Reward Points</div></div><i class="ti ti-trophy"></i></div>';
1831    if(rows.length){ h+='<div class="accwrap-head" style="margin-top:16px">My Rewards</div>'+rows.map(function(r){ const icon=String(r.kind)==='eotm'?'ti-crown':(String(r.kind)==='badge'?'ti-medal':(String(r.kind)==='achievement'?'ti-star':'ti-coin')); return '<div class="rw-item"><i class="ti '+icon+'"></i><div><b>'+esc(r.title||r.kind)+'</b>'+(r.note?'<div style="font-size:12px;color:var(--muted)">'+esc(r.note)+'</div>':'')+'</div>'+(Number(r.points)>0?'<span class="rw-badge">+'+r.points+'</span>':'')+'</div>'; }).join(''); }
1832    const board=lb.rows||[];
1833    if(board.length){ h+='<div class="accwrap-head" style="margin-top:16px">Leaderboard</div>'+board.map(function(b,i){ const me=String(b.employeeId)===String(id); return '<div class="lb-row'+(me?' me':'')+'"><span class="lb-rank">'+(i+1)+'</span><div class="lb-name"><b>'+esc(b.name||b.employeeId)+'</b>'+(me?' <span style="color:var(--gold-soft)">(you)</span>':'')+'</div><span class="lb-pts">'+(b.points||0)+' pts'+(b.badges?(' · '+b.badges+'\u{1F3C5}'):'')+'</span></div>'; }).join(''); }
1834    if(!rows.length && !board.length) h+='<div class="empty" style="margin-top:16px">No rewards yet. Keep up the great work!</div>';
1835    el.innerHTML=h;
1836  }
1837  /* ---- Phase 4 (Module 3): Learning (employee) ---- */
1838  var _myCourses=[];
1839  async function loadCourses(){
1840    const id=currentId||ssGet('ems_id'); if(!id) return;
1841    const d=await api({action:'emsMyCourses', employeeId:id}); _myCourses=(d&&d.rows)||[];
1842    const el=document.getElementById('coursesWrap'); if(!el) return;
1843    if(!_myCourses.length){ el.innerHTML='<div class="empty">No courses assigned yet.</div>'; return; }
1844    const tc=function(s){ s=String(s||'').replace('_',' '); return s.charAt(0).toUpperCase()+s.slice(1); };
1845    el.innerHTML=_myCourses.map(function(c){
1846      const st=String(c.status||'assigned'); const comp=st==='completed'; const pct=comp?100:(Number(c.progress)||0);
1847      const mand=(c.mandatory===true||String(c.mandatory)==='true');
1848      const openBtn=c.url?'<a class="dl ghost" href="'+esc(c.url)+'" target="_blank" style="padding:7px 12px"><i class="ti ti-external-link"></i> Open course</a>':'';
1849      let ctrl='';
1850      if(comp){ ctrl='<button class="btn" onclick="genCertificate(\''+esc(c.id)+'\')"><i class="ti ti-certificate"></i> Download Certificate</button>'; }
1851      else { ctrl='<div class="crs-edit"><input type="range" min="0" max="100" value="'+pct+'" id="cp_'+esc(c.id)+'" oninput="document.getElementById(\'cv_'+esc(c.id)+'\').textContent=this.value+\'%\'"><span id="cv_'+esc(c.id)+'">'+pct+'%</span><button class="dl ghost" style="padding:7px 12px" onclick="courseSaveProgress(\''+esc(c.id)+'\')">Save</button><button class="btn" onclick="courseComplete(\''+esc(c.id)+'\')"><i class="ti ti-check"></i> Mark complete</button></div>'; }
1852      return '<div class="crs-card"><div class="ctop"><div><b>'+esc(c.course_title||'')+'</b>'+(mand?' <span class="cs" style="background:rgba(243,160,90,.18);color:#f3a05a">Mandatory</span>':'')+'</div><span class="cs '+(comp?'resolved':'open')+'">'+esc(tc(st))+'</span></div><div class="crs-meta">'+esc(tc(c.category||''))+(c.provider?(' · '+esc(c.provider)):'')+(c.duration?(' · '+esc(c.duration)):'')+(c.due_date&&!comp?(' · due '+esc(c.due_date)):'')+(comp&&c.completed_on?(' · completed '+esc(fmtDate(c.completed_on)||c.completed_on)):'')+'</div>'+(c.description?'<div class="crs-desc">'+esc(c.description)+'</div>':'')+'<div class="goal-bar"><span style="width:'+pct+'%"></span></div><div class="crs-actions">'+openBtn+ctrl+'</div></div>';
1853    }).join('');
1854  }
1855  async function courseSaveProgress(cid){
1856    const id=currentId||ssGet('ems_id'); const el=document.getElementById('cp_'+cid); if(!el) return;
1857    const d=await api({action:'emsCourseProgress', employeeId:id, id:cid, progress:el.value});
1858    if(d&&d.status==='success'){ toast('Progress saved \u2713'); loadCourses(); } else toast((d&&d.message)||'Failed');
1859  }
1860  async function courseComplete(cid){
1861    if(!confirm('Mark this course as complete? Your certificate will be ready to download.')) return;
1862    const id=currentId||ssGet('ems_id');
1863    const d=await api({action:'emsCourseProgress', employeeId:id, id:cid, complete:'true'});
1864    if(d&&d.status==='success'){ toast('Completed \u2713'); loadCourses(); } else toast((d&&d.message)||'Failed');
1865  }
1866  function genCertificate(cid){
1867    try{
1868      const c=(_myCourses||[]).find(function(x){return String(x.id)===String(cid);}); if(!c){ toast('Course not found'); return; }
1869      const e=BUNDLE.employee||{}, co=company();
1870      const doc=new window.jspdf.jsPDF({unit:'pt',format:'a4',orientation:'landscape'}); const W=842,H=595;
1871      doc.setFillColor(17,17,22); doc.rect(0,0,W,H,'F');
1872      doc.setDrawColor(200,155,60); doc.setLineWidth(3); doc.rect(24,24,W-48,H-48);
1873      doc.setLineWidth(1); doc.rect(34,34,W-68,H-68);
1874      try{ doc.addImage(logoImg(),'JPEG',W/2-26,58,52,52); }catch(_){}
1875      doc.setTextColor(232,201,119); doc.setFont('helvetica','bold'); doc.setFontSize(15); doc.text(String(co.name||'').toUpperCase(),W/2,132,{align:'center'});
1876      doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(30); doc.text('CERTIFICATE OF COMPLETION',W/2,190,{align:'center'});
1877      doc.setDrawColor(200,155,60); doc.setLineWidth(2); doc.line(W/2-150,206,W/2+150,206);
1878      doc.setTextColor(200,200,205); doc.setFont('helvetica','normal'); doc.setFontSize(13); doc.text('This is to certify that',W/2,250,{align:'center'});
1879      doc.setTextColor(232,201,119); doc.setFont('helvetica','bold'); doc.setFontSize(28); doc.text(String(e.name||''),W/2,290,{align:'center'});
1880      doc.setTextColor(200,200,205); doc.setFont('helvetica','normal'); doc.setFontSize(13); doc.text('has successfully completed the course',W/2,325,{align:'center'});
1881      doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(19); doc.text(doc.splitTextToSize('"'+String(c.course_title||'')+'"',W-220),W/2,360,{align:'center'});
1882      const dt=fmtDate(c.completed_on)||c.completed_on||new Date().toLocaleDateString('en-IN');
1883      doc.setTextColor(185,185,190); doc.setFont('helvetica','normal'); doc.setFontSize(11);
1884      doc.text('Date of Completion: '+dt,W/2,422,{align:'center'});
1885      if(c.provider) doc.text('Provider: '+String(c.provider),W/2,440,{align:'center'});
1886      doc.setDrawColor(150,150,155); doc.setLineWidth(.8); doc.line(W-262,502,W-92,502);
1887      doc.setTextColor(200,200,205); doc.setFontSize(11); doc.text('Authorised Signatory',W-177,518,{align:'center'});
1888      doc.setTextColor(140,140,145); doc.setFontSize(8.5); doc.text(String(co.name||'')+' \u00b7 '+String(co.hr||'HR Department'),W-177,532,{align:'center'});
1889      doc.setTextColor(120,120,125); doc.setFontSize(8.5); doc.text('Employee ID: '+String(e.employeeId||''),72,532);
1890      doc.save((String(e.name||'employee').replace(/\s+/g,'_'))+'_Certificate_'+(String(c.course_title||'course').replace(/\s+/g,'_'))+'.pdf');
1891    }catch(err){ toast('Could not generate certificate. Please try again.'); }
1892  }
1893  /* ---- Phase 4 (Module 4): Engagement (employee) ---- */
1894  var _myPolls=[];
1895  async function loadEngagement(){
1896    const id=currentId||ssGet('ems_id'); if(!id) return;
1897    const res=await Promise.all([api({action:'emsMyPolls', employeeId:id}), api({action:'emsSuggestionBrowse', employeeId:id})]);
1898    _myPolls=(res[0]&&res[0].rows)||[];
1899    renderEngagement(_myPolls, (res[1]&&res[1].rows)||[]);
1900  }
1901  function renderEngagement(polls, suggestions){
1902    const el=document.getElementById('engageWrap'); if(!el) return;
1903    let h='';
1904    const pulses=polls.filter(function(p){return String(p.type)==='pulse' && !p.responded;});
1905    pulses.forEach(function(p){ h+='<div class="pulse-card"><div class="pulse-q"><i class="ti ti-mood-smile"></i> '+esc((p.questions[0]||{}).q||'How are you feeling?')+'</div><div class="pulse-scale">'+[1,2,3,4,5].map(function(n){ return '<button class="pulse-dot" onclick="submitPulse(\''+esc(p.id)+'\','+n+')">'+n+'</button>'; }).join('')+'</div></div>'; });
1906    const active=polls.filter(function(p){return String(p.type)!=='pulse';});
1907    if(active.length){ h+='<div class="accwrap-head" style="margin-top:14px">Polls &amp; Surveys</div>'; active.forEach(function(p){ if(p.responded){ h+='<div class="poll-card"><b>'+esc(p.title)+'</b> <span class="cs resolved">Thanks for responding</span></div>'; return; } h+='<div class="poll-card"><b>'+esc(p.title)+'</b>'+(p.description?'<div style="color:var(--muted);font-size:13px;margin:4px 0">'+esc(p.description)+'</div>':'')+(p.questions||[]).map(function(q,qi){ return '<div class="poll-q"><div class="poll-qt">'+esc(q.q||'')+'</div>'+pollInput(p.id,qi,q)+'</div>'; }).join('')+'<button class="btn" style="margin-top:10px" onclick="submitPoll(\''+esc(p.id)+'\')"><i class="ti ti-send"></i> Submit</button></div>'; }); }
1908    h+='<div class="accwrap-head" style="margin-top:24px">Suggestion Box</div><div class="sug-form"><div class="lv-form"><div><label>Category</label><select class="inp" id="sugCat"><option value="general">General</option><option value="workplace">Workplace</option><option value="process">Process</option><option value="tools">Tools</option><option value="culture">Culture</option><option value="other">Other</option></select></div><div style="grid-column:span 2"><label>Title</label><input class="inp" id="sugTitle"></div></div><textarea id="sugMsg" placeholder="Share your idea…"></textarea><label class="lv-half"><input type="checkbox" id="sugAnon"> Submit anonymously</label><button class="btn" onclick="submitSuggestion()"><i class="ti ti-send"></i> Submit idea</button></div><div style="margin-top:16px">'+renderSuggestions(suggestions)+'</div>';
1909    el.innerHTML=h;
1910  }
1911  function pollInput(pid,qi,q){
1912    const kind=String(q.kind||'single');
1913    if(kind==='rating'){ return '<div class="pulse-scale" data-p="'+esc(pid)+'" data-q="'+qi+'">'+[1,2,3,4,5].map(function(n){ return '<button type="button" class="pulse-dot" onclick="pickRating(this)">'+n+'</button>'; }).join('')+'</div>'; }
1914    if(kind==='text'){ return '<textarea class="inp" id="pa_'+esc(pid)+'_'+qi+'" rows="2"></textarea>'; }
1915    const tp=(kind==='multi'?'checkbox':'radio');
1916    return (q.options||[]).map(function(o){ return '<label class="poll-opt"><input type="'+tp+'" name="pa_'+esc(pid)+'_'+qi+'" value="'+esc(o)+'"> '+esc(o)+'</label>'; }).join('');
1917  }
1918  function pickRating(btn){ const wrap=btn.parentNode; wrap.setAttribute('data-val',btn.textContent); [].forEach.call(wrap.children,function(b){b.classList.remove('on');}); btn.classList.add('on'); }
1919  async function submitPoll(pid){
1920    const p=(_myPolls||[]).find(function(x){return String(x.id)===String(pid);}); if(!p) return;
1921    const answers=(p.questions||[]).map(function(q,qi){
1922      const kind=String(q.kind||'single');
1923      if(kind==='rating'){ const w=document.querySelector('.pulse-scale[data-p="'+pid+'"][data-q="'+qi+'"]'); return w?Number(w.getAttribute('data-val')||0):0; }
1924      if(kind==='text'){ const t=document.getElementById('pa_'+pid+'_'+qi); return t?t.value.trim():''; }
1925      if(kind==='multi'){ return [].slice.call(document.querySelectorAll('input[name="pa_'+pid+'_'+qi+'"]:checked')).map(function(c){return c.value;}); }
1926      const r=document.querySelector('input[name="pa_'+pid+'_'+qi+'"]:checked'); return r?r.value:'';
1927    });
1928    const id=currentId||ssGet('ems_id');
1929    const d=await api({action:'emsPollRespond', employeeId:id, pollId:pid, answers:JSON.stringify(answers)});
1930    if(d&&d.status==='success'){ toast('Response submitted \u2713'); loadEngagement(); } else toast((d&&d.message)||'Failed');
1931  }
1932  async function submitPulse(pid,n){
1933    const id=currentId||ssGet('ems_id');
1934    const d=await api({action:'emsPollRespond', employeeId:id, pollId:pid, answers:JSON.stringify([n])});
1935    if(d&&d.status==='success'){ toast('Thanks for sharing \u2713'); loadEngagement(); } else toast((d&&d.message)||'Failed');
1936  }
1937  function renderSuggestions(rows){
1938    if(!rows||!rows.length) return '<div class="empty">No suggestions yet.</div>';
1939    return rows.map(function(s){ const anon=(s.anonymous===true||String(s.anonymous)==='true'); const voted=s.voted; const st=String(s.status||'new'); return '<div class="sug-item"><button class="sug-vote'+(voted?' on':'')+'" onclick="voteSuggestion(\''+esc(s.id)+'\')"><i class="ti ti-arrow-big-up'+(voted?'-filled':'')+'"></i><span>'+(s.votes||0)+'</span></button><div class="sug-body"><b>'+esc(s.title||'')+'</b> <span class="cs '+(st==='done'?'resolved':(st==='declined'?'':'open'))+'">'+esc(st)+'</span><div class="sug-meta">'+esc(s.category||'')+' \u00b7 '+(anon?'Anonymous':esc(s.name||''))+'</div>'+(s.message?'<div class="sug-msg">'+esc(s.message)+'</div>':'')+(s.admin_note?'<div class="creply"><b>HR:</b> '+esc(s.admin_note)+'</div>':'')+'</div></div>'; }).join('');
1940  }
1941  async function submitSuggestion(){
1942    const id=currentId||ssGet('ems_id');
1943    const title=document.getElementById('sugTitle').value.trim(), msg=document.getElementById('sugMsg').value.trim();
1944    if(!title){ toast('Add a title'); return; }
1945    const d=await api({action:'emsSuggestionSubmit', employeeId:id, category:document.getElementById('sugCat').value, title:title, message:msg, anonymous:document.getElementById('sugAnon').checked?'true':'false'});
1946    if(d&&d.status==='success'){ toast('Idea submitted \u2713'); loadEngagement(); } else toast('Failed');
1947  }
1948  async function voteSuggestion(sid){
1949    const id=currentId||ssGet('ems_id');
1950    const d=await api({action:'emsSuggestionVote', employeeId:id, id:sid});
1951    if(d&&d.status==='success'){ loadEngagement(); } else toast('Failed');
1952  }
1953  /* ---- Team Directory ---- */
1954  var _dir=[];
1955  async function loadDirectory(){
1956    const id=currentId||ssGet('ems_id'); if(!id) return;
1957    const d=await api({action:'emsDirectory', employeeId:id}); _dir=(d&&d.rows)||[]; renderDirectory();
1958  }
1959  function renderDirectory(){
1960    const el=document.getElementById('dirList'); if(!el) return;
1961    const ql=((document.getElementById('dirSearch')||{}).value||'').trim().toLowerCase();
1962    let rows=_dir; if(ql) rows=_dir.filter(function(e){ return (String(e.name)+' '+String(e.department)+' '+String(e.designation)+' '+String(e.employeeId)).toLowerCase().indexOf(ql)>=0; });
1963    if(!rows.length){ el.innerHTML='<div class="empty">No colleagues found.</div>'; return; }
1964    const groups={}; rows.forEach(function(e){ const d=e.department||'Other'; (groups[d]=groups[d]||[]).push(e); });
1965    let h=''; Object.keys(groups).sort().forEach(function(dep){
1966      h+='<div class="dir-dep">'+esc(dep)+' <span>'+groups[dep].length+'</span></div><div class="dir-grid">';
1967      h+=groups[dep].map(function(e){ const ini=(String(e.name||'?').trim()[0]||'?').toUpperCase(); return '<div class="dir-card"><div class="dir-av">'+esc(ini)+'</div><div class="dir-info"><b>'+esc(e.name||'')+'</b><span>'+esc(e.designation||'\u2014')+'</span></div></div>'; }).join('');
1968      h+='</div>';
1969    });
1970    el.innerHTML=h;
1971  }
1972  var _attMonth=null, _leaveTypes=[];
1973  async function renderAssetsCard(){
1974    const id=currentId||ssGet('ems_id'); const el=document.getElementById('assetsCard'); if(!id||!el) return;
1975    try{
1976      const d=await api({action:'emsMyAssets', employeeId:id});
1977      const rows=(d&&d.rows)||[]; if(!d||d.status!=='success'||!rows.length){ el.innerHTML=''; return; }
1978      el.innerHTML='<div class="asset-card"><div class="asset-head"><i class="ti ti-devices"></i> My Assets <span>'+rows.length+'</span></div>'+rows.map(function(a){ const ck=(a.ack===true||String(a.ack)==='true'); const icon={laptop:'ti-device-laptop',phone:'ti-device-mobile',sim:'ti-sim-card',id_card:'ti-id',furniture:'ti-armchair-2',software:'ti-app-window'}[String(a.category)]||'ti-device-desktop'; const cond=String(a.condition||''); return '<div class="asset-item"><i class="ti '+icon+'"></i><div class="asset-info"><b>'+esc(a.name||'')+'</b>'+(a.tag?' <span class="asset-tag">'+esc(a.tag)+'</span>':'')+'<div class="asset-meta">'+esc(cond.charAt(0).toUpperCase()+cond.slice(1))+(a.assigned_on?(' · since '+esc(fmtDate(a.assigned_on)||a.assigned_on)):'')+'</div></div>'+(ck?'<span class="asset-ack done"><i class="ti ti-circle-check-filled"></i> Received</span>':'<button class="asset-ack" onclick="ackAsset(\''+esc(a.id)+'\')">Acknowledge</button>')+'</div>'; }).join('')+'</div>';
1979    }
1979catch(e){ el.innerHTML=''; }
1980  }
1981  async function ackAsset(aid){
1982    const emp=currentId||ssGet('ems_id');
1983    const d=await api({action:'emsAssetAck', employeeId:emp, id:aid});
1984    if(d&&d.status==='success'){ toast('Receipt acknowledged \u2713'); renderAssetsCard(); } else toast((d&&d.message)||'Failed');
1985  }
1986  async function renderOnboardCard(){
1987    const id=currentId||ssGet('ems_id'); const el=document.getElementById('onboardCard'); if(!id||!el) return;
1988    try{
1989      const d=await api({action:'emsMyOnboarding', employeeId:id});
1990      const rows=(d&&d.rows)||[]; if(!d||d.status!=='success'||!rows.length){ el.innerHTML=''; return; }
1991      const done=d.done||0, total=d.total||rows.length; if(done>=total){ el.innerHTML=''; return; }
1992      const pct=Math.round(done/Math.max(1,total)*100);
1993      el.innerHTML='<div class="onb-card"><div class="onb-head"><div><div class="onb-title"><i class="ti ti-rocket"></i> Welcome aboard!</div><div class="onb-sub">Your onboarding checklist \u00b7 '+done+' of '+total+' complete</div></div><div class="onb-pct">'+pct+'%</div></div><div class="onb-bar"><span style="width:'+pct+'%"></span></div><div class="onb-steps">'+rows.map(function(r){ const ck=(r.done===true||String(r.done)==='true'); return '<div class="onb-step'+(ck?' done':'')+'"><i class="ti '+(ck?'ti-circle-check-filled':'ti-circle')+'"></i> <span>'+esc(r.task)+'</span></div>'; }).join('')+'</div></div>';
1994    }catch(e){ el.innerHTML=''; }
1995  }
1996  async function renderPunchCard(){
1997    // Attendance disabled for this employee → no punch card at all.
1998    if(window._attEnabled===false){ var _pc0=document.getElementById('punchCard'); if(_pc0){ _pc0.innerHTML=''; _pc0.style.display='none'; } return; }
1999    try{ window._onDuty=!!localStorage.getItem('ems_onduty_'+_todayISO()); }catch(_od){}
2000    const id=currentId||ssGet('ems_id'); const el=document.getElementById('punchCard'); if(!id||!el) return;
2001    try{ const d=await api({action:'emsAttToday', employeeId:id}); if(!d||d.status!=='success'){ el.innerHTML=''; return; }
2002      const t=d.today||{}; const state=d.state;
2003      var _pi=(state==='in'&&t.punch_in)?String(t.punch_in).split(':'):null;
2004      window._pinSec=_pi?((+_pi[0])*3600+(+_pi[1]||0)*60+(+_pi[2]||0)):null;
2005      window._pinMin=_pi?((+_pi[0])*60+(+_pi[1]||0)):null;
2006      window._pbreak=Number(t.break_mins)||0;
2007      function _liveWorkSecs(){ if(window._pinSec==null) return (Number(t.work_mins)||0)*60; var now=new Date(); var s=(now.getHours()*3600+now.getMinutes()*60+now.getSeconds())-window._pinSec-window._pbreak*60; return s>0?s:0; }
2008      function _fmtHMS(s){ var h=Math.floor(s/3600),m=Math.floor((s%3600)/60),x=s%60; return (h<10?'0':'')+h+':'+(m<10?'0':'')+m+':'+(x<10?'0':'')+x; }
2009      const work=Math.floor(_liveWorkSecs()/60); const wh=(state==='in')?_fmtHMS(_liveWorkSecs()):((Math.floor(work/60))+'h '+(work%60)+'m');
2010      const pin=esc(t.punch_in?fmt12(t.punch_in):'\u2014'), pout=esc(t.punch_out?fmt12(t.punch_out):'\u2014'), pct=Number(d.pct)||0;
2011      let badge, action;
2012      if(state==='none'){ badge='<span class="p2-badge wait">Not started</span>'; action='<button class="p2-btn in" onclick="doPunch()"><i class="ti ti-fingerprint"></i> Punch In</button>'; }
2013      else if(state==='in'){ badge='<span class="p2-badge live"><span class="p2-dot"></span> Working</span>'; action='<div class="p2-actions"><button class="p2-btn out" onclick="doPunch()"><i class="ti ti-door-exit"></i> Punch Out</button><button class="p2-duty'+(window._onDuty?' on':'')+'" onclick="toggleDuty()"><i class="ti ti-map-pin"></i> '+(window._onDuty?'On field duty':'On Duty')+'</button></div>'; }
2014      else { badge='<span class="p2-badge done"><i class="ti ti-circle-check-filled"></i> Done for today</span>'; action=''; }
2015      el.innerHTML='<div class="punch2"><div class="p2-head"><span class="p2-title"><i class="ti ti-clock-check"></i> Today\u2019s Attendance</span>'+badge+'</div>'
2016        +'<div class="p2-times"><div class="p2-time in"><span>IN</span><b>'+pin+'</b></div><div class="p2-sep"><i class="ti ti-arrow-right"></i></div><div class="p2-time out"><span>OUT</span><b>'+pout+'</b></div><div class="p2-time worked'+(state==='in'?' live':'')+'"><span>WORKED'+(state==='in'?' <b style="color:#34d399;font-size:8px">\u25cf LIVE</b>':'')+'</span><b id="p2Worked">'+wh+'</b></div></div>'
2017        +'<div class="p2-foot"><div class="p2-month"><div class="p2-mlbl">This month <b>'+pct+'%</b></div><div class="p2-bar"><span style="width:'+pct+'%"></span></div></div>'+action+'</div></div>';
2018      try{ clearInterval(window._workTick); }catch(_wt){}
2019      if(state==='in'){ window._workTick=setInterval(function(){ var wb=document.getElementById('p2Worked'); if(!wb||window._pinSec==null){ try{clearInterval(window._workTick);}catch(_){} return; } var now=new Date(); var s=(now.getHours()*3600+now.getMinutes()*60+now.getSeconds())-window._pinSec-window._pbreak*60; if(s<0)s=0; var h=Math.floor(s/3600),m=Math.floor((s%3600)/60),x=s%60; wb.textContent=(h<10?'0':'')+h+':'+(m<10?'0':'')+m+':'+(x<10?'0':'')+x; }, 1000); }
2020      try{ const nw=await api({action:'emsMyIp', employeeId:id}); if(nw&&nw.status==='success'){ window._attSec=nw; }
2021        if(nw&&nw.status==='success'){ const host=el.querySelector('.punch2');
2022          if(host && nw.lock){ const w=document.createElement('div'); w.className='net-status'; w.innerHTML= nw.office ? '<div class="net-ok"><i class="ti ti-wifi"></i> Office network verified'+(nw.location?' \u00b7 '+esc(nw.location):'')+'</div>' : '<div class="net-warn"><i class="ti ti-camera"></i> Off office network \u2014 a live selfie is needed to punch (HR is notified)</div>'; host.appendChild(w); if(!window._netTimer){ window._netTimer=setInterval(netCheck,120000); } }
2023          if(host && nw.geoLock){ _geoStatusInto(host, nw.geoPoints||[]); }
2024          if(host && nw.faceLock){ try{ _faceInit(); }catch(_){}
2025            var fw=document.createElement('div'); fw.className='net-status'; fw.style.marginTop='6px';
2026            if(nw.faceRegistered){ fw.innerHTML='<div class="net-ok"><i class="ti ti-user-check"></i> Face ID active — your face is verified on every punch</div>'; }
2027            else { fw.innerHTML='<div class="net-warn" style="cursor:pointer" onclick="startFaceSetup()"><i class="ti ti-user-scan"></i> Set up your Face ID once to enable punching — tap here</div>'; }
2028            host.appendChild(fw);
2029          }
2030        }
2031      }catch(_n){}
2032    }catch(e){ el.innerHTML=''; }
2033  }
2034  function emsDeviceId(){ var d=null; try{ d=localStorage.getItem('ems_device'); }catch(_){} if(!d){ d='DEV-'+Math.random().toString(36).slice(2,10)+Date.now().toString(36); try{ localStorage.setItem('ems_device',d); }catch(_){} } return d; }
2035  // ===== GPS geo-fence helpers =====
2036  function _distM(la1,lo1,la2,lo2){ var R=6371000,tR=function(d){return d*Math.PI/180;}; var dLa=tR(la2-la1),dLo=tR(lo2-lo1); var a=Math.sin(dLa/2)*Math.sin(dLa/2)+Math.cos(tR(la1))*Math.cos(tR(la2))*Math.sin(dLo/2)*Math.sin(dLo/2); return Math.round(R*2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a))); }
2037  function _getGeo(){ return new Promise(function(res,rej){ if(!navigator.geolocation){ rej(new Error('This phone does not support GPS location.')); return; } navigator.geolocation.getCurrentPosition(function(p){ res({lat:p.coords.latitude,lng:p.coords.longitude,acc:p.coords.accuracy}); }, function(err){ rej(new Error((err&&err.code===1)?'Location permission denied — allow Location for the portal, then punch.':'Could not get your location — turn on GPS and try again.')); }, {enableHighAccuracy:true,timeout:12000,maximumAge:0}); }); }
2038  function _geoStatusInto(host, points){
2039    if(!host) return; var w=document.createElement('div'); w.className='net-status'; w.style.marginTop='6px';
2040    w.innerHTML='<div class="net-warn"><i class="ti ti-map-pin"></i> Checking your location…</div>'; host.appendChild(w);
2041    if(!points||!points.length){ w.innerHTML='<div class="net-warn"><i class="ti ti-map-pin-exclamation"></i> Office location not set yet — ask HR.</div>'; return; }
2042    _getGeo().then(function(g){ window._punchGeo=g; var best=null; points.forEach(function(p){ var d=_distM(g.lat,g.lng,p.lat,p.lng); if(best===null||d<best.d) best={d:d,r:Number(p.radius)||150,n:p.name}; });
2043      if(best.d<=best.r) w.innerHTML='<div class="net-ok"><i class="ti ti-map-pin-check"></i> At '+esc(best.n)+' · '+best.d+' m (within '+best.r+' m)</div>';
2044      else w.innerHTML='<div class="net-bad"><i class="ti ti-map-pin-off"></i> '+best.d+' m from '+esc(best.n)+' — outside office area ('+best.r+' m)<div style="font-size:11px;opacity:.85;margin-top:2px">Your location: '+g.lat.toFixed(5)+', '+g.lng.toFixed(5)+'</div></div>';
2045    }).catch(function(e){ w.innerHTML='<div class="net-warn"><i class="ti ti-map-pin-exclamation"></i> '+esc((e&&e.message)||'Turn on location (GPS) to punch')+'</div>'; });
2046  }
2047  async function doPunch(){
2048    const id=currentId||ssGet('ems_id'); if(!id) return;
2049    const sec=window._attSec||{};
2050    if(sec.faceLock && !sec.faceRegistered){ toast('Set up your Face ID first'); startFaceSetup(); return; }
2051    if(sec.geoLock){
2052      try{ window._punchGeo=await _getGeo(); }
2053      catch(e){ toast((e&&e.message)||'Location needed — turn on GPS and allow it'); return; }
2054    } else { window._punchGeo=null; }
2055    if(sec.selfie || sec.faceLock){ window._selfieMode='punch'; openSelfie(); return; }
2056    sendPunch(id,'');
2057  }
2058  function _reSyncPunch(){ renderPunchCard(); const a=document.getElementById('attendance'); if(a&&a.classList.contains('show')) loadAttendance(); }
2059  async function sendPunch(id,photo){
2060    // Debounce: ignore a second tap while a punch is already in flight — stops a double-tap from firing two
2061    // concurrent punches (which raced into a duplicate-key error on the server).
2062    if(window._punchBusy) return; window._punchBusy=true;
2063    try{
2064      var _g=window._punchGeo||{}; var _fd=window._punchFaceDesc;
2065      const d=await Promise.race([
2066        api({action:'emsAttPunch', employeeId:id, token:ssGet('ems_tok'), device:emsDeviceId(), photo:photo||'', lat:(_g.lat!=null?_g.lat:''), lng:(_g.lng!=null?_g.lng:''), faceDesc:(_fd?JSON.stringify(_fd):'')}),
2067        new Promise(function(_,rej){ setTimeout(function(){ rej(new Error('timeout')); }, 15000); })
2068      ]);
2069      if(d&&d.status==='success'){ toast(d.state==='in'?'Punched in \u2713':(d.state==='out'?'Punched out \u2713':'Done \u2713')); _reSyncPunch(); }
2070      else if(d&&d.geoBlocked){ toast((d.message)||'You are outside the office area'); try{ alert((d.message)||'You are outside the office area.'); }catch(_al){} }
2071      else if(d&&d.faceSetup){ toast((d.message)||'Set up your Face ID first'); startFaceSetup(); }
2072      else if(d&&d.faceFail){ toast((d.message)||'Face did not match'); try{ alert((d.message)||'Face did not match the registered employee.'); }catch(_al){} }
2073      else if(d&&d.selfieRequired){ toast('Selfie needed \u2014 please try again'); }
2074      else if(d&&d.ipBlocked){ toast((d.message)||'Not on office network'); }
2075      else if(d&&d.deviceBlocked){ toast((d.message)||'This device is not registered'); }
2076      else { var _m=(d&&d.message)||'Punch failed \u2014 please try again'; toast(_m); try{ alert('Punch could not complete:\n\n'+_m); }catch(_al){} }
2077    }catch(e){
2078      // slow connection: the punch may already be saved on the server — re-check quietly
2079      toast('Finishing up\u2026'); setTimeout(_reSyncPunch, 2000);
2080    }finally{ window._punchBusy=false; }
2081  }
2082  var _selfieStream=null;
2083  function _selfieMsg(t,ok){ var e=document.getElementById('selfieState'); if(e){ e.textContent=t||''; e.style.color=ok?'#7ee0a8':'#ffb0a2'; } }
2084  // Switch the selfie sheet into a clean "use your phone camera" state \u2014 no broken black box, native photo
2085  // is the primary action (works everywhere, even where the live camera is blocked or the page isn't HTTPS).
2086  function _selfieFallback(){
2087    var v=document.getElementById('selfieVid'); if(v){ v.style.display='none'; }
2088    var ph=document.getElementById('selfiePh'); if(ph){ ph.style.display='flex'; }
2089    var cap=document.getElementById('selfieCapBtn'); if(cap){ cap.style.display='none'; }
2090    _selfieMsg('');
2091    var nb=document.getElementById('selfieNativeBtn');
2092    if(nb){ nb.style.borderColor='var(--gold-soft)'; nb.style.background='var(--gold-soft)'; nb.style.color='#1a1305'; nb.style.marginTop='4px'; nb.style.fontSize='15px'; nb.style.padding='13px'; nb.innerHTML='<i class="ti ti-camera"></i> '+((window._selfieMode==='register')?'Take a selfie to set up':'Take photo with camera'); }
2093  }
2094  // Camera UI state: 'starting' | 'live' | 'error'. When 'error', the main button retries the live camera.
2095  var _selfieCamState='live';
2096  // Open the front camera trying several constraint sets \u2014 some phones / in-app WebViews reject the strict
2097  // {facingMode:'user'} but accept {video:true}. First match wins; only if ALL fail do we show the retry state.
2098  function _getSelfieCam(){
2099    var tries=[
2100      {video:{facingMode:'user'},audio:false},
2101      {video:{facingMode:{ideal:'user'}},audio:false},
2102      {video:true,audio:false}
2103    ];
2104    return new Promise(function(resolve,reject){
2105      var i=0, lastErr=null;
2106      (function next(){
2107        if(i>=tries.length){ reject(lastErr||new Error('no camera')); return; }
2108        var c=tries[i++];
2109        navigator.mediaDevices.getUserMedia(c).then(resolve).cat
2109ch(function(e){ lastErr=e; next(); });
2110      })();
2111    });
2112  }
2113  function _selfieCamError(err){
2114    _selfieCamState='error';
2115    var name=(err&&err.name)||'';
2116    var v=document.getElementById('selfieVid'); if(v){ v.style.display='none'; }
2117    var ph=document.getElementById('selfiePh'); if(ph){ ph.style.display='flex'; var t=ph.querySelector('.selfie-ph-t'); if(t) t.textContent='Camera needs permission'; }
2118    var cap=document.getElementById('selfieCapBtn');
2119    // Keep the LIVE CAMERA as the primary action \u2014 the main button becomes "Allow camera & retry".
2120    if(cap){ cap.style.display=''; cap.disabled=false; cap.style.opacity='1'; cap.innerHTML='<i class="ti ti-refresh"></i> Allow camera &amp; retry'; }
2121    var secure=(typeof window.isSecureContext==='undefined')?true:window.isSecureContext;
2122    var msg = (!secure) ? 'For the camera, open this page on https (not http).'
2123            : (name==='NotAllowedError'||name==='SecurityError'||name==='PermissionDeniedError') ? 'Allow camera access when your phone asks, then tap \u201cAllow camera & retry\u201d.'
2124            : (name==='NotFoundError'||name==='DevicesNotFoundError') ? 'No camera found on this device.'
2125            : (name==='NotReadableError'||name==='TrackStartError') ? 'Camera is busy in another app \u2014 close it, then tap retry.'
2126            : 'Could not open the camera \u2014 tap \u201cAllow camera & retry\u201d.';
2127    _selfieMsg(msg);
2128    // Secondary, low-emphasis fallback: take a photo with the phone camera app.
2129    var nb=document.getElementById('selfieNativeBtn'); if(nb){ nb.style.borderColor='rgba(255,255,255,.22)'; nb.style.background='none'; nb.style.color='var(--muted)'; nb.style.marginTop='9px'; nb.style.fontSize='13px'; nb.style.padding='11px'; nb.innerHTML='<i class="ti ti-camera-plus"></i> Or take a photo with your camera'; }
2130  }
2131  function openSelfie(){
2132    var ov=document.getElementById('selfieOverlay'); if(!ov)return; ov.classList.add('on');
2133    var cap=document.getElementById('selfieCapBtn'); _selfieMsg('');
2134    _selfieCamState='starting';
2135    // stop any previous stream before restarting (retry)
2136    if(_selfieStream){ try{ _selfieStream.getTracks().forEach(function(t){t.stop();}); }catch(_){} _selfieStream=null; }
2137    var v0=document.getElementById('selfieVid'); if(v0){ v0.style.display=''; }
2138    var ph0=document.getElementById('selfiePh'); if(ph0){ ph0.style.display='none'; }
2139    if(cap){ cap.style.display=''; cap.disabled=true; cap.style.opacity='1'; cap.innerHTML='<i class="ti ti-camera"></i> Starting camera\u2026'; }
2140    var nb0=document.getElementById('selfieNativeBtn'); if(nb0){ nb0.style.borderColor='rgba(255,255,255,.22)'; nb0.style.background='none'; nb0.style.color='var(--muted)'; nb0.style.marginTop='9px'; nb0.style.fontSize='13px'; nb0.style.padding='11px'; nb0.innerHTML='<i class="ti ti-camera-plus"></i> Camera not opening? Take photo instead'; }
2141    if(!navigator.mediaDevices||!navigator.mediaDevices.getUserMedia){ _selfieCamError({name:'NotFoundError'}); return; }
2142    _getSelfieCam().then(function(st){
2143      _selfieStream=st; var v=document.getElementById('selfieVid');
2144      if(!v){ _selfieMsg('Camera view missing.'); return; }
2145      if(v.style.display==='none') v.style.display='';
2146      var ph=document.getElementById('selfiePh'); if(ph) ph.style.display='none';
2147      v.srcObject=st; v.setAttribute('playsinline',''); v.muted=true; var pp=v.play(); if(pp&&pp.catch)pp.catch(function(){});
2148      var tries=0; var iv=setInterval(function(){ tries++;
2149        if(v.videoWidth>0){ clearInterval(iv); _selfieCamState='live'; _selfieMsg(''); if(cap){ cap.disabled=false; cap.innerHTML=_selfieCapText(); } }
2150        else if(tries>50){ clearInterval(iv); _selfieCamState='live'; _selfieMsg('Camera slow to start \u2014 tap Capture to try anyway.'); if(cap){ cap.disabled=false; cap.innerHTML=_selfieCapText(); } }
2151      },100);
2152    }).catch(function(err){ _selfieCamError(err); });
2153  }
2154  function selfieNativePick(){ var fi=document.getElementById('selfieFileInput'); if(fi){ fi.value=''; fi.click(); } }
2155  function selfieFilePicked(inp){
2156    var f=inp.files&&inp.files[0]; if(!f){ return; }
2157    _selfieMsg('Reading photo\u2026', true);
2158    var r=new FileReader();
2159    r.onload=function(){
2160      var img=new Image();
2161      img.onload=function(){
2162        try{
2163          var cv=_cvFrom(img);
2164          var photo=cv.toDataURL('image/jpeg',0.82);
2165          if(!photo||photo.length<200){ _selfieMsg('Could not read the photo. Please try again.'); return; }
2166          _faceHandleCanvas(cv, photo);
2167        }catch(e){ _selfieMsg('Photo error: '+((e&&e.message)||e)); }
2168      };
2169      img.onerror=function(){ _selfieMsg('Could not open the photo. Please try again.'); };
2170      img.src=String(r.result||'');
2171    };
2172    r.onerror=function(){ _selfieMsg('Could not read the photo. Please try again.'); };
2173    r.readAsDataURL(f);
2174  }
2175  function closeSelfie(){ var ov=document.getElementById('selfieOverlay'); if(ov)ov.classList.remove('on'); if(_selfieStream){ try{ _selfieStream.getTracks().forEach(function(t){t.stop();}); }catch(_){} _selfieStream=null; } var fi=document.getElementById('selfieFileInput'); if(fi)fi.value=''; }
2176  function _doCapture(v){
2177    try{
2178      var cv=_cvFrom(v);
2179      var photo=cv.toDataURL('image/jpeg',0.82);
2180      if(!photo||photo.length<200){ _selfieMsg('Could not read the camera frame. Reopen and try again.'); return; }
2181      _faceHandleCanvas(cv, photo);
2182    }catch(e){ _selfieMsg('Capture error: '+((e&&e.message)||e)); }
2183  }
2184  function captureSelfie(){
2185    // In the error state this button re-opens the live camera (asks permission again) instead of capturing.
2186    if(_selfieCamState==='error'){ openSelfie(); return; }
2187    var v=document.getElementById('selfieVid'); if(!v){ _selfieMsg('Camera not found.'); return; }
2188    if(v.videoWidth>0){ _doCapture(v); return; }
2189    _selfieMsg('Camera warming up\u2026'); var pp=v.play(); if(pp&&pp.catch)pp.catch(function(){});
2190    setTimeout(function(){ if(v.videoWidth>0){ _doCapture(v); } else { _selfieMsg('Camera not ready. Close, reopen, and allow camera permission.'); } }, 800);
2191  }
2192  // ===================== Face ID (register + verify) =====================
2193  var FACE_SCRIPTS=['https://cdn.jsdelivr.net/npm/@vladmandic/face-api/dist/face-api.js','https://unpkg.com/@vladmandic/face-api/dist/face-api.js'];
2194  var FACE_MODEL_URLS=['https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model','https://cdn.jsdelivr.net/gh/vladmandic/face-api/model','https://unpkg.com/@vladmandic/face-api/model'];
2195  var _faceLib=null, _faceReady=false, _faceLoading=null;
2196  function _loadScript(src){ return new Promise(function(res,rej){ var s=document.createElement('script'); s.src=src; s.onload=function(){res();}; s.onerror=function(){ rej(new Error('load failed')); }; document.head.appendChild(s); }); }
2197  async function _loadFaceLib(){ if(window.faceapi) return; var last=null; for(var i=0;i<FACE_SCRIPTS.length;i++){ try{ await _loadScript(FACE_SCRIPTS[i]); if(window.faceapi) return; }catch(e){ last=e; } } if(!window.faceapi) throw (last||new Error('face lib')); }
2198  async function _loadFaceModels(f){ var last=null; for(var u=0;u<FACE_MODEL_URLS.length;u++){ try{ var M=FACE_MODEL_URLS[u]; await f.nets.tinyFaceDetector.loadFromUri(M); await f.nets.faceLandmark68Net.loadFromUri(M); await f.nets.faceRecognitionNet.loadFromUri(M); return; }catch(e){ last=e; } } throw (last||new Error('models')); }
2199  function _faceInit(){
2200    if(_faceReady) return Promise.resolve();
2201    if(_faceLoading) return _faceLoading;
2202    _faceLoading=(async function(){
2203      await _loadFaceLib();
2204      var f=window.faceapi; if(!f) throw new Error('face lib');
2205      await _loadFaceModels(f);
2206      _faceLib=f; _faceReady=true;
2207    })().catch(function(e){ _faceLoading=null; throw e; });
2208    return _faceLoading;
2209  }
2210  function _faceDistJs(a,b){ if(!a||!b||a.length!==b.length) return 99; var s=0; for(var i=0;i<a.length;i++){ var d=a[i]-b[i]; s+=d*d; } return Math.sqrt(s); }
2211  // Detect faces in a canvas \u2192 single clear face + its 128-descriptor, or a clear reason why not.
2212  async function _faceCheck(cv){
2213    try{ await _faceInit(); }catch(e){ return {ok:false,msg:'Face check could not load \u2014 check your internet and try again.'}; }
2214    var f=_faceLib;
2215    var opts=new f.TinyFaceDetectorOptions({inputSize:320,scoreThreshold:0.4});
2216    var all;
2217    try{ all=await f.detectAllFaces(cv,opts).withFaceLandmarks().withFaceDescriptors(); }catch(e){ return {ok:false,msg:'Could not read the photo. Try again in good light.'}; }
2218    if(!all||all.length===0) return {ok:false,msg:'No face detected \u2014 hold the phone in front of your face, remove any cover, and use good light.'};
2219    if(all.length>1) return {ok:false,msg:'More than one person in the photo \u2014 only you should be in the frame.'};
2220    var d=all[0];
2221    if(d.detection.score<0.5) return {ok:false,msg:'Face not clear \u2014 remove mask/cover and face the camera in good light.'};
2222    var box=d.detection.box, area=(box.width*box.height)/((cv.width*cv.height)||1);
2223    if(area<0.04) return {ok:false,msg:'Move closer \u2014 your face is too small in the frame.'};
2224    return {ok:true, descriptor:Array.prototype.slice.call(d.descriptor)};
2225  }
2226  function _cvFrom(vOrImg){ var w=Math.min((vOrImg.videoWidth||vOrImg.naturalWi
2226dth||vOrImg.width),640)||640; var ratio=((vOrImg.videoHeight||vOrImg.naturalHeight||vOrImg.height)/((vOrImg.videoWidth||vOrImg.naturalWidth||vOrImg.width)||1))||1; var h=Math.round(w*ratio)||640; var cv=document.createElement('canvas'); cv.width=w; cv.height=h; cv.getContext('2d').drawImage(vOrImg,0,0,w,h); return cv; }
2227  function _selfieCapText(){ return (window._selfieMode==='register')?'<i class="ti ti-camera"></i> Capture face':'<i class="ti ti-camera"></i> Capture &amp; Punch'; }
2228  function startFaceSetup(){ window._selfieMode='register'; try{ _faceInit(); }catch(_){} openSelfie(); var t=document.getElementById('selfieTitle'); if(t)t.innerHTML='<i class="ti ti-user-scan"></i> Set up your Face ID'; var s=document.getElementById('selfieSub'); if(s)s.textContent='One-time setup. Face the camera in good light, no mask or cover, then tap Capture.'; var b=document.getElementById('selfieCapBtn'); if(b)b.innerHTML='<i class="ti ti-camera"></i> Capture face'; }
2229  // Shared handler for a captured frame (video/image) \u2192 register OR verify+punch.
2230  async function _faceHandleCanvas(cv, photo){
2231    var sec=window._attSec||{}; var mode=window._selfieMode||'punch';
2232    if(mode==='register'){
2233      _selfieMsg('Checking your face\u2026', true);
2234      var chk=await _faceCheck(cv); if(!chk.ok){ _selfieMsg(chk.msg); return; }
2235      _selfieMsg('Saving your Face ID\u2026', true);
2236      var id=currentId||ssGet('ems_id');
2237      try{ var d=await api({action:'emsFaceRegister', employeeId:id, token:ssGet('ems_tok'), descriptor:JSON.stringify(chk.descriptor)});
2238        if(d&&d.status==='success'){ closeSelfie(); toast('Face ID saved \u2713 \u2014 you can punch now'); if(window._attSec) window._attSec.faceRegistered=true; renderPunchCard(); }
2239        else _selfieMsg((d&&d.message)||'Could not save Face ID. Try again.');
2240      }catch(e){ _selfieMsg('Network issue while saving \u2014 try again.'); }
2241      return;
2242    }
2243    // punch mode
2244    if(sec.faceLock){
2245      _selfieMsg('Verifying your face\u2026', true);
2246      var chk2=await _faceCheck(cv); if(!chk2.ok){ _selfieMsg(chk2.msg); return; }
2247      if(sec.faceDescriptor){ var dist=_faceDistJs(chk2.descriptor, sec.faceDescriptor); if(dist>0.52){ _selfieMsg('Face does not match your registered Face ID \u2014 only you can punch.'); return; } }
2248      window._punchFaceDesc=chk2.descriptor;
2249    } else { window._punchFaceDesc=null; }
2250    var id2=currentId||ssGet('ems_id'); closeSelfie(); toast('Verifying\u2026'); sendPunch(id2, photo);
2251  }
2252  function toggleDuty(){ window._onDuty=!window._onDuty; try{ var k='ems_onduty_'+_todayISO(); if(window._onDuty)localStorage.setItem(k,'1'); else localStorage.removeItem(k); }catch(_){} toast(window._onDuty?'On field duty \u2014 you won\u2019t be auto punched-out.':'Field duty turned off.'); window._netOff=0; renderPunchCard(); }
2253  async function netCheck(){
2254    const live=document.querySelector('#punchCard .p2-badge.live'); if(!live){ window._netWarned=false; window._netOff=0; return; }
2255    if(window._onDuty) return;
2256    const id=currentId||ssGet('ems_id'); if(!id) return;
2257    try{ const nw=await api({action:'emsMyIp', employeeId:id});
2258      if(nw&&nw.status==='success'&&nw.lock&&!nw.office&&!nw.offNet){
2259        window._netOff=(window._netOff||0)+1;
2260        if(window._netOff===1){ toast('\u26a0 You left office premises. On field duty? Tap \u201cOn Duty\u201d \u2014 otherwise you\u2019ll be auto punched-out shortly.'); }
2261        else if(window._netOff>=2){ window._netOff=0; try{ const d=await api({action:'emsAttPunch', employeeId:id, token:ssGet('ems_tok'), auto:'true'}); if(d&&d.state==='out'){ toast('Auto punched-out \u2014 you left office premises.'); renderPunchCard(); } }catch(_a){} }
2262      } else { window._netOff=0; window._netWarned=false; }
2263    }catch(_n){}
2264  }
2265  async function loadAttendance(){
2266    const id=currentId||ssGet('ems_id'); if(!id) return;
2267    if(!_attMonth) _attMonth=new Date().toISOString().slice(0,7);
2268    try{
2269      const d=await api({action:'emsAttMy', employeeId:id, month:_attMonth});
2270      if(!d||d.status!=='success'){ var ac0=document.getElementById('attCalendar'); if(ac0)ac0.innerHTML='<div class="empty">Couldn’t load attendance. Tap refresh and try again.</div>'; return; }
2271      loadRegMine();
2272      renderAttCalendar(_attMonth, d.rows||[], d.holidays||[], d.summary||{}, d.weekOffDays||[]);
2273    }catch(e){ var ac=document.getElementById('attCalendar'); if(ac)ac.innerHTML='<div class="empty">Couldn’t load attendance. Check your connection and tap refresh.</div>'; }
2274  }
2275  function attMonth(dir){
2276    const parts=(_attMonth||new Date().toISOString().slice(0,7)).split('-').map(Number);
2277    const dt=new Date(parts[0],parts[1]-1+dir,1);
2278    if(dt>new Date()) return;
2279    _attMonth=dt.getFullYear()+'-'+String(dt.getMonth()+1).padStart(2,'0'); loadAttendance();
2280  }
2281  function renderAttCalendar(ym, rows, holidays, summary, weekOffDays){
2282    window.__attLast={ym:ym,rows:rows,holidays:holidays,summary:summary,weekOffDays:weekOffDays};
2283    var woSet=new Set(weekOffDays||[]);
2284    const p=ym.split('-').map(Number), y=p[0], m=p[1];
2285    const lbl=document.getElementById('acalLabel'); if(lbl) lbl.textContent=new Date(y,m-1,1).toLocaleString('en-IN',{month:'long',year:'numeric'});
2286    const byDate={}; rows.forEach(r=>byDate[r.date]=r);
2287    const holByDate={}; (holidays||[]).forEach(h=>holByDate[h.date]=h);
2288    window.__attByDate=byDate; window.__holByDate=holByDate; window.__attWoSet=woSet;
2289    const today=_todayISO();
2290    const days=new Date(y,m,0).getDate(), startDow=new Date(y,m-1,1).getDay();
2291    let cells=['S','M','T','W','T','F','S'].map(d=>'<div class="acal-dow">'+d+'</div>').join('');
2292    for(let i=0;i<startDow;i++) cells+='<div class="acal-cell empty"></div>';
2293    for(let d=1;d<=days;d++){
2294      const ds=ym+'-'+String(d).padStart(2,'0'); const rec=byDate[ds], hol=holByDate[ds];
2295      let cls='none';
2296      if(rec) cls=(String(rec.status)==='early'?'present':String(rec.status)); // early leave shows as a present (green) day
2297      else if(hol && hol.type==='holiday') cls='holiday';
2298      else if(hol && hol.type==='weekoff') cls='weekoff';
2299      else if(hol && hol.type==='working') cls=(ds<today?'absent':(ds>today?'future':'none'));
2300      else if(woSet.has(new Date(y,m-1,d).getDay())) cls='weekoff';
2301      else if(ds<today) cls='absent';
2302      else if(ds>today) cls='future';
2303      const tip=ds+(rec&&rec.punch_in?(' \u00b7 '+fmt12(rec.punch_in)+(rec.punch_out?('\u2013'+fmt12(rec.punch_out)):'')):'')+(hol?(' \u00b7 '+(hol.name||'Holiday')):'');
2304      var _rg=(window.__regByDate||{})[ds]; var _rgDot=_rg?('<i class="acal-reg '+esc(_rg.status)+'" title="Regularization '+esc(_rg.status)+'"></i>'):'';
2305      var _inc=(rec&&rec.punch_in&&!rec.punch_out&&ds<today)&&!(_rg&&(_rg.status==='pending'||_rg.status==='approved'));
2306      var _warn=_inc?'<span class="acal-warn" title="Punch out missing \u2014 tap to add">!</span>':'';
2307      cells+='<div class="acal-cell '+cls+(_inc?' incomplete':'')+(ds===today?' tdy':'')+'" title="'+esc(tip)+(_inc?' \u00b7 Punch out missing':'')+'" onclick="showAttDay(\''+ds+'\')"><span>'+d+'</span>'+_warn+_rgDot+'</div>';
2308    }
2309    document.getElementById('attCalendar').innerHTML='<div class="acal-grid">'+cells+'</div>';
2310    const s=summary||{};
2311    document.getElementById('attSummary').innerHTML='<div class="att-stats"><div class="att-stat present"><b>'+(s.present||0)+'</b><span>Present</span></div><div class="att-stat half"><b>'+(s.half||0)+'</b><span>Half</span></div><div class="att-stat leave"><b>'+(s.leave||0)+'</b><span>Leave</span></div><div class="att-stat late"><b>'+(s.late||0)+'</b><span>Late</span></div></div>';
2312  }
2313  function _hmNum(s){ var m=String(s||'').match(/(\d{1,2}):(\d{2})/); return m?((+m[1])*60+(+m[2])):-1; }
2314  function _wmFromRec(rec){ if(!rec) return 0; var im=_hmNum(rec.punch_in),om=_hmNum(rec.punch_out); if(im>=0&&om>=0&&om>=im){ var w=om-im-(Number(rec.break_mins)||0); return w>0?w:0; } return Number(rec.work_mins)||0; }
2315  function showAttDay(ds){
2316    var rec=(window.__attByDate||{})[ds], hol=(window.__holByDate||{})[ds], woSet=window.__attWoSet||new Set();
2317    var dt=new Date(ds+'T00:00:00'); var nice=isNaN(+dt)?ds:dt.toLocaleDateString('en-IN',{weekday:'long',day:'numeric',month:'long',year:'numeric'});
2318    var today=_todayISO();
2319    var st='';
2320    if(rec) st=String(rec.status||'');
2321    else if(hol&&hol.type==='holiday') st='holiday';
2322    else if(hol&&hol.type==='weekoff') st='weekoff';
2323    else if(woSet.has(dt.getDay())) st='weekoff';
2324    else if(ds<today) st='absent';
2325    else if(ds>today) st='upcoming';
2326    var stLabel={present:'Present',half:'Half Day',early:'Early Leave',leave:'On Leave',holiday:'Holiday',weekoff:'Week Off',absent:'Absent',upcoming:'Upcoming'}[st]||'No record';
2327    var stCol={present:'#7fe0a8',half:'#f3d27a',early:'#7fe0a8',leave:'#8fb6f0',holiday:'#c6a3ff',absent:'#ff9c8b',weekoff:'#a9b6c9',upcoming:'#7c889a'}[st]||'#a9b6c9';
2328    var _inc=!!(rec&&rec.punch_in&&!rec.punch_out&&ds<today);
2329    var r=[];
2330    if(rec&&rec.punch_in) r.push(['Punch In',fmt12(rec.punch_in)]);
2331    if(rec&&rec.punch_out) r.push(['Punch Out',fmt12(rec.punch_out)]);
2332    else if(_inc) r.push(['Punch Out','__MISS__']);
2333    if(rec){ var wm=_wmFromRec(rec); if(wm>0) r.push(['Work Hours',Math.floor(wm/60)+'h '+(wm%60)+'m']); }
2334    if(rec&&Number(rec.late_mins)>0) r.push(['Late By',Number(rec.late_mins)+' min']);
2335    if(hol&&hol.name) r.push(['Note',hol.name]);
2336    else if(rec&&rec.note) r.push(['Note',rec.note]);
2337    var _warnBlk=_inc?'<div class="attd-warn"><i class="ti ti-alert-triangle-filled"></i><div><b>Punch out missing</b><span>You punched in but didn\u2019t punch out on this day. Add your correct punch-out time below \u2014 it goes to HR for approval.</span></div></div>':'';
2338    var body=_warnBlk+(r.length?r.map(function(x){var miss=(x[1]==='__MISS__');return '<div class="attd-row"><span>'+esc(x[0])+'</span><b'+(miss?' style="color:#f3902f"':'')+'>'+(miss?'<i class="ti ti-alert-triangle-filled" style="font-size:12.5px;vertical-align:-1px"></i> Missing':esc(String(x[1])))+'</b></div>';}).join(''):'<div class="attd-empty">'+(st==='upcoming'?'This day is yet to come.':((st==='present'||st==='half')?'No punch time recorded for this day.':'No attendance recorded for this day.'))+'</div>');
2339    // Regularization: show any request status for this day, and offer "Add Punch" on a missed/absent past day.
2340    var reg=(window.__regByDate||{})[ds];
2341    var regBlk='';
2342    if(reg){ var rc={pending:'#f3b04f',approved:'#7fe0a8',rejected:'#ff9c8b'}[reg.status]||'#a9b6c9'; var rl={pending:'Pending with HR',approved:'Approved',rejected:'Rejected'}[reg.status]||reg.status;
2343      regBlk='<div class="reg-status" style="border-color:'+rc+'55"><div class="reg-status-h"><i class="ti ti-clock-hour-4" style="color:'+rc+'"></i> Regularization <span style="color:'+rc+';font-weight:800">'+esc(rl)+'</span></div><div class="reg-status-b">Requested: '+(reg.punchIn?('In '+fmt12(reg.punchIn)):'')+(reg.punchOut?((reg.punchIn?' · ':'')+'Out '+fmt12
2343(reg.punchOut)):'')+(reg.reason?('<br><span class="reg-reason">“'+esc(reg.reason)+'”</span>'):'')+(reg.status==='rejected'&&reg.decisionNote?('<br><span class="reg-reason">HR: '+esc(reg.decisionNote)+'</span>'):'')+'</div></div>';
2344    }
2345    // Add Punch / correction stays available on ANY past-or-today day (Petpooja-style) — the employee
2346    // can always request a correction; hidden only on future days or when a request is already pending.
2347    var canReg=(ds<=today)&&!(reg&&reg.status==='pending');
2348    var hasPunch=!!(rec&&(rec.punch_in||rec.punch_out));
2349    var addBtn=canReg?('<button class="reg-addbtn'+(_inc?' warn':'')+'" onclick="openRegApply(\''+ds+'\')"><i class="ti ti-'+(_inc?'clock-plus':(hasPunch?'edit':'plus'))+'"></i> '+(_inc?'Add Punch Out':(hasPunch?'Edit / Correct Punch':'Add Punch &amp; Request'))+'</button>'):'';
2350    var m=document.getElementById('attDayModal');
2351    if(!m){ m=document.createElement('div'); m.id='attDayModal'; m.className='attd-modal'; m.addEventListener('click',function(ev){ if(ev.target===m) m.classList.remove('open'); }); document.body.appendChild(m); }
2352    m.innerHTML='<div class="attd-card"><div class="attd-head"><div><div class="attd-date">'+esc(nice)+'</div><span class="attd-pill" style="color:'+stCol+';border-color:'+stCol+'55">'+esc(stLabel)+'</span></div><button class="attd-x" onclick="document.getElementById(\'attDayModal\').classList.remove(\'open\')"><i class="ti ti-x"></i></button></div><div class="attd-body">'+body+regBlk+addBtn+'</div></div>';
2353    m.classList.add('open');
2354  }
2355  async function loadRegMine(){
2356    try{ var id=currentId||ssGet('ems_id'); if(!id) return; var d=await api({action:'emsRegMine', employeeId:id}); if(d&&d.status==='success'){ var by={}; (d.rows||[]).forEach(function(x){ if(!by[x.date]||x.status==='pending') by[x.date]=x; }); window.__regByDate=by; try{ var _al=window.__attLast; var _ap=document.getElementById('attendance'); if(_al && _ap && _ap.classList.contains('show')) renderAttCalendar(_al.ym,_al.rows,_al.holidays,_al.summary,_al.weekOffDays); }catch(_){} } }catch(_){}
2357  }
2358  function openRegApply(ds){
2359    var dt=new Date(ds+'T00:00:00'); var nice=isNaN(+dt)?ds:dt.toLocaleDateString('en-IN',{weekday:'short',day:'numeric',month:'long',year:'numeric'});
2360    var rec=(window.__attByDate||{})[ds]||{};
2361    var _hm=function(v){ var mm=String(v||'').match(/(\d{1,2}):(\d{2})/); return mm?(String(+mm[1]).padStart(2,'0')+':'+mm[2]):''; };
2362    var pin=_hm(rec.punch_in)||'09:30', pout=_hm(rec.punch_out);
2363    var isEdit=!!(rec.punch_in||rec.punch_out);
2364    var incOut=!!(rec.punch_in && !rec.punch_out && ds < _todayISO());
2365    var m=document.getElementById('regApplyModal');
2366    if(!m){ m=document.createElement('div'); m.id='regApplyModal'; m.className='attd-modal'; m.addEventListener('click',function(ev){ if(ev.target===m) m.classList.remove('open'); }); document.body.appendChild(m); }
2367    m.innerHTML='<div class="attd-card"><div class="attd-head"><div><div class="attd-date">'+(isEdit?'Correct Punch':'Add Punch')+'</div><span class="attd-sub">'+esc(nice)+'</span></div><button class="attd-x" onclick="document.getElementById(\'regApplyModal\').classList.remove(\'open\')"><i class="ti ti-x"></i></button></div>'
2368      +'<div class="attd-body"><div class="reg-note"><i class="ti ti-info-circle"></i> '+(incOut?'You punched in but didn\u2019t punch out. Enter your correct punch-out time and a reason \u2014 it goes to HR for approval.':(isEdit?'Fix a wrong or missing punch time. Enter the correct times and a reason \u2014 your request goes to HR for approval.':'Forgot to punch or faced a server issue? Enter the correct time and a reason. Your request goes to HR for approval.'))+'</div>'
2369      +'<label class="reg-lbl">Punch In <span style="color:#ff9c8b">*</span></label><input type="time" id="regIn" class="reg-inp" value="'+esc(pin)+'">'
2370      +'<label class="reg-lbl">Punch Out '+(incOut?'<span style="color:#ff9c8b">*</span>':'<span class="reg-opt">(optional)</span>')+'</label><input type="time" id="regOut" class="reg-inp"'+(pout?(' value="'+esc(pout)+'"'):'')+(incOut?' autofocus':'')+'>'
2371      +'<label class="reg-lbl">Reason <span style="color:#ff9c8b">*</span></label><textarea id="regReason" class="reg-inp" rows="2" placeholder="e.g. Forgot to punch in / server issue"></textarea>'
2372      +'<div class="reg-actions"><button class="reg-cancel" onclick="document.getElementById(\'regApplyModal\').classList.remove(\'open\')">Cancel</button><button class="reg-submit" onclick="submitReg(\''+ds+'\')"><i class="ti ti-send"></i> Send Request</button></div></div></div>';
2373    m.classList.add('open');
2374  }
2375  async function submitReg(ds){
2376    var id=currentId||ssGet('ems_id');
2377    var pin=(document.getElementById('regIn')||{}).value||'', pout=(document.getElementById('regOut')||{}).value||'', reason=((document.getElementById('regReason')||{}).value||'').trim();
2378    if(!pin&&!pout){ toast('Enter a punch time'); return; }
2379    if(!reason){ toast('Please add a reason'); return; }
2380    var btn=document.querySelector('#regApplyModal .reg-submit'); if(btn){ btn.disabled=true; btn.innerHTML='Sending…'; }
2381    try{
2382      var d=await api({action:'emsRegApply', employeeId:id, token:ssGet('ems_tok'), date:ds, punchIn:pin, punchOut:pout, reason:reason});
2383      if(d&&d.status==='success'){ toast('Request sent to HR ✓'); var rm=document.getElementById('regApplyModal'); if(rm)rm.classList.remove('open'); var am=document.getElementById('attDayModal'); if(am)am.classList.remove('open'); await loadRegMine(); loadAttendance(); }
2384      else { toast((d&&d.message)||'Could not send'); if(btn){ btn.disabled=false; btn.innerHTML='<i class="ti ti-send"></i> Send Request'; } }
2385    }catch(e){ toast('Could not send'); if(btn){ btn.disabled=false; btn.innerHTML='<i class="ti ti-send"></i> Send Request'; } }
2386  }
2387  async function loadLeave(){
2388    const id=currentId||ssGet('ems_id'); if(!id) return;
2389    try{
2390      const res=await Promise.all([ api({action:'emsLeaveInit', employeeId:id}), api({action:'emsLeaveMine', employeeId:id}) ]);
2391      const init=res[0], mine=res[1];
2392      if(init&&init.status==='success'){ _leaveTypes=init.types||[]; renderLeaveBalances(init.types||[], init.balances||[]); fillLeaveTypeSelect(); }
2393      if(mine&&mine.status==='success') renderMyLeaves(mine.rows||[]);
2394    }catch(e){ var lb=document.getElementById('leaveBalances'); if(lb)lb.innerHTML='<div class="empty">Couldn’t load leave. Check your connection and tap refresh.</div>'; }
2395    loadHolidays();
2396  }
2397  function renderLeaveBalances(types, balances){
2398    const byType={}; balances.forEach(b=>byType[b.type_code]=b);
2399    const el=document.getElementById('leaveBalances'); if(!el) return;
2400    el.innerHTML='<div class="lv-bals">'+types.map(function(t){ const b=byType[t.code]||{allotted:t.annual_quota||0,used:0,pending:0}; const all=Number(b.allotted!=null?b.allotted:(t.annual_quota||0))||0; const avail=(all>0)?(all-(Number(b.used)||0)-(Number(b.pending)||0)):'\u221e'; const paid=(String(t.paid)!=='false'); const tag='<span class="lv-tag '+(paid?'paid':'unpaid')+'">'+(paid?'PAID':'UNPAID')+'</span>'; return '<div class="lv-bal"><div class="lv-bal-k">'+esc(t.name||t.code)+tag+'</div><div class="lv-bal-v">'+avail+'</div><div class="lv-bal-s">'+(all>0?('of '+all+' \u00b7 used '+(Number(b.used)||0)):'unlimited')+'</div></div>'; }).join('')+'</div>';
2401  }
2402  async function loadHolidays(){
2403    const host=document.getElementById('leaveHolidays'); if(!host) return;
2404    try{
2405      const yr=new Date().getFullYear();
2406      const d=await api({action:'emsHolidaysYear', year:yr});
2407      const rows=((d&&d.rows)||[]).filter(function(h){ return String(h.type||'holiday')==='holiday'; });
2408      const head='<div class="accwrap-head" style="margin-top:16px"><i class="ti ti-calendar-star"></i> Company Holidays '+yr+'</div><div class="hol-note"><i class="ti ti-info-circle"></i> All company holidays are paid and applied automatically. No leave request is required, and your leave balance is not affected.</div>';
2409      if(!rows.length){ host.innerHTML=head+'<div class="empty">Holiday calendar not published yet.</div>'; return; }
2410      const today=_todayISO();
2411      host.innerHTML=head+'<div class="hol-grid">'+rows.map(function(h){ const dd=new Date(String(h.date)+'T00:00:00'); const ok=!isNaN(+dd); const past=String(h.date)<today; const wd=ok?dd.toLocaleDateString('en-IN',{weekday:'long'}):''; const dnum=ok?dd.getDate():'\u2014'; const mon=ok?dd.toLocaleDateString('en-IN',{month:'short'}):''; return '<div class="hol-card'+(past?' past':'')+'"><div class="hol-date"><b>'+dnum+'</b><span>'+esc(mon)+'</span></div><div class="hol-info"><b>'+esc(h.name||'Holiday')+'</b><span>'+esc(wd)+' \u00b7 Paid holiday</span></div></div>'; }).join('')+'</div>';
2412    }catch(e){ host.innerHTML=''; }
2413  }
2414  function fillLeaveTypeSelect(){ const s=document.getElementById('lvType'); if(s&&_leaveTypes.length) s.innerHTML=_leaveTypes.map(t=>'<option value="'+esc(t.code)+'">'+esc(t.name||t.code)+'</option>').join(''); }
2415  async function applyLeave(){
2416    const id=currentId||ssGet('ems_id');
2417    const type=document.getElementById('lvType').value, from=document.getElementById('lvFrom').value, to=document.getElementById('lvTo').value||document.getElementById('lvFrom').value, half=document.getElementById('lvHalf').checked, reason=document.getElementById('lvReason').value.trim();
2418    if(!from){ toast('Choose a date'); return; }
2419    if(!reason){ toast('Add a reason'); return; }
2420    try{
2421      const d=await api({action:'emsLeaveApply', employeeId:id, token:ssGet('ems_tok'), typeCode:type, fromDate:from, toDate:to, halfDay:half?'true':'false', reason:reason});
2422      if(d&&d.status==='success'){ toast('Leave applied \u2713'); document.getElementById('lvReason').value=''; document.getElementById('lvHalf').checked=false; loadLeave(); }
2423      else toast((d&&d.message)||'Apply failed');
2424    }catch(e){ toast('Network issue \u2014 please try again.'); }
2425  }
2426  async function cancelLeave(lid){
2427    if(!confirm('Cancel this leave request?')) return;
2428    const id=currentId||ssGet('ems_id');
2429    try{
2430      const d=await api({action:'emsLeaveCancel', employeeId:id, id:lid});
2431      if(d&&d.status==='success'){ toast('Cancelled');
2431 loadLeave(); } else toast((d&&d.message)||'Failed');
2432    }catch(e){ toast('Network issue \u2014 please try again.'); }
2433  }
2434  function renderMyLeaves(rows){
2435    const el=document.getElementById('myLeaves'); if(!el) return;
2436    if(!rows.length){ el.innerHTML='<div class="accwrap-head">My leaves</div><div class="empty">No leave requests yet.</div>'; return; }
2437    el.innerHTML='<div class="accwrap-head">My leaves</div>'+rows.map(function(r){ const s=String(r.status||'pending'); const badge=s==='approved'?'<span class="cs resolved">Approved</span>':(s==='rejected'?'<span class="cs" style="background:rgba(224,83,63,.15);color:#ff9c8b">Rejected</span>':(s==='cancelled'?'<span class="cs">Cancelled</span>':'<span class="cs open">Pending</span>')); const cancel=(s==='pending')?'<button class="dl ghost" style="margin-top:8px;padding:6px 12px" onclick="cancelLeave(\''+esc(r.id)+'\')"><i class="ti ti-x"></i> Cancel</button>':''; const dn=r.decision_note?'<div class="creply"><b>HR:</b> '+esc(r.decision_note)+'</div>':''; return '<div class="cmp-item"><div class="ctop"><b>'+esc(r.type_code||'')+' \u00b7 '+esc(String(r.days||''))+'d</b>'+badge+'</div><div style="font-size:12.5px;color:var(--muted)">'+esc(r.from_date||'')+(r.to_date&&r.to_date!==r.from_date?(' \u2192 '+esc(r.to_date)):'')+'</div>'+(r.reason?'<div style="margin-top:4px">'+esc(r.reason)+'</div>':'')+dn+cancel+'</div>'; }).join('');
2438  }
2439
2440  /* ---- tabs ---- */
2441  async function loadMyLetters(){
2442    const box=document.getElementById('myLettersList'); if(!box)return;
2443    box.innerHTML='<div class="empty">Loading\u2026</div>';
2444    try{
2445      const d=await api({action:'emsMyLetters', employeeId:currentId||ssGet('ems_id'), token:ssGet('ems_tok')});
2446      const rows=(d&&d.rows)||[];
2447      if(!rows.length){ box.innerHTML='<div class="empty">No letters issued yet.</div>'; return; }
2448      box.innerHTML=rows.map(function(r){ return '<div class="drow"><div class="ic"><i class="ti ti-file-text"></i></div><div class="dt"><b>'+esc(r.letterName||'Letter')+'</b><span>'+esc(r.refNo||'')+' \u00b7 '+esc(r.generatedAt||'')+'</span></div>'+(r.pdfLink?'<a class="dl" href="'+esc(r.pdfLink)+'" target="_blank"><i class="ti ti-download"></i> Download</a>':'<button class="dl lock" disabled><i class="ti ti-lock"></i> N/A</button>')+'</div>'; }).join('');
2449    }catch(e){ box.innerHTML='<div class="empty">Could not load letters.</div>'; }
2450  }
2451  function typeName(name){ var el=document.getElementById('eName'); if(!el)return; name=String(name||'Employee'); clearInterval(window._nameTimer); el.classList.add('type-cursor'); el.textContent=''; var i=0; window._nameTimer=setInterval(function(){ if(i<=name.length){ el.textContent=name.slice(0,i); i++; } else { clearInterval(window._nameTimer); } }, 65); }
2452  function showTab(id,btn){
2453    // Attendance & Leave are blocked entirely when attendance is turned off for this employee — even a stray
2454    // click or a direct call can never open them; they land on the Dashboard instead.
2455    if((id==='attendance'||id==='leave') && window._attEnabled===false){ var _dbt=document.querySelector('.tab[data-tab="dashboard"]'); if(_dbt){ return showTab('dashboard',_dbt); } }
2456    [...document.querySelectorAll('.tab')].forEach(t=>t.classList.remove('active')); if(btn) btn.classList.add('active'); [...document.querySelectorAll('.panel')].forEach(p=>p.classList.remove('show')); document.getElementById(id).classList.add('show'); window._curTab=id; ping(id); if(id==='notice'){ setTimeout(function(){ try{ _noticeMarkSeen(); }catch(_){} }, 2500); } var _bk=document.getElementById('backBtn'); if(_bk)_bk.style.display=(id==='dashboard'?'none':''); if(id==='letters') loadMyLetters(); else if(id==='attendance') loadAttendance(); else if(id==='leave') loadLeave(); else if(id==='goals') loadGoals(); else if(id==='tasks') loadTasks(); else if(id==='performance') loadPerformance(); else if(id==='rewards') loadRewards(); else if(id==='learning') loadCourses(); else if(id==='engagement') loadEngagement(); else if(id==='directory') loadDirectory(); else if(id==='orgchart') loadOrgChart(); else if(id==='timeline') loadTimeline(); else if(id==='kb') loadKB(); else if(id==='meetings') loadMeetings(); else if(id==='forms') loadForms(); else if(id==='messages') loadMessages(); else if(id==='dashboard'){ loadDashboard(); if(window._empName) typeName(window._empName); } }
2457
2458  /* ---- refresh + session ---- */
2459  async function refresh(silent){
2460    const id=currentId||ssGet('ems_id'); if(!id)return;
2461    const b=document.getElementById('refBtn'); if(!silent)b.querySelector('i').classList.add('spin');
2462    try{ const d=await api({action:'emsGetEmployee', employeeId:id, token:ssGet('ems_tok'), lite: silent?'1':''});
2463      if(d.status==='session_replaced'){ ssDel('ems_id');ssDel('ems_tok'); location.reload(); return; }
2464      if(d.status==='success'){ currentId=id; load(d); }
2465    }catch(e){}
2466    setTimeout(()=>b.querySelector('i').classList.remove('spin'),600);
2467  }
2468  function ping(activity, hb){ const id=currentId||ssGet('ems_id'); if(!id)return; var pg=activity||window._curTab||'dashboard'; api({action:'emsPing', employeeId:id, name:(BUNDLE&&BUNDLE.employee||{}).name||'', activity:pg, page:pg, hb: hb?'1':''}).catch(()=>{}); }
2469  // Background refresh of the full employee bundle: every 2 min, and only while the person is actually using
2470  // the app (visible + touched within 3 min). Was every 35s regardless — the heaviest read in the portal,
2471  // multiplied by every open employee app. Any tab switch / manual refresh still loads fresh data instantly.
2472  setInterval(()=>{ if(document.visibilityState!=='visible' || !ssGet('ems_id')) return; if(Date.now()-(window._lastAct||0)>3*60*1000) return; refresh(true); }, 120000);
2473  window._curTab=window._curTab||'dashboard';
2474  // "Live" must mean ACTUALLY using the app right now. Heartbeats stop the moment the app goes to the
2475  // background, and also when the person hasn't touched/scrolled/typed for 3 minutes — so a login left
2476  // open (or a backgrounded tab) drops off the HR "Live now" list within ~2 minutes instead of showing
2477  // live forever. Coming back to the app makes them live again instantly.
2478  window._lastAct=Date.now();
2479  ['pointerdown','keydown','touchstart','scroll'].forEach(function(_ev){ document.addEventListener(_ev,function(){ window._lastAct=Date.now(); },{passive:true}); });
2480  document.addEventListener('visibilitychange',function(){ if(!document.hidden){ window._lastAct=Date.now(); try{ ping(window._curTab||'dashboard', true); }catch(_){} } });
2481  setInterval(function(){ if(document.hidden) return; if(Date.now()-(window._lastAct||0)>3*60*1000) return; ping(window._curTab||'dashboard', true); }, 45000);
2482  setTimeout(()=>{ try{ ping(window._curTab||'dashboard'); }catch(_){} }, 1500);
2483  (function(){ const id=ssGet('ems_id'); if(id){ currentId=id; _mvsSaveSession(id, ssGet('ems_tok'), id); document.getElementById('login').classList.add('hidden'); document.getElementById('app').style.display='block'; refresh(true); } })();
2484
2485  function renderPolicies(d){
2486    const list=d.policies||[];
2487    if(!list.length){document.getElementById('policyList').innerHTML='<div class="empty">No policies posted yet.</div>';return}
2488    document.getElementById('policyList').innerHTML=list.map(p=>{
2489      const btn=p.pdfLink?'<div style="margin-top:10px"><a class="dl" href="'+esc(p.pdfLink)+'" target="_blank"><i class="ti ti-download"></i> Download PDF</a></div>':'';
2490      return accItem('ti-book', esc(p.title), 'Tap to read', '<div class="ptext">'+esc(p.content||'')+'</div>'+btn);
2491    }).join('');
2492  }
2493  function renderWarnings(d){
2494    const list=d.warnings||[]; const box=document.getElementById('warnTop'); if(!box)return; if(!list.length){box.innerHTML='';return}
2495    box.innerHTML='<div class="warncard"><div class="sectitle" style="color:#ff9c8b;margin-bottom:10px"><i class="ti ti-alert-triangle"></i> Disciplinary Warning'+(list.length>1?'s ('+list.length+')':'')+'</div>'+list.slice().reverse().map(w=>'<div class="cmp-item" style="border-color:rgba(224,83,63,.5);background:rgba(224,83,63,.1)"><div class="ctop"><b style="color:#ffb3a6">'+esc(w.type)+'</b><span class="hint">'+esc(fmtDT(w.time))+'</span></div><div style="font-size:13px;color:var(--txt)">'+esc(w.reason||'')+'</div><button class="dl ghost" style="margin-top:8px" onclick=\'genWarning('+JSON.stringify(JSON.stringify(w))+')\'><i class="ti ti-download"></i> Warning Letter PDF</button></div>').join('')+'</div>';
2496  }
2497  function renderResignation(d){
2498    const list=d.resignations||[]; const pending=list.find(r=>String(r.status).toLowerCase()==='pending'); const box=document.getElementById('resignBox');
2499    if(list.length){
2500      box.innerHTML=list.slice().reverse().map(r=>{const s=String(r.status).toLowerCase();const badge=s==='pending'?'<span class="cs open">Pending</span>':(s==='accepted'||s==='relieved'||s==='approved'||s==='settled'?'<span class="cs resolved">'+esc(r.status)+'</span>':'<span class="cs">'+esc(r.status)+'</span>');return '<div class="cmp-item"><div class="ctop"><b>Resignation</b>'+badge+'</div><div style="font-size:12.5px;color:var(--muted)">Last working day: '+esc(fmtDate(r.lastWorkingDay))+'</div><div style="margin-top:4px">'+esc(r.reason||'')+'</div>'+(s==='pending'?'<div class="creply" style="color:var(--warn)"><b>Status:</b> Your resignation is pending HR department review. You will be notified once HR responds.</div>':'')+(r.settlement?'<div class="creply"><b>
2500HR:</b> '+esc(r.settlement)+'</div>':'')+'</div>';}).join('');
2501      if(!pending) box.innerHTML+=resignForm();
2502    } else box.innerHTML=resignForm();
2503  }
2504  function resignForm(){ return '<label style="margin-top:6px;display:block">Last working day</label><input class="inp" id="rsDate" type="date"><label style="margin-top:8px;display:block">Reason</label><textarea id="rsReason" placeholder="Reason for resignation…"></textarea><button class="btn danger" style="margin-top:10px;background:linear-gradient(180deg,#e8786a,var(--red));color:#fff" onclick="submitResign()">Submit Resignation</button>'; }
2505  async function submitResign(){ const dt=document.getElementById('rsDate').value, r=document.getElementById('rsReason').value.trim(); if(!r){toast('Enter a reason');return} if(!confirm('Submit your resignation? HR will be notified.'))return; const d=await api({action:'emsResign', employeeId:currentId, name:(BUNDLE.employee||{}).name||'', reason:r, lastWorkingDay:dt}); if(d.status==='success'){toast('Resignation submitted');refresh(true);} else toast(d.message||'Failed'); }
2506  function genWarning(jsonStr){ const w=JSON.parse(jsonStr); const e=BUNDLE.employee||{}, c=company(); const doc=jp(); letterHead(doc,c);
2507    doc.setTextColor(192,57,43); doc.setFont('helvetica','bold'); doc.setFontSize(14); doc.text(String(w.type||'WARNING').toUpperCase(),297,128,{align:'center'});
2508    doc.setTextColor(90,90,90); doc.setFont('helvetica','normal'); doc.setFontSize(10); doc.text('Date: '+fmtDate(w.time),555,150,{align:'right'}); doc.text('Ref: MVS/WARN/'+(e.employeeId||''),40,150);
2509    doc.setTextColor(20,20,20); doc.setFontSize(11); doc.text('To: '+(e.name||'')+' ('+(e.employeeId||'')+')',40,180);
2510    const body='This letter is to formally place on record a '+(w.type||'warning')+' issued to you for the following reason:\n\n'+(w.reason||'')+'\n\nYou are advised to take corrective action immediately. Repeated instances may lead to further disciplinary action as per company policy.';
2511    const lines=doc.splitTextToSize(body,515); doc.setFontSize(11); doc.text(lines,40,206);
2512    sigBlock(doc,c,Math.min(206+lines.length*15+40,690)); letterFoot(doc,c); doc.save((e.name||'emp').replace(/\s+/g,'_')+'_warning.pdf');
2513  }
2514
2515  /* ================= PDF GENERATION ================= */
2516  function logoImg(){ return "data:image/jpeg;base64,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"; }
2517  function company(){ const c=(BUNDLE&&BUNDLE.config)||{}; return {name:c.companyName||'MVS Foundation', addr:c.companyAddress||'', email:c.companyEmail||'', phone:c.companyPhone||'', web:c.website||'', hr:c.hrName||'HR Department', hrDes:c.hrDesignation||'Human Resources'}; }
2518  function jp(){ return new window.jspdf.jsPDF({unit:'pt', format:'a4'}); }
2519  function letterHead(doc, c){
2520    doc.setFillColor(19,41,75); doc.rect(0,0,595,90,'F');
2521    try{ doc.addImage(logoImg(),'JPEG',40,22,46,46); }catch(e){}
2522    doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(18); doc.text(c.name,100,46);
2523    doc.setFont('helvetica','normal'); doc.setFontSize(9); doc.setTextColor(220,210,180);
2524    doc.text([c.addr, [c.email,c.phone,c.web].filter(Boolean).join('  |  ')].filter(Boolean).join('\n'),100,60);
2525    doc.setDrawColor(202,165,74); doc.setLineWidth(2); doc.line(40,90,555,90);
2526  }
2527  function letterFoot(doc,c){
2528    doc.setDrawColor(220,220,220); doc.setLineWidth(.5); doc.line(40,770,555,770);
2529    doc.setFontSize(8); doc.setTextColor(150,150,150);
2530    doc.text(c.name+'  •  This is a system-generated document from the MVS Employee Portal.',40,784);
2531  }
2532  function sigBlock(doc,c,y,stampKey){
2533    const cfg=(BUNDLE&&BUNDLE.config)||{};
2534    doc.setTextColor(30,30,30); doc.setFont('helvetica','normal'); doc.setFontSize(11);
2535    doc.text('For '+c.name+',',40,y);
2536    // One official stamp / seal, placed just below the sign-off line with its true aspect ratio
2537    // (no stretching). Prefers the company seal, then the HR signature.
2538    let placedH=0;
2539    try{
2540      const st=cfg[stampKey]||cfg.stampDirector||cfg.signatureHR||cfg.signature||cfg.stamp;
2541      if(st&&String(st).indexOf('data:')===0){
2542        const props=doc.getImageProperties(st); const maxW=118, maxH=78;
2543        const scale=Math.min(maxW/props.width, maxH/props.height);
2544        const w=props.width*scale, h=props.height*scale;
2545        const fmt=String(st).indexOf('image/png')>0?'PNG':'JPEG';
2546        doc.addImage(st,fmt,40,y+10,w,h); placedH=h;
2547      }
2548    }catch(_){}
2549    const ny=y+(placedH>0?placedH+24:36);
2550    doc.setFont('helvetica','bold'); doc.setTextColor(30,30,30); doc.setFontSize(11); doc.text(c.hr||'HR Department', 40, ny);
2551    doc.setFont('helvetica','normal'); doc.setFontSize(10); doc.setTextColor(90,90,90); doc.text(c.hrDes||'Human Resources',40,ny+14);
2552  }
2553  function salaryRows(e){
2554    const earn=[['Basic',numv(e.basic)],['HRA',numv(e.hra)],['Other Allowance',numv(e.allowance)]];
2555    const ded=[['Provident Fund (PF)',numv(e.pf)],['ESI',numv(e.esi)],['Professional Tax',numv(e.profTax)],['TDS',numv(e.tds)]];
2556    const gross=earn.reduce((a,b)=>a+b[1],0), totded=ded.reduce((a,b)=>a+b[1],0);
2557    return {earn,ded,gross,totded,net:gross-totded};
2558  }
2559  function moneyTable(doc, x, y, title, rows, total, totalLabel){
2560    doc.setFillColor(244,247,252); doc.rect(x,y,255,24,'F');
2561    doc.setTextColor(19,41,75); doc.setFont('helvetica','bold'); doc.setFontSize(11); doc.text(title,x+10,y+16);
2562    doc.setFont('helvetica','normal'); doc.setFontSize(10); doc.setTextColor(40,40,40); let yy=y+42;
2563    rows.forEach(r=>{ doc.text(String(r[0]),x+10,yy); doc.text(rupee(r[1]),x+245,yy,{align:'right'}); yy+=20; });
2564    doc.setDrawColor(210,210,210); doc.line(x,yy-12,x+255,yy-12);
2565    doc.setFont('helvetica','bold'); doc.text(totalLabel,x+10,yy+2); doc.text(rupee(total),x+245,yy+2,{align:'right'});
2566    return yy+18;
2567  }
2568  function openKRA(){ const k=(BUNDLE&&BUNDLE.employee||{}).kra||''; document.getElementById('kraBody').textContent = k ? k : 'Your Key Responsibility Areas have not been set by HR yet. Please check back later.'; document.getElementById('kraModal').style.display='flex'; }
2569  function genKRA(){
2570    try{
2571      if(!BUNDLE){return} const e=BUNDLE.employee||{}, c=company(); const doc=jp();
2572      letterHead(doc,c); doc.setTextColor(30,30,30);
2573      const ref='MVS/KRA/'+(e.employeeId||'')+'/'+new Date().getFullYear();
2574      doc.setFontSize(10); doc.setTextColor(90,90,90); doc.text('Ref: '+ref,40,118); doc.text('Date: '+fmtDate(new Date()),555,118,{align:'right'});
2575      doc.setTextColor(20,20,20); doc.setFont('helvetica','bold'); doc.setFontSize(13); doc.text('KEY RESPONSIBILITY AREAS (KRA)',297,150,{align:'center'});
2576      doc.setFont('helvetica','normal'); doc.setFontSize(11); doc.setTextColor(30,30,30);
2577      doc.text('Employee:',40,186); doc.setFont('helvetica','bold'); doc.text(String(e.name||''),120,186);
2578      doc.setFont('helvetica','normal'); doc.text('Employee ID: '+(e.employeeId||''),380,186);
2579      var y=214; var rows=[['Department',e.department||'-'],['Designation / Role',e.designation||'-'],['Employment Type',e.employmentType||'-'],['Reporting Manager',BUNDLE.reportingManager||e.reportingManager||'-'],['Work Location',e.workLocation||'-'],['Assigned Work',e.assignedWork||'-']];
2580      doc.setFontSize(10.5);
2581      rows.forEach(function(r){ const vlines=doc.splitTextToSize(String(r[1]),330); const rowH=Math.max(27, vlines.length*13.5+12); doc.setTextColor(120,120,120); doc.setFont('helvetica','normal'); doc.text(String(r[0]),40,y); doc.setTextColor(25,25,25); doc.setFont('helvetica','bold'); doc.text(vlines,210,y); doc.setDrawColor(232,232,232); doc.setLineWidth(.5); doc.line(40,y+rowH-9,555,y+rowH-9); y+=rowH; });
2582      y+=16; doc.setFont('helvetica','bold'); doc.setFontSize(12); doc.setTextColor(19,41,75); doc.text('Key Responsibility Areas',40,y);
2583      doc.setDrawColor(202,165,74); doc.setLineWidth(1.6); doc.line(40,y+7,205,y+7); y+=28;
2584      doc.setFont('helvetica','normal'); doc.setFontSize(11); doc.setTextColor(45,45,45);
2585      var kra=String(e.kra||'').trim() || 'Your Key Responsibility Areas have not been set by HR yet.';
2586      doc.text(doc.splitTextToSize(kra,515),40,y);
2587      letterFoot(doc,c); doc.save(String(e.name||'employee').replace(/\s+/g,'_')+'_KRA.pdf');
2588    }catch(err){ toast('Could not generate KRA. Please try again.'); }
2589  }
2590  function qrDataUrl(text){
2591    return new Promise(function(res){ try{ const img=new Image(); img.crossOrigin='anonymous'; img.onload=function(){ try{ const cv=document.createElement('canvas'); cv.width=img.width; cv.height=img.height; cv.getContext('2d').drawImage(img,0,0); res(cv.toDataURL('image/png')); }catch(_){ res(''); } }; img.onerror=function(){ res(''); }; img.src='https://api.qrserver.com/v1/create-qr-code/?size=220x220&margin=0&data='+encodeURIComponent(text); }catch(_){ res(''); } });
2592  }
2593  async function genICard(){
2594    try{
2595      const e=BUNDLE.employee||{}, c=company();
2596      const doc=new window.jspdf.jsPDF({unit:'pt',format:[340,540]});
2597      doc.setFillColor(14,27,44); doc.rect(0,0,340,540,'F');
2598      doc.setFillColor(202,165,74); doc.rect(0,0,340,8,'F');
2599      try{ doc.addImage(logoImg(),'JPEG',143,22,54,54); }catch(_){}
2600      doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(15);
2601      doc.text(String(c.name||''),170,96,{align:'center'});
2602      doc.setTextColor(232,201,119); doc.setFont('helvetica','normal'); doc.setFontSize(8);
2603      doc.text('EMPLOYEE IDENTITY CARD',170,110,{align:'center'});
2604      const ph=String(e.photo||'');
2605      if(ph.indexOf('data:')===0){ const fmt=ph.indexOf('image/png')>0?'PNG':'JPEG'; try{ doc.addImage(ph,fmt,130,128,80,80); }catch(_){} doc.setDrawColor(202,165,74); doc.setLineWidth(2); doc.rect(130,128,80,80); }
2606      else { doc.setFillColor(255,255,255); doc.circle(170,168,40,'F'); doc.setTextColor(19,41,75); doc.setFont('helvetica','bold'); doc.setFontSize(34); doc.text(String((e.name||'?').slice(0,1)).toUpperCase(),170,180,{align:'center'}); }
2607      doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(18);
2608      doc.text(String(e.name||''),170,242,{align:'center'});
2609      doc.setTextColor(232,201,119); doc.setFont('helvetica','normal'); doc.setFontSize(12);
2610      doc.text(String(e.designation||''),170,260,{align:'center'});
2611      let y=302; const row=(k,v)=>{ doc.setTextColor(169,182,201); doc.setFont('helvetica','normal'); doc.setFontSize(11); doc.text(String(k),30,y); doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.text(String(v||'-'),310,y,{align:'right'}); doc.setDrawColor(90,105,130); doc.setLineWidth(.4); doc.line(30,y+9,310,y+9); y+=31; };
2612      row('Employee ID', e.employeeId); row('Department', e.department); row('Date of Joining', fmtDate(e.doj)); row('Mobile', e.mobile);
2613      doc.setFillColor(202,165,74); doc.rect(0,512,340,28,'F'); doc.setTextColor(26,18,7); doc.setFont('helvetica','bold'); doc.setFontSize(9);
2614      doc.text(String(c.web||c.email||c.name||''),170,530,{align:'center'});
2615      try{
2616        const vcard='BEGIN:VCARD\nVERSION:3.0\nFN:'+String(e.name||'')+'\nORG:'+String(c.name||'')+'\nTITLE:'+String(e.designation||'')+'\nNOTE:Employee ID '+String(e.employeeId||'')+' - Verified '+String(c.name||'')+'\nEND:VCARD';
2617        const qr=await qrDataUrl(vcard);
2618        if(qr){ doc.setFillColor(255,255,255); doc.roundedRect(134,428,72,72,7,7,'F'); doc.addImage(qr,'PNG',139,433,62,62); doc.setTextColor(169,182,201); doc.setFont('helvetica','normal'); doc.setFontSize(7); doc.text('Scan to verify',170,508,{align:'center'}); }
2619      }catch(_q){}
2620      doc.save(String(e.name||'employee').replace(/\s+/g,'_')+'_ICard.pdf');
2621    }catch(err){ toast('Could not generate I-Card. Please try again.'); }
2622  }
2623  function genDoc(kind){
2624    if(kind==='icard'){ return genICard(); }
2625    if(!BUNDLE){return} const e=BUNDLE.employee||{}, c=company(); const doc=jp();
2626    letterHead(doc,c); doc.setTextColor(30,30,30);
2627    const ref='MVS/'+(e.employeeId||'')+'/'+new Date().getFullYear();
2628    doc.setFontSize(10); doc.setTextColor(90,90,90); doc.text('Ref: '+ref,40,118); doc.text('Date: '+fmtDate(new Date()),555,118,{align:'right'});
2629    doc.setTextColor(20,20,20); doc.setFont('helvetica','bold'); doc.setFontSize(13);
2630    let title={offer:'OFFER LETTER',appointment:'APPOINTMENT LETTER',experience:'EXPERIENCE / RELIEVING LETTER'}[kind];
2631    doc.text(title,297,150,{align:'center'});
2632    doc.setFont('helvetica','normal'); doc.setFontSize(11); let y=186;
2633    doc.text('To,',40,y); doc.setFont('helvetica','bold'); doc.text(String(e.name||''),40,y+16);
2634    doc.setFont('helvetica','normal'); doc.setTextColor(90,90,90); doc.setFontSize(10);
2635    doc.text([String(e.designation||''),String(e.department?('Department: '+e.department):'')].filter(Boolean).join('\n'),40,y+32);
2636    doc.setTextColor(30,30,30); doc.setFontSize(11); y+=70;
2637    const wrap=(t)=>doc.splitTextToSize(t,515);
2638    let body=[];
2639    if(kind==='offer'){
2640      body=['Dear '+(e.name||'')+',',
2641        'We are pleased to offer you the position of '+(e.designation||'')+' in the '+(e.department||'')+' department at '+c.name+'. Your tentative date of joining is '+fmtDate(e.doj)+'.',
2642        'Your annual Cost to Company (CTC) is '+rupee(e.ctc)+'. A detailed salary structure (Annexure A) is provided below. Your employment will be governed by the company policies in force.',
2643        'We look forward to a long and rewarding association. Kindly sign and return a copy as a token of your acceptance.'];
2644    } else if(kind==='appointment'){
2645      body=['Dear '+(e.name||'')+',',
2646        'With reference to your joining on '+fmtDate(e.doj)+', we are pleased to confirm your appointment as '+(e.designation||'')+' in the '+(e.department||'')+' department at '+c.name+'.',
2647        'You will report to '+(BUNDLE.reportingManager||'your reporting manager')+'. Your annual CTC is '+rupee(e.ctc)+' as detailed in Annexure A below. All other terms of your employment remain as per company policy.',
2648        'We congratulate you on your confirmation and wish you continued success.'];
2649    } else {
2650      body=['To Whomsoever It May Concern,',
2651        'This is to certify that '+(e.name||'')+' (Employee ID: '+(e.employeeId||'')+') has been associated with '+c.name+' as '+(e.designation||'')+' in the '+(e.department||'')+' department since '+fmtDate(e.doj)+'.',
2652        'During the tenure, conduct and performance have been found to be satisfactory. We wish them success in all future endeavours.'];
2653    }
2654    doc.setFontSize(11); doc.setTextColor(30,30,30);
2655    body.forEach(p=>{ const lines=wrap(p); doc.text(lines,40,y); y+=lines.length*15+8; });
2656    if(kind==='offer'||kind==='appointment'){
2657      const s=salaryRows(e); y+=4;
2658      doc.setFont('helvetica','bold'); doc.setTextColor(19,41,75); doc.text('Annexure A — Salary Structure (Monthly)',40,y); y+=10;
2659      const y2=moneyTable(doc,40,y,'Earnings',s.earn,s.gross,'Gross Earnings');
2660      moneyTable(doc,300,y,'Deductions',s.ded,s.totded,'Total Deductions');
2661      y=Math.max(y2,y)+14; doc.setTextColor(19,41,75); doc.setFont('helvetica','bold'); doc.setFontSize(12);
2662      doc.text('Net Monthly Pay: '+rupee(s.net),40,y); y+=10;
2663    }
2664    sigBlock(doc,c,Math.min(y+30,690)); letterFoot(doc,c);
2665    doc.save((e.name||'employee').replace(/\s+/g,'_')+'_'+kind+'.pdf');
2666  }
2667  function fmtAmt(n){ return Number(n||0).toLocaleString('en-IN',{minimumFractionDigits:2,maximumFractionDigits:2}); }
2668  async function genSlip(year, mIdx){
2669    try{
2670      const e=BUNDLE.employee||{}, c=company(); const doc=jp();
2671      const mName=new Date(year,mIdx,1).toLocaleString('en-IN',{month:'long'});
2672      const days=new Date(year,mIdx+1,0).getDate();
2673      const period=mName+' '+year; const W=595, M=40, RW=W-2*M;
2674      const ym=year+'-'+String(mIdx+1).padStart(2,'0');
2675      let ps=null; try{ const pd=await api({action:'emsMyPayslips', employeeId:currentId||ssGet('ems_id')}); if(pd&&pd.rows) ps=pd.rows.find(function(r){return String(r.month)===ym;}); }catch(_){}
2676      if(!ps){ toast('Payslip for '+period+' will be available once HR finalizes that month’s payroll.'); return; }
2677      const num=function(v){return Number(v||0);};
2678      const baseEarn=[['Basic',num(ps.basic!=null?ps.basic:e.basic)],['HRA',num(ps.hra!=null?ps.hra:e.hra)],['Other Allowance',num(ps.allowance!=null?ps.allowance:e.allowance)]];
2679      const baseDed=[['Provident Fund (PF)',num(ps.pf!=null?ps.pf:e.pf)],['ESI',num(ps.esi!=null?ps.esi:e.esi)],['Professional Tax',num(ps.prof_tax!=null?ps.prof_tax:e.profTax)],['TDS',num(ps.tds!=null?ps.tds:e.tds)]];
2680      const LABEL={overtime:'Over Time',extra_day:'Extra Day',incentive:'Incentive',bonus:'Bonus',reimbursement:'Reimbursement',late_coming:'Late Coming',early_going:'Early Going',asset_recovery:'Assets Recovery',pf:'Provident Fund (PF)',esi:'ESI',tds:'TDS',deduction:'Other Deduction'};
2681      const DEDK={deduction:1,late_coming:1,early_going:1,asset_recovery:1,pf:1,esi:1,tds:1};
2682      const payDays=(ps.paid_days!=null?num(ps.paid_days):30), lopDays=num(ps.lop_days);
2683      const presentDays=(ps.present_days!=null?num(ps.present_days):null);
2684      let comps=[]; try{ comps=JSON.parse(ps.earnings_extra||'[]'); }catch(_){}
2685      const earnC=comps.filter(function(x){return !DEDK[x.kind];}).map(function(x){return [LABEL[x.kind]||x.label||x.kind, num(x.amount)];}).filter(function(x){return num(x[1])>0;});
2686      const dedC=comps.filter(function(x){return DEDK[x.kind];}).map(function(x){return [LABEL[x.kind]||x.label||x.kind, num(x.amount)];}).filter(function(x){return num(x[1])>0;});
2687      const dedSeen={}; dedC.forEach(function(x){ dedSeen[String(x[0]).toLowerCase()]=1; });
2688      let ded=[]; if(num(ps.lop_amount)>0) ded.push(['Leave / LOP ('+lopDays+'d)', num(ps.lop_amount)]);
2689      ded=ded.concat(dedC).concat(baseDed.filter(function(x){ return num(x[1])>0 && !dedSeen[String(x[0]).toLowerCase()]; }));
2690      const earn=baseEarn.filter(function(x){ return String(x[0])==='Basic' || num(x[1])>0; }).concat(earnC);
2691      const s={earn:earn, ded:ded, gross:num(ps.gross), totded:num(ps.total_deductions), net:num(ps.net)};
2692      const esiDed=s.ded.some(function(x){return /esi/i.test(x[0]) && num(x[1])>0;});
2693      const pfDed=s.ded.some(function(x){return /provident/i.test(x[0]) && num(x[1])>0;});
2694      doc.setFillColor(21,21,27); doc.rect(0,0,W,98,'F');
2695      doc.setFillColor(200,155,60); doc.rect(0,98,W,3.2,'F');
2696      try{ doc.addImage(logoImg(),'JPEG',M,26,48,48); }catch(_){}
2697      doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(19); doc.text(String(c.name||''),100,46);
2698      doc.setFont('helvetica','normal'); doc.setFontSize(8.5); doc.setTextColor(206,196,172); doc.text(doc.splitTextToSize(String(c.addr||''),330),100,62);
2699      doc.setTextColor(232,201,119); doc.setFont('helvetica','bold'); doc.setFontSize(11); doc.text('PAYSLIP',W-M,42,{align:'right'});
2700      doc.setTextColor(220,210,190); doc.setFont('helvetica','normal'); doc.setFontSize(9); doc.text(period,W-M,58,{align:'right'});
2701      let y=128; doc.setTextColor(24,24,30); doc.setFont('helvetica','bold'); doc.setFontSize(15); doc.text('Salary Slip',W/2,y,{align:'center'});
2702      doc.setDrawColor(200,155,60); doc.setLineWidth(1.5); doc.line(W/2-46,y+7,W/2+46,y+7); y+=22;
2703      const cells=[['Employee ID',e.employeeId||'-'],['Employee Name',e.name||'-'],['Designation',e.designation||'-'],['Month / Year',period],['Department',e.department||'-'],['Date of Joining',fmtDate(e.doj)||'-'],['Bank Name',e.bankName||'-'],['Account No.',e.bankAccount||'-'],['IFSC',e.ifsc||'-'],['Pay Days',String(payDays)+(lopDays>0?(' \u00b7 LOP '+lopDays+'d'):'')]];
2704      if(presentDays!=null) cells.push(['Present Days',String(presentDays)]);
2705      if(esiDed && e.esiNo) cells.push(['ESI No.',String(e.esiNo)]);
2706      if(pfDed && (e.pfNo||e.uan)) cells.push(['PF No. / UAN',String(e.pfNo||e.uan)]);
2707      if(cells.length%2) cells.push(['','']);
2708      const rows=[]; for(let ci=0;ci<cells.length;ci+=2) rows.push([cells[ci][0],cells[ci][1],cells[ci+1][0],cells[ci+1][1]]);
2709      const rh=21, cLbl=92, colW=RW/2; doc.setLineWidth(.6); doc.setDrawColor(205,205,212);
2710      rows.forEach(function(r,i){ const ry=y+i*rh;
2711        doc.setFillColor(246,247,249); doc.rect(M,ry,cLbl,rh,'F'); doc.rect(M+colW,ry,cLbl,rh,'F');
2712        doc.rect(M,ry,colW,rh); doc.rect(M+colW,ry,colW,rh); doc.rect(M+cLbl,ry,colW-cLbl,rh); doc.rect(M+colW+cLbl,ry,colW-cLbl,rh);
2713        doc.setFontSize(9); doc.setFont('helvetica','bold'); doc.setTextColor(75,75,85); doc.text(String(r[0]),M+7,ry+13.5); doc.text(String(r[2]),M+colW+7,ry+13.5);
2714        doc.setFont('helvetica','normal'); doc.setTextColor(25,25,30);
2715        doc.text(doc.splitTextToSize(String(r[1]),colW-cLbl-12),M+cLbl+7,ry+13.5); doc.text(doc.splitTextToSize(String(r[3]),colW-cLbl-12),M+colW+cLbl+7,ry+13.5);
2716      });
2717      y+=rows.length*rh+18; const hw=RW/2-6;
2718      function half(x,title,rws,total,tl){ let yy=y;
2719        doc.setFillColor(21,21,27); doc.rect(x,yy,hw,24,'F');
2720        doc.setTextColor(232,201,119); doc.setFont('helvetica','bold'); doc.setFontSize(10.5); doc.text(title,x+9,yy+16); doc.text('Amount (INR)',x+hw-9,yy+16,{align:'right'}); yy+=24;
2721        doc.setFont('helvetica','normal'); doc.setFontSize(10); doc.setTextColor(35,35,42); doc.setDrawColor(214,214,220); doc.setLineWidth(.5);
2722        rws.forEach(function(r){ doc.rect(x,yy,hw,20); doc.text(String(r[0]),x+9,yy+13.5); doc.text(fmtAmt(r[1]),x+hw-9,yy+13.5,{align:'right'}); yy+=20; });
2723        doc.setFillColor(243,245,248); doc.rect(x,yy,hw,22,'F'); doc.rect(x,yy,hw,22);
2724        doc.setFont('helvetica','bold'); doc.setTextColor(21,21,27); doc.text(tl,x+9,yy+15); doc.text(fmtAmt(total),x+hw-9,yy+15,{align:'right'}); return yy+22;
2725      }
2726      const ly=half(M,'Earnings',s.earn,s.gross,'Total Earnings');
2727      const ry=half(M+hw+12,'Deductions',s.ded,s.totded,'Total Deductions');
2728      y=Math.max(ly,ry)+18;
2729      doc.setFillColor(21,21,27); doc.roundedRect(M,y,RW,44,6,6,'F'); doc.setFillColor(200,155,60); doc.rect(M,y,5,44,'F');
2730      doc.setTextColor(255,255,255); doc.setFont('helvetica','bold'); doc.setFontSize(13); doc.text('Total Net Payable',M+18,y+21);
2731      doc.setTextColor(232,201,119); doc.setFontSize(17); doc.text('INR '+fmtAmt(s.net),W-M-14,y+24,{align:'right'});
2732      doc.setTextColor(198,198,205); doc.setFont('helvetica','normal'); doc.setFontSize(9); doc.text('Rupees '+inWords(s.net)+' only.',M+18,y+37); y+=64;
2733      doc.setTextColor(60,60,70); doc.setFont('helvetica','normal'); doc.setFontSize(9.5); doc.text('Salary paid by NEFT / UPI to the bank account on record.',M,y);
2734      doc.setDrawColor(228,228,230); doc.setLineWidth(.5); doc.line(M,792,W-M,792);
2735      doc.setFont('helvetica','italic'); doc.setFontSize(8.5); doc.setTextColor(120,120,130); doc.text('This is a computer-generated salary slip and does not require a signature.',W/2,806,{align:'center'});
2736      doc.setFont('helvetica','normal'); doc.setFontSize(7.5); doc.setTextColor(150,150,155); doc.text(String(c.name||'')+'  \u2022  '+period,W/2,818,{align:'center'});
2737      doc.save(String(e.name||'employee').replace(/\s+/g,'_')+'_Payslip_'+mName+'_'+year+'.pdf');
2738    }catch(err){ toast('Could not generate payslip. Please try again.'); }
2739  }
2740  function genNotice(jsonStr){
2741    const n=JSON.parse(jsonStr); const c=company(); const doc=jp(); letterHead(doc,c);
2742    doc.setTextColor(19,41,75); doc.setFont('helvetica','bold'); doc.setFontSize(14); doc.text('NOTICE',297,128,{align:'center'});
2743    doc.setTextColor(90,90,90); doc.setFont('helvetica','normal'); doc.setFontSize(10); doc.text('Date: '+fmtDate(n.t),555,150,{align:'right'});
2744    doc.setTextColor(20,20,20); doc.setFont('helvetica','bold'); doc.setFontSize(12); doc.text('Subject: '+(n.s||''),40,180);
2745    doc.setFont('helvetica','normal'); doc.setFontSize(11); doc.setTextColor(30,30,30);
2746    const lines=doc.splitTextToSize(String(n.m||''),515); doc.text(lines,40,210);
2747    sigBlock(doc,c,Math.min(210+lines.length*15+50,690)); letterFoot(doc,c);
2748    doc.save('notice_'+(n.s||'mvs').replace(/\s+/g,'_').slice(0,24)+'.pdf');
2749  }
2750  function inWords(num){ num=Math.round(num); if(num===0)return'Zero';
2751    const a=['','One','Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten','Eleven','Twelve','Thirteen','Fourteen','Fifteen','Sixteen','Seventeen','Eighteen','Nineteen'];
2752    const b=['','','Twenty','Thirty','Forty','Fifty','Sixty','Seventy','Eighty','Ninety'];
2753    function two(n){ return n<20?a[n]:b[Math.floor(n/10)]+(n%10?' '+a[n%10]:''); }
2754    function three(n){ return (n>=100?a[Math.floor(n/100)]+' Hundred'+(n%100?' ':''):'')+two(n%100); }
2755    let s=''; const cr=Math.floor(num/10000000); num%=10000000; const lk=Math.floor(num/100000); num%=100000; const th=Math.floor(num/1000); num%=1000;
2756    if(cr)s+=three(cr)+' Crore '; if(lk)s+=three(lk)+' Lakh '; if(th)s+=three(th)+' Thousand '; if(num)s+=three(num);
2757    return s.trim();
2758  }
2759</script>
2759
2760</body>
2761</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.