*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
:root{
  --fs-xs:12.5px; --fs-sm:14px; --fs-body:16px; --fs-lg:18px; --fs-xl:22px; --fs-2xl:28px;
  --lh-tight:1.35; --lh-body:1.7;
  --text:#141A2E; --text-muted:#4A5270; --text-subtle:#6B7394;
}
body{font-family:'Sarabun',sans-serif;background:#F4F6FA;color:var(--text);font-size:var(--fs-body);line-height:var(--lh-body);letter-spacing:0.01em;}
::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-thumb{background:#D8DCE8;border-radius:4px;}
::-webkit-scrollbar-track{background:transparent;}
input,select,button{font-family:inherit;}

@keyframes tpToast{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:translateY(0);}}
@keyframes tpBlink{0%,100%{opacity:1;}50%{opacity:.45;}}
.tp-blink{animation:tpBlink 2.4s ease-in-out infinite;}

/* bar tooltip (dashboard chart) */
.tp-bar .tp-tip{position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%,4px);background:#1A1F36;color:#fff;font:500 var(--fs-xs)/1 'IBM Plex Mono',monospace;padding:4px 8px;border-radius:6px;white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .15s ease, transform .15s ease;box-shadow:0 4px 12px rgba(16,24,40,.2);z-index:5;}
.tp-bar:hover .tp-tip{opacity:1;transform:translate(-50%,0);}
.tp-fill{transition:filter .15s ease;}
.tp-bar:hover .tp-fill{filter:brightness(.9);}

/* hover behaviours that replace the prototype's style-hover attribute */
.tp-nav{transition:all .15s;}
.tp-nav:not(.is-active):hover{background:#EFF4FF !important;transform:translateX(4px);box-shadow:inset 3px 0 0 #2563EB;}
.tp-btn-primary:hover{background:#1D4FD7 !important;transform:translateY(-2px);box-shadow:0 7px 18px rgba(37,99,235,0.34);}
.tp-btn-green:hover{background:#047857 !important;transform:translateY(-2px);box-shadow:0 7px 18px rgba(5,150,105,0.34);}
.tp-btn-ghost:hover{background:#F4F6FA !important;transform:translateY(-2px);box-shadow:0 5px 13px rgba(16,24,40,0.1);}
.tp-btn-confirm:hover{background:#D1FAE5 !important;transform:translateY(-2px);box-shadow:0 6px 15px rgba(5,150,105,0.22);}
.tp-btn-reject:hover{background:#FEE2E2 !important;transform:translateY(-2px);box-shadow:0 6px 15px rgba(220,38,38,0.22);}
.tp-btn-remove:hover{background:#FEE2E2 !important;transform:translateY(-2px);box-shadow:0 5px 13px rgba(220,38,38,0.18);}
.tp-card-click{transition:transform .18s ease, box-shadow .18s ease;}
.tp-card-click:hover{transform:translateY(-3px);box-shadow:0 8px 20px rgba(16,24,40,0.12);}
.tp-tab{transition:all .15s;}
.tp-tab:not(.is-active):hover{color:#2563EB;transform:translateY(-2px);}

/* ===== login — dark "orbit field" (token-driven, scoped · app ยังเป็น light theme) ===== */
.tp-login-wrap{
  /* primitive→semantic tokens, scoped to login only (app stays light) */
  --lg-canvas:#060218; --lg-card:rgba(17,10,40,0.72); --lg-border:rgba(255,255,255,0.08); --lg-border-hi:rgba(255,255,255,0.16);
  --lg-text:#F7F8F8; --lg-text-2:#B9B2CE; --lg-muted:#948CAE; --lg-ph:#8A82A4;
  --lg-field:rgba(255,255,255,0.035); --lg-field-border:rgba(255,255,255,0.10);
  /* accent #A068FF ใช้กับพื้นผิว/เส้น/โฟกัสเท่านั้น · ปุ่มใช้ม่วงเข้มกว่าเพราะ #A068FF + ตัวอักษรขาว = 3.5:1 (ตก AA) */
  --lg-accent:#A068FF; --lg-accent-dim:rgba(160,104,255,0.35); --lg-grad:linear-gradient(135deg,#6D28D9,#7C3AED); --lg-glow:rgba(160,104,255,0.6);
  position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;
  background:var(--lg-canvas);overflow:hidden;padding:24px;
}
.tp-login-aurora{position:absolute;inset:-20% -10%;pointer-events:none;filter:blur(22px);
  background:
    radial-gradient(40% 50% at 22% 30%, rgba(124,58,237,0.34), transparent 70%),
    radial-gradient(38% 46% at 80% 70%, rgba(160,104,255,0.26), transparent 72%),
    radial-gradient(28% 38% at 64% 16%, rgba(217,161,255,0.10), transparent 76%);
  will-change:transform;animation:lgDrift 22s ease-in-out infinite alternate;}
/* translate ล้วน — scale บนเลเยอร์ที่ blur(22px) ทำให้ต้อง re-raster ภาพเบลอใหม่ระหว่างวิ่ง */
@keyframes lgDrift{from{transform:translate3d(-2.5%,-1.5%,0);}to{transform:translate3d(2.5%,2.5%,0);}}

/* วงโคจร — 4 วงซ้อน หมุนสวนทาง มี node ของระบบวิ่งไปด้วย */
.tp-login-orbits{position:absolute;inset:0;pointer-events:none;display:grid;place-items:center;
  transform:scale(var(--ring-scale,1));
  animation:lgOrbitsIn 1.1s cubic-bezier(.22,1,.36,1) both;}
@keyframes lgOrbitsIn{from{opacity:0;transform:scale(calc(var(--ring-scale,1) * .88));}to{opacity:1;transform:scale(var(--ring-scale,1));}}
.tp-orbit{position:absolute;width:var(--d);height:var(--d);border-radius:50%;
  will-change:transform;backface-visibility:hidden;
  animation:lgSpin var(--dur) linear infinite;}
.tp-orbit--rev{animation-name:lgSpinRev;}
.tp-orbit::before{content:"";position:absolute;inset:0;border-radius:50%;padding:1px;
  background:linear-gradient(180deg,rgba(217,161,255,0) 0%,rgba(217,161,255,.85) 43%,rgba(217,161,255,0) 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude;}
@keyframes lgSpin{to{transform:rotate(360deg);}}
@keyframes lgSpinRev{to{transform:rotate(-360deg);}}

.tp-node{position:absolute;top:50%;left:50%;width:var(--s,9px);height:var(--s,9px);border-radius:50%;
  background:var(--lg-accent);box-shadow:0 0 14px 2px rgba(160,104,255,.55);
  transform:translate(-50%,-50%) rotate(var(--a)) translate(var(--r)) rotate(calc(-1 * var(--a)));
  animation:lgNodeIn .7s cubic-bezier(.22,1,.36,1) var(--delay,0s) both;}
@keyframes lgNodeIn{from{opacity:0;filter:blur(6px);}to{opacity:1;filter:blur(0);}}
/* ไม่ใช้ backdrop-filter กับ chip — glass ที่ "เคลื่อนที่" บังคับ re-composite ทุกเฟรม (แพงกว่า glass ที่อยู่นิ่ง) */
.tp-chip{--s:46px;border-radius:14px;background:rgba(76,45,120,.55);border:1px solid rgba(160,104,255,.4);
  display:grid;place-items:center;
  box-shadow:0 10px 30px -12px rgba(160,104,255,.9),0 0 0 6px rgba(160,104,255,.06);}
.tp-chip-in{display:grid;place-items:center;color:#DFCFFF;animation:lgSpinRev var(--dur) linear infinite;}
.tp-orbit--rev .tp-chip-in{animation-name:lgSpin;}
.tp-chip svg{width:21px;height:21px;stroke-width:1.6;}

.tp-login-card{position:relative;z-index:1;width:400px;max-width:92vw;
  background:var(--lg-card);backdrop-filter:blur(22px) saturate(140%);-webkit-backdrop-filter:blur(22px) saturate(140%);
  border:1px solid var(--lg-border);border-radius:16px;padding:32px 30px 24px;
  box-shadow:0 1px 0 rgba(255,255,255,0.06) inset,0 24px 60px -20px rgba(0,0,0,0.72),0 0 0 1px rgba(0,0,0,0.4);
  animation:lgCardIn .42s cubic-bezier(.16,.84,.44,1) both;}
.tp-login-card::before{content:"";position:absolute;top:0;left:18px;right:18px;height:1px;
  background:linear-gradient(90deg,transparent,rgba(160,104,255,0.6),rgba(217,161,255,0.6),transparent);}
@keyframes lgCardIn{from{opacity:0;transform:translateY(14px) scale(.985);}to{opacity:1;transform:none;}}

/* เส้นแสงเคลื่อนไหว — light beam วิ่งรอบขอบการ์ด
   หมุน conic ด้วย transform บนเลเยอร์ลูก (GPU) ไม่ใช่ animate @property --angle
   ของเดิม animate custom property → เบราว์เซอร์ต้อง repaint conic ใหม่ทุกเฟรมบน main thread = ต้นเหตุกระตุก */
.tp-login-beam{position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:2;overflow:hidden;padding:1.5px;
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);-webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);mask-composite:exclude;}
.tp-login-beam i{position:absolute;top:50%;left:50%;width:180%;aspect-ratio:1;
  background:conic-gradient(transparent 0deg 295deg, rgba(160,104,255,0.7) 330deg, #D9A1FF 352deg, transparent 360deg);
  transform:translate(-50%,-50%);transform-origin:center;will-change:transform;
  animation:lgBeamSpin 5.5s linear infinite;}
@keyframes lgBeamSpin{from{transform:translate(-50%,-50%) rotate(0turn);}to{transform:translate(-50%,-50%) rotate(1turn);}}

.tp-login-brand{display:flex;flex-direction:column;align-items:center;gap:9px;margin-bottom:22px;}
.tp-login-logo-img{width:170px;height:auto;background:rgba(255,255,255,.75);backdrop-filter:blur(7px) saturate(120%);-webkit-backdrop-filter:blur(7px) saturate(120%);padding:8px 13px;border-radius:12px;border:1px solid rgba(255,255,255,.28);box-shadow:0 4px 16px rgba(0,0,0,.22);}
.tp-login-eyebrow{font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--lg-muted);letter-spacing:.18em;text-transform:uppercase;}

.tp-login-head{margin-bottom:22px;}
.tp-login-h1{font-size:24px;font-weight:600;color:var(--lg-text);letter-spacing:-0.01em;line-height:1.2;margin:0 0 5px;}
.tp-login-sub{font-size:13.5px;color:var(--lg-text-2);margin:0;}

.tp-login-fields{display:flex;flex-direction:column;gap:15px;}
.tp-field-group{display:flex;flex-direction:column;gap:7px;}
.tp-login-label{font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--lg-muted);text-transform:uppercase;letter-spacing:.08em;}

.tp-login-card .tp-field{position:relative;}
.tp-login-card .tp-field-icon{position:absolute;left:13px;top:50%;transform:translateY(-50%);width:17px;height:17px;color:var(--lg-muted);pointer-events:none;transition:color .16s ease;}
.tp-login-card .tp-field input{width:100%;background:var(--lg-field);border:1px solid var(--lg-field-border);border-radius:10px;
  padding:11px 14px 11px 40px;font-size:14px;color:var(--lg-text);
  transition:border-color .16s ease,box-shadow .16s ease,background .16s ease;}
.tp-login-card .tp-field input::placeholder{color:var(--lg-ph);}
.tp-login-card .tp-field input:hover{border-color:var(--lg-border-hi);}
.tp-login-card .tp-field input:focus{outline:none;border-color:var(--lg-accent);background:rgba(255,255,255,0.05);
  box-shadow:0 0 0 3px rgba(160,104,255,0.25),0 0 22px -6px rgba(160,104,255,0.55);}
.tp-login-card .tp-field:focus-within .tp-field-icon{color:var(--lg-accent);}
.tp-login-card .tp-pw input{padding-right:42px;}
.tp-login-card .tp-pw-eye{color:var(--lg-muted);}
.tp-login-card .tp-pw-eye:hover{color:var(--lg-text);background:rgba(255,255,255,0.07);}

.tp-login-err{display:flex;align-items:center;gap:9px;font-size:13px;color:#FCA5A5;
  background:rgba(220,38,38,0.12);border:1px solid rgba(248,113,113,0.32);border-radius:9px;padding:9px 12px;}
.tp-login-err svg{width:16px;height:16px;flex-shrink:0;}

.tp-login-submit{position:relative;overflow:hidden;isolation:isolate;
  margin-top:4px;display:flex;align-items:center;justify-content:center;gap:8px;
  padding:12px;border-radius:10px;border:1px solid transparent;cursor:pointer;
  font-size:14.5px;font-weight:600;color:#fff;letter-spacing:.01em;font-family:inherit;background:var(--lg-grad);
  box-shadow:0 8px 22px -8px var(--lg-glow),0 0 0 1px rgba(255,255,255,0.06) inset;
  transition:transform .16s ease,box-shadow .16s ease;}
/* fill สไลด์เข้าจากซ้าย — เป็นชั้นแสงโปร่ง ไม่ใช่ #A068FF ทึบ (ทึบแล้ว contrast ตก 3.5:1) */
.tp-login-submit::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(90deg,rgba(217,161,255,0.22),rgba(160,104,255,0.12));
  transform:translateX(-100%);transition:transform .4s cubic-bezier(.22,1,.36,1);}
.tp-login-submit>*{position:relative;}
.tp-login-submit svg{width:17px;height:17px;transition:transform .16s ease;}
.tp-login-submit:hover{transform:translateY(-1px);
  box-shadow:0 12px 28px -8px rgba(160,104,255,0.85),0 0 0 1px rgba(255,255,255,0.09) inset;}
.tp-login-submit:hover::after,.tp-login-submit:focus-visible::after{transform:translateX(0);}
.tp-login-submit:hover svg{transform:translateX(3px);}
.tp-login-submit:active{transform:translateY(0);filter:brightness(.94);}
.tp-login-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(160,104,255,0.55),0 8px 22px -8px var(--lg-glow);}

.tp-login-foot{display:flex;align-items:center;justify-content:center;gap:7px;margin-top:20px;
  font-family:'IBM Plex Mono',monospace;font-size:11px;color:var(--lg-muted);letter-spacing:.04em;}
.tp-login-foot svg{width:13px;height:13px;}

.tp-recover{margin-top:18px;padding-top:18px;border-top:1px solid var(--lg-border);display:flex;flex-direction:column;gap:14px;}
.tp-recover-note{display:flex;align-items:flex-start;gap:9px;font-size:12.5px;line-height:1.5;color:var(--lg-text-2);
  background:rgba(160,104,255,0.10);border:1px solid rgba(160,104,255,0.24);border-radius:9px;padding:10px 12px;}
.tp-recover-note svg{width:16px;height:16px;flex-shrink:0;margin-top:2px;color:var(--lg-accent);}

@media (prefers-reduced-motion:reduce){
  .tp-login-aurora,.tp-login-card,.tp-login-beam i,
  .tp-login-orbits,.tp-orbit,.tp-chip-in,.tp-node{animation:none;}
  .tp-login-submit,.tp-login-submit::after,.tp-login-submit svg,.tp-login-card .tp-field input,.tp-login-card .tp-field-icon{transition:none;}
  .tp-login-submit:hover{transform:none;}
}
@media (max-width:1200px){ .tp-login-orbits{--ring-scale:.86;} }
@media (max-width:900px){ .tp-login-orbits{--ring-scale:.68;} }
@media (max-width:600px){ .tp-login-orbits{--ring-scale:.52;} }
@media (max-width:380px){
  .tp-login-card{padding:26px 20px 20px;}
  .tp-login-h1{font-size:21px;}
  .tp-login-orbits{--ring-scale:.44;}
}

/* show/hide password toggle (login + เปลี่ยนรหัส) */
.tp-pw{position:relative;}
.tp-pw input{padding-right:40px !important;width:100%;}
.tp-pw-eye{position:absolute;right:7px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;color:#9aa2bd;display:flex;align-items:center;border-radius:6px;line-height:0;}
.tp-pw-eye:hover{color:#475067;background:#EEF1F8;}
.tp-pw-eye .pw-off{display:none;}
.tp-pw-eye.is-shown .pw-on{display:none;}
.tp-pw-eye.is-shown .pw-off{display:flex;}

/* nav item base/active (was navBase/navOn/navOff in prototype JS) */
.tp-nav{display:flex;align-items:center;gap:10px;padding:9px 11px;border-radius:8px;cursor:pointer;font-size:var(--fs-sm);margin-bottom:2px;border:1px solid transparent;white-space:nowrap;background:transparent;color:var(--text-subtle);}
.tp-nav.is-active{background:#EFF4FF;color:#2563EB;border-color:#BFCFFF;font-weight:600;}

/* sub-tab base/active (was tabBase/tabOn/tabOff) */
.tp-tab{padding:14px 16px;font-size:var(--fs-sm);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;white-space:nowrap;color:var(--text-subtle);}
.tp-tab.is-active{color:#2563EB;border-bottom-color:#2563EB;font-weight:600;}

/* view switching (was display:{{ dispX }} in prototype) */
.tp-view{display:none;}
.tp-view.is-active{display:block;}
.tp-subview{display:none;}
.tp-subview.is-active{display:block;}

/* ช่องค้นหาในแต่ละ list (filter ฝั่ง client) */
.tp-search{background:#fff;border:1px solid #C7D0E8;border-radius:7px;padding:6px 11px;font-size:var(--fs-sm);color:var(--text);min-width:200px;max-width:300px;}
.tp-search:focus{outline:none;border-color:#2563EB;box-shadow:0 0 0 3px rgba(37,99,235,0.12);}
.tp-search::placeholder{color:#9aa2bd;}

/* ยอดนับจำนวนรายการในหัวข้อแต่ละหมวด */
.tp-count{margin-left:9px;font-family:'IBM Plex Mono',monospace;font-size:var(--fs-xs);font-weight:400;color:#6B7394;background:#F4F6FA;border:1px solid #E2E6F0;border-radius:20px;padding:1px 9px;vertical-align:middle;}

/* dropzone แนบหลักฐานจัดซื้อ (Finance) */
.tp-dropzone{transition:background .15s ease, border-color .15s ease;}
.tp-dropzone:hover{background:#EFF4FF !important;border-color:#2563EB !important;}

/* เลื่อนเฉพาะส่วนรายการ — หัวข้อ/ช่องค้นหา/หัวตารางอยู่กับที่ (thead sticky) */
.tp-scroll{max-height:max(280px, calc(100vh - 240px));overflow:auto;}
.tp-scroll thead th{position:sticky;top:0;background:#fff;z-index:2;box-shadow:inset 0 -1px 0 #EDEFF5;}

/* dashboard card ที่ role เข้า view ปลายทางไม่ได้ — ไม่ทำตัวเป็นปุ่ม nav */
.tp-nav-disabled{cursor:default !important;}
.tp-nav-disabled.tp-card-click:hover{transform:none;box-shadow:0 1px 3px rgba(16,24,40,0.04);}
.tp-nav-disabled.tp-btn-ghost:hover{transform:none;box-shadow:none;background:#fff;}
