/* ======== Data Haji Khusus — stylesheet ========
   Berkas NYATA di public/, bukan symlink. nginx CloudPanel memakai
   disable_symlinks if_not_owner; symlink milik root dilayani sebagai 403.
   Token diambil dari porsihaji (19 token, light-only — tidak ada dark mode
   di sana, jadi di sini juga tidak, daripada dark mode setengah jadi). */
:root{
  --bg:#EEF3FB; --surface:#FFFFFF; --surface-2:#F6F9FD;
  --ink:#0F1B2E; --muted:#5B6B82; --faint:#93A1B5;
  --primary:#1D4ED8; --primary-dark:#1A3A8F; --primary-soft:#E3ECFB;
  --gold:#D9A63C; --gold-soft:#FBF0D8;
  --border:#DCE5F1; --border-strong:#C6D3E6;
  --danger:#C43D2B; --danger-soft:#FBE7E3;
  --ok:#1E7A54; --ok-soft:#E2F1EA;
  --warn:#C88A2E; --warn-soft:#FBF0D8;
  --radius:10px; --shadow:0 1px 2px rgba(15,27,46,.05),0 8px 24px rgba(15,27,46,.08);
}
*{box-sizing:border-box;margin:0;padding:0}
body{
  font-family:'Plus Jakarta Sans',system-ui,sans-serif;
  background:var(--bg);color:var(--ink);line-height:1.5;font-size:14px;
  -webkit-font-smoothing:antialiased;
}
.mono{font-family:'JetBrains Mono',ui-monospace,monospace;font-variant-numeric:tabular-nums}
a{color:var(--primary)}

/* ---- Shell ---- */
.wrap{max-width:1100px;margin:0 auto;padding:0 24px}
header{background:var(--primary);color:#fff;padding:18px 0;position:relative;overflow:hidden}
header::after{content:"";position:absolute;right:-60px;top:-50px;width:240px;height:240px;
  background:radial-gradient(circle,rgba(217,166,60,.26),transparent 70%)}
.head{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
.brand{display:flex;gap:12px;align-items:center}
.glyph{width:40px;height:40px;border-radius:11px;background:rgba(255,255,255,.13);
  border:1px solid rgba(255,255,255,.22);display:grid;place-items:center;flex:none}
.glyph svg{width:22px;height:22px}
h1{font-size:17px;font-weight:800;letter-spacing:-.01em;line-height:1.2}
.sub{font-size:12px;color:rgba(255,255,255,.72);margin-top:2px}
nav{display:flex;gap:4px;background:rgba(255,255,255,.1);padding:4px;border-radius:9px}
nav a{font-size:13px;font-weight:600;color:rgba(255,255,255,.8);text-decoration:none;
  padding:7px 12px;border-radius:7px}
nav a:hover{background:rgba(255,255,255,.12);color:#fff}
nav a[aria-current="page"]{background:#fff;color:var(--primary-dark)}
main{max-width:1100px;margin:0 auto;padding:22px 24px 80px}

/* ---- Buttons ---- */
.btn{font-family:inherit;font-size:13px;font-weight:600;border-radius:8px;cursor:pointer;
  border:1px solid transparent;padding:9px 14px;display:inline-flex;align-items:center;gap:7px;
  line-height:1;transition:.14s ease;white-space:nowrap;text-decoration:none}
.btn-solid{background:var(--primary);color:#fff;border-color:var(--primary)}
.btn-solid:hover{background:var(--primary-dark)}
.btn-ok{background:var(--ok);color:#fff;border-color:var(--ok)}
.btn-ok:hover{filter:brightness(.92)}
.btn-line{background:var(--surface);color:var(--ink);border-color:var(--border-strong)}
.btn-line:hover{background:var(--surface-2)}
.btn-ghost{background:rgba(255,255,255,.12);color:#fff;border-color:rgba(255,255,255,.24)}
.btn-ghost:hover{background:rgba(255,255,255,.2)}
.btn:disabled{opacity:.5;cursor:not-allowed}

/* ---- Cards & stats ---- */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:18px 20px;margin-bottom:16px}
.card h2{font-size:15px;font-weight:700;margin-bottom:4px}
.card .hint{font-size:12.5px;color:var(--muted);margin-bottom:14px;line-height:1.6}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-bottom:16px}
.stat{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  padding:13px 15px;box-shadow:var(--shadow)}
.stat .k{font-size:10.5px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.stat .v{font-size:24px;font-weight:800;letter-spacing:-.02em;margin-top:3px}
.stat .n{font-size:11.5px;color:var(--faint);margin-top:2px}
.stat.ok .v{color:var(--ok)} .stat.warn .v{color:var(--warn)} .stat.bad .v{color:var(--danger)}

/* ---- Alerts ---- */
.alert{font-size:13px;font-weight:600;padding:11px 14px;border-radius:9px;margin-bottom:16px;
  display:flex;gap:9px;align-items:flex-start;line-height:1.55}
.alert svg{width:17px;height:17px;flex:none;margin-top:1px}
.alert.ok{background:var(--ok-soft);color:var(--ok)}
.alert.bad{background:var(--danger-soft);color:var(--danger)}
.alert.warn{background:var(--warn-soft);color:#8a5e13}
.alert.info{background:var(--primary-soft);color:var(--primary-dark)}
.alert ul{margin:6px 0 0 18px;font-weight:500}

/* ---- Form ---- */
.field{margin-bottom:14px}
.field label{display:block;font-size:12px;font-weight:700;color:var(--muted);margin-bottom:6px}
.field input[type=text],.field input[type=password]{width:100%;padding:11px 13px;
  border:1px solid var(--border-strong);border-radius:9px;font-family:inherit;font-size:14px;
  background:var(--surface-2);color:var(--ink)}
.field input:focus{outline:none;border-color:var(--primary);background:#fff;
  box-shadow:0 0 0 3px var(--primary-soft)}
input[type=file]{font-family:inherit;font-size:13px;padding:10px;background:var(--surface-2);
  border:1px dashed var(--border-strong);border-radius:9px;width:100%;cursor:pointer}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}

/* ---- Table ---- */
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--border)}
th{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;
  background:var(--surface-2)}
tbody tr:last-child td{border-bottom:none}
td.num{text-align:right;font-variant-numeric:tabular-nums}
.scroll{max-height:320px;overflow:auto;border:1px solid var(--border);border-radius:9px}
.foot{font-size:11.5px;color:var(--faint);margin-top:8px}

/* ======== Chart ========
   Spec mark dari skill dataviz: mark tipis, ujung DATA membulat 4px dengan
   baseline tetap persegi, celah surface 2px antar fill (bukan border), grid
   hairline solid, teks memakai token ink dan tidak pernah warna seri.

   Palet sudah dijalankan lewat scripts/validate_palette.js terhadap surface
   #FFFFFF yang sebenarnya, bukan dikira-kira:
     seri tunggal  #1D4ED8                        → ALL CHECKS PASS
     ramp ordinal  #86b6ef,#5598e7,#2a78d6,#1c5cab → ALL CHECKS PASS (--ordinal)
   Ramp itu GAGAL bila divalidasi sebagai kategorikal; ia memang bukan
   kategorikal — konsentrasi pasar adalah peringkat, bukan identitas. */

/* ---- Hero number ---- */
.hero{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:22px 24px;margin-bottom:16px;
  display:flex;align-items:baseline;gap:18px;flex-wrap:wrap}
/* Angka proporsional, BUKAN tabular-nums: hero adalah satu angka yang dibaca
   sebagai kata, tidak dibandingkan kolom-per-kolom dengan angka lain. */
.hero .big{font-size:56px;font-weight:800;letter-spacing:-.03em;line-height:1;
  color:var(--primary-dark);font-variant-numeric:proportional-nums}
.hero .lbl{font-size:14px;font-weight:600;color:var(--muted)}
.hero .lbl b{display:block;font-size:12px;font-weight:500;color:var(--faint);margin-top:3px}

/* ---- Bar horizontal (chart 1 & 4) ---- */
.bars{display:flex;flex-direction:column;gap:9px}
.bars .b{display:grid;grid-template-columns:minmax(120px,230px) 1fr;gap:12px;align-items:center;
  min-height:24px;padding:1px 0;border-radius:6px}
.bars .b:hover{background:var(--surface-2)}
.bars .bl{font-size:12.5px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bars .bl.q{color:var(--muted);font-style:italic}
.bars .bt{display:flex;align-items:center;gap:8px;min-width:0}
.bars .bf{height:18px;background:var(--primary);
  /* ujung DATA membulat, baseline persegi */
  border-radius:0 4px 4px 0;min-width:2px;flex:none}
.bars .b.q .bf{background:var(--faint)}
.bars .bv{font-size:12px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums;
  white-space:nowrap}
.bars .bp{font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums;white-space:nowrap}
/* Grid hairline recessive, di BELAKANG mark */
.plot{position:relative}
.plot .gridline{position:absolute;top:0;bottom:0;width:1px;background:var(--border);z-index:0}
.plot .bars{position:relative;z-index:1}

/* ---- Bar bertumpuk (chart 2) ---- */
/* GRID, bukan flex dengan flex-basis persen.
   Dengan flex:0 0 X% yang berjumlah 100%, tiga gap 2px membuat total jadi
   100% + 6px -- dan overflow:hidden memotong 6px dari segmen terakhir. Grid
   membagi sisa setelah gap, jadi persentasenya tetap jujur dan tidak ada yang
   terpotong. Terlihat saat mengukur render, bukan saat menulis CSS-nya. */
.stack{display:grid;height:34px;border-radius:6px;
  /* celah antar segmen adalah gap surface 2px, BUKAN border */
  gap:2px;background:var(--surface)}
.stack .sg{min-width:3px;position:relative;overflow:hidden}
.stack .sg:first-child{border-radius:6px 0 0 6px}
.stack .sg:last-child{border-radius:0 6px 6px 0}
.stkey{display:flex;flex-wrap:wrap;gap:14px;margin-top:12px}
.stkey .i{display:flex;gap:7px;align-items:flex-start;font-size:12px}
.stkey .sw{width:11px;height:11px;border-radius:3px;flex:none;margin-top:3px}
.stkey .t{color:var(--ink);font-weight:600}
.stkey .t b{display:block;font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}

/* ---- Tooltip hover ---- */
[data-tip]{position:relative}
[data-tip]:hover::after{content:attr(data-tip);position:absolute;left:50%;bottom:calc(100% + 7px);
  transform:translateX(-50%);background:var(--ink);color:#fff;font-size:11.5px;font-weight:600;
  padding:6px 9px;border-radius:6px;white-space:pre;z-index:30;pointer-events:none;
  box-shadow:0 4px 14px rgba(15,27,46,.22)}

/* ---- Grid kartu ---- */
.g2{display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));gap:16px;
  align-items:start;margin-bottom:16px}
.g2 .card{margin-bottom:0}

/* ---- Toolbar filter (satu baris, DI ATAS semua chart) ---- */
.toolbar{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:12px 16px;margin-bottom:16px;
  display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.toolbar input[type=search]{padding:9px 12px;border:1px solid var(--border-strong);border-radius:8px;
  font-family:inherit;font-size:13px;background:var(--surface-2);min-width:220px;color:var(--ink)}
.toolbar input:focus{outline:none;border-color:var(--primary);background:#fff;
  box-shadow:0 0 0 3px var(--primary-soft)}
.toolbar .sp{flex:1}
.toolbar .ct{font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}

/* ---- Tabel: header sortable + kolom numerik ---- */
th a{color:var(--muted);text-decoration:none;display:inline-flex;gap:4px;align-items:center}
th a:hover{color:var(--primary)}
th a .ar{font-size:9px;opacity:.9}
th[aria-sort] a{color:var(--primary-dark)}
.pager{display:flex;gap:8px;align-items:center;justify-content:flex-end;margin-top:12px;
  font-size:12.5px;color:var(--muted)}
.pager a,.pager span.cur{padding:5px 10px;border:1px solid var(--border-strong);border-radius:7px;
  text-decoration:none;color:var(--ink);background:var(--surface);font-weight:600}
.pager span.cur{background:var(--primary);color:#fff;border-color:var(--primary)}
.pager span.off{padding:5px 10px;border:1px solid var(--border);border-radius:7px;
  color:var(--faint);background:var(--surface-2)}

/* ---- Keadaan kosong ---- */
.empty{text-align:center;padding:44px 24px;color:var(--muted)}
.empty .ic{width:46px;height:46px;margin:0 auto 14px;border-radius:13px;background:var(--primary-soft);
  display:grid;place-items:center;color:var(--primary)}
.empty .ic svg{width:24px;height:24px}
.empty h3{font-size:15px;font-weight:700;color:var(--ink);margin-bottom:6px}
.empty p{font-size:13px;max-width:430px;margin:0 auto 16px;line-height:1.6}

/* ---- Meter progres batch ----
   Fill membawa severity, track adalah step terang dari ramp yang sama -- bukan
   grey netral, supaya track tidak terbaca sebagai kategori keempat di sebelah
   segmen status. */
.meter{height:12px;border-radius:7px;background:var(--primary-soft);overflow:hidden}
.meter .mf{height:100%;background:var(--primary);border-radius:7px 0 0 7px;
  transition:width .3s ease}
.meter .mf[style*="width:100%"]{border-radius:7px}

@media (max-width:620px){
  .hero .big{font-size:40px}
  .bars .b{grid-template-columns:1fr;gap:3px}
  .bars .bl{white-space:normal}
}

/* Forced colors / cetak: fill kehilangan warnanya, jadi identitas tidak boleh
   bergantung pada fill. Label langsung + nilai di ujung bar sudah ada di
   markup, dan setiap chart punya table view -- jadi tidak ada yang hilang. */
@media (forced-colors:active){
  .bars .bf,.stack .sg,.meter .mf{border:1px solid CanvasText}
}

/* ---- Baris agregat chart 1 ("Lainnya" / "Tanpa PIHK") ----
   Angka, bukan bar. Ia dipisahkan garis supaya terlihat jelas bahwa ia TIDAK
   berada di skala yang sama dengan bar di atasnya. */
.aggr{margin-top:12px;padding-top:11px;border-top:1px dashed var(--border-strong);
  display:flex;flex-direction:column;gap:7px}
.aggr .ai{display:grid;grid-template-columns:minmax(120px,230px) auto auto;gap:12px;
  align-items:baseline}
.aggr .al{font-size:12.5px;color:var(--muted);font-style:italic}
.aggr .av{font-size:13px;font-weight:700;color:var(--ink);text-align:right}
.aggr .ap{font-size:11.5px;color:var(--faint);text-align:right;min-width:46px}

/* ======== Badge status (halaman Roster) ========
   Ini palet STATUS, bukan kategorikal. Konsekuensinya: 8 StatusRoster TIDAK
   menjadi 8 hue. Hanya empat slot yang berwarna, dari palet status 4-slot yang
   sudah dijalankan lewat scripts/validate_palette.js terhadap surface #FFFFFF:
     #1E7A54,#C88A2E,#C43D2B,#4a3aa7  --pairs all  -> ALL CHECKS PASS
   Satu WARN: kontras #C88A2E = 2,95. WARN itu BUKAN bisa diabaikan -- ia
   mewajibkan label terlihat atau table view. Di sini teks label selalu ada di
   dalam badge-nya sendiri, jadi kewajibannya dipenuhi oleh konstruksi dan bukan
   oleh disiplin: tidak ada cara merender badge ini tanpa teksnya.

   Teks badge memakai warna statusnya sendiri, bukan token ink. Itu pengecualian
   yang disengaja dari aturan "teks memakai token teks": badge ADALAH mark-nya,
   bukan teks di sebelah mark. Nilai dan label di sel lain tetap token ink.

   Slot 'tinjau' adalah violet #4a3aa7 dan ia milik TIMEOUT. Grey --muted sebagai
   gantinya GAGAL chroma floor dan normal-vision floor (dE 11,6 < 15); merah akan
   membuat timeout terbaca sebagai verdict -- persis kesalahan baca yang seluruh
   desain ini dibangun untuk mencegah. */
.bdg{display:inline-block;font-size:11.5px;font-weight:700;padding:2px 8px;border-radius:999px;
  border:1px solid transparent;white-space:nowrap;line-height:1.5}
.bdg.ok{background:var(--ok-soft);color:var(--ok);border-color:#bfe0d1}
.bdg.warn{background:var(--warn-soft);color:#8a5e13;border-color:#ecd8a8}
.bdg.bad{background:var(--danger-soft);color:var(--danger);border-color:#f0c4bb}
.bdg.tinjau{background:#eceafa;color:#4a3aa7;border-color:#cdc6ef}
.bdg.netral{background:var(--surface-2);color:var(--muted);border-color:var(--border)}
/* Jumlah percobaan: angka di sebelah badge, bukan badge kedua. Ia tidak membawa
   severity, jadi ia tidak boleh mengambil satu pun slot warna status. */
.bdg-n{font-size:11px;color:var(--faint);margin-left:5px}

/* Tile ringkasan yang sekaligus tautan filter. .stat.ok/.warn/.bad sudah ada di
   atas dan mewarnai .v; dua slot sisanya ditambahkan di sini supaya tile dan
   badge memakai satu himpunan warna yang sama, bukan dua yang akan menyimpang. */
.stat-link{display:block;text-decoration:none;color:inherit}
.stat-link:hover{border-color:var(--border-strong);background:var(--surface-2)}
.stat-link.aktif{border-color:var(--primary);box-shadow:0 0 0 3px var(--primary-soft)}
.stat.tinjau .v{color:#4a3aa7} .stat.netral .v{color:var(--muted)}

/* Tooltip yang MEMBUNGKUS. [data-tip] bawaan memakai white-space:pre karena
   isinya pendek; catatan() di StatusRoster/Outcome beberapa kalimat, dan tanpa
   ini tooltipnya jadi satu baris selebar beberapa layar lalu terpotong di tepi
   viewport -- teks penjelas yang tidak bisa dibaca sampai habis. */
.tipw[data-tip]:hover::after{white-space:normal;width:300px;text-align:left;
  font-weight:500;line-height:1.5}

/* Tabel roster tidak memakai batas tinggi .scroll (320px): 50 baris butuh
   ruangnya, dan scroll di dalam scroll membuat pager di bawah tabel hanya
   terlihat setelah operator menggulir dua panel yang berbeda.
   `overflow` TETAP auto dan tidak diubah ke visible: tabel ini 7 kolom dan di
   layar sempit ia harus tetap bisa digulir ke samping. Dengan max-height
   dilepas, sumbu vertikalnya tidak pernah menggulir karena isinya selalu pas. */
.scroll-tall{max-height:none}
