/* ============================================================
   admin.css — design system PMS v2 (sumber aturan: design-system.md)
   Warna adalah LEGENDA: hijau=sukses/aktif, amber=perhatian,
   merah=bahaya/menunggu aksi, abu=netral/nonaktif, biru=aksi utama.
   Jangan hardcode hex baru di halaman — tambah kelas bernama di sini.
   ============================================================ */

:root {
    --brand:          #1687e8;
    --brand-hover:    #0968bd;
    --brand-soft:     #e8f4ff;
    --brand-from:     #1687e8;
    --brand-to:       #0968bd;
    --page-bg:        #f3f6f9;
    --text:           #17212b;
    --muted:          #718096;
    --faint:          #8f9bab;
    --line:           #e5e9ef;
    --line-soft:      #f0f3f7;
    --soft:           #f6f8fc;   /* fix penutup: dulu dipakai sebagai fallback var(--soft, #f6f8fc) di .picker-fixed tanpa deklarasi nyata — sekarang dideklarasikan, nilainya sama persis (nol perubahan rupa) */
    --sidebar-bg:     #042772;
    --ok:             #19a36f;
    --warn:           #e4a329;
    --danger:         #df5c61;
    --shadow:         0 8px 24px rgba(25,57,87,.06);
    --radius:         12px;
    --radius-control: 8px;
    --sidebar-w:      248px;
    --topbar-h:       64px;
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
    font-family: 'Inter', -apple-system, 'Segoe UI', sans-serif;
    font-size: 14px;
    color: var(--text);
    background: var(--page-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

.mono { font-family: 'Roboto Mono', monospace; }

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------- Shell: sidebar + topbar + content ---------- */

.shell { display: flex; min-height: 100vh; }

.sidebar {
    width: var(--sidebar-w);
    min-width: var(--sidebar-w);
    background: var(--sidebar-bg);
    color: #eaf6ff;
    border-right: 1px solid #1d4ed8;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
    scrollbar-width: none;
    z-index: 30;
    display: flex;
    flex-direction: column;
}
.sidebar::-webkit-scrollbar { display: none; }

.sidebar-brand {
    height: var(--topbar-h);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 20px;
    color: #fff;
    border-bottom: 1px solid rgba(255,255,255,.08);
    flex: none;
}
.brand-mark {
    width: 34px; height: 34px;
    display: grid; place-items: center;
    border-radius: 9px;
    color: var(--brand-hover); background: #fff;
    font-weight: 800; font-size: 16px;
}
.brand-name { font-weight: 700; font-size: 16px; letter-spacing: -0.01em; }
.brand-eyebrow {
    font-family: 'Roboto Mono', monospace;
    font-size: 10px; letter-spacing: .12em; text-transform: uppercase;
    color: rgba(255,255,255,.55);
}

.sidebar-nav { padding: 14px 12px; flex: 1; }
.nav-section {
    font-size: 10px; font-weight: 700; text-transform: uppercase;
    letter-spacing: .11em; color: #a9d8fa;
    padding: 12px 10px 6px;
}
.nav-item {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 10px; border-radius: 8px;
    color: rgba(255,255,255,.78);
    font-size: 14px;
    transition: background .15s ease, color .15s ease;
}
.nav-item .lucide { opacity: .65; }
.nav-item:hover, .nav-item.active { color: #fff; background: rgba(255,255,255,.17); text-decoration: none; }
.nav-item.active {
    font-weight: 600;
    box-shadow: inset 3px 0 0 #fff;
}
.nav-item.active .lucide { opacity: 1; }

/* Grup menu expandable — <details>/<summary> native (design-system §6):
   accessible tanpa JS, chevron rotate 180° saat terbuka. */
.nav-group { margin-top: 2px; }
.nav-group-head {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 10px; border-radius: 8px;
    color: rgba(255,255,255,.78);
    font-size: 14px; cursor: pointer;
    list-style: none; user-select: none;
    transition: background .15s ease, color .15s ease;
}
.nav-group-head::-webkit-details-marker { display: none; }
.nav-group-head .lucide { opacity: .65; }
.nav-group-head:hover { background: rgba(255,255,255,.1); color: #fff; }
.nav-group-label {
    flex: 1; min-width: 0;
    color: #a9d8fa; font-size: 10px; font-weight: 700;
    letter-spacing: .11em; text-transform: uppercase;
}
/* Penanda halaman aktif tetap terlihat walau grupnya sedang ditutup. .nav-group-label
   punya warna sendiri (bukan warisan), jadi hover/aktif harus menimpanya secara eksplisit. */
.nav-group-head:hover .nav-group-label { color: #fff; }
.nav-group-head.has-active { color: #fff; font-weight: 600; }
.nav-group-head.has-active .nav-group-label { color: #fff; }
.nav-group-head.has-active .lucide { opacity: 1; }
.nav-caret { transition: transform .18s ease; opacity: .5 !important; flex: none; }
.nav-group[open] > .nav-group-head .nav-caret { transform: rotate(180deg); }

.nav-sub {
    margin: 2px 0 6px 20px; padding-left: 12px;
    border-left: 1px solid rgba(255,255,255,.15);
    display: flex; flex-direction: column; gap: 1px;
}
.nav-sub .nav-item {
    position: relative;
    padding: 8px 10px; font-size: 13.5px;
}
/* tick horizontal per item subnav */
.nav-sub .nav-item::before {
    content: ''; position: absolute; left: -12px; top: 50%;
    width: 8px; height: 1px; background: rgba(255,255,255,.22);
}
.nav-sub .nav-item.active::before { background: #fff; }

/* Sub-kelompok di dalam grup module (Data Master / Transaksi / Laporan) — bisa dilipat,
   aturannya sama dengan seksi di atasnya. Dibuat jelas KLIKABEL (cursor + hover + caret):
   lipatan yang tidak terlihat sebagai tombol hanya akan ditemukan orang secara kebetulan. */
.nav-subgroup-wrap { margin: 2px 0; }
.nav-subgroup {
    display: flex; align-items: center; gap: 8px;
    padding: 7px 10px; border-radius: 7px;
    font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
    color: rgba(255,255,255,.42);
    cursor: pointer; list-style: none; user-select: none;
    transition: background .15s ease, color .15s ease;
}
.nav-subgroup::-webkit-details-marker { display: none; }
.nav-subgroup:hover { background: rgba(255,255,255,.07); color: rgba(255,255,255,.75); }
.nav-subgroup-label { flex: 1; min-width: 0; }

/* Jumlah menu di dalamnya: satu-satunya petunjuk isi saat kelompoknya tertutup. */
.nav-subgroup-count {
    font-size: 10px; font-weight: 600; letter-spacing: 0;
    color: rgba(255,255,255,.35);
    background: rgba(255,255,255,.08);
    border-radius: 999px; padding: 1px 6px;
}
/* Penanda halaman aktif tetap terlihat walau sub-kelompoknya sedang ditutup. */
.nav-subgroup.has-active { color: #fff; }
.nav-subgroup.has-active .nav-subgroup-count { background: rgba(255,255,255,.2); color: #fff; }
.nav-subgroup-wrap[open] > .nav-subgroup .nav-caret { transform: rotate(180deg); }

.nav-subitems { display: flex; flex-direction: column; gap: 1px; padding-bottom: 4px; }

.sidebar-footer {
    border-top: 1px solid rgba(255,255,255,.25);
    padding: 17px 12px 20px;
    color: #b8dfff;
    font-size: 11px;
    margin-top: auto;
}
.sidebar-footer b { color: #fff; }

.sidebar-backdrop { display: none; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
    height: var(--topbar-h);
    background: #fff;
    border-bottom: 1px solid var(--line);
    display: flex; align-items: center; gap: 14px;
    padding: 0 24px;
    position: sticky; top: 0; z-index: 20;
}
/* Breadcrumb: sumber data yang tersedia baru judul halaman (belum ada jalur navigasi
   bertingkat), jadi tampil sebagai satu ruas tebal, bukan rangkaian "A / B / C".
   (fix penutup: kelas .topbar-title yang dulu ikut dipasang di markup tidak pernah
   punya aturan CSS sendiri — dihapus dari markup, .breadcrumb sudah cukup.) */
.breadcrumb { color: var(--muted); font-size: 13px; }
.breadcrumb b { color: var(--text); }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.hamburger { display: none; }

/* user menu */
.user-menu { position: relative; }
.user-chip {
    display: flex; align-items: center; gap: 10px;
    background: none; border: none; cursor: pointer;
    padding: 6px 8px; border-radius: 10px;
    font: inherit; color: var(--text);
    transition: background .15s ease;
}
.user-chip:hover { background: var(--line-soft); }
.user-avatar {
    width: 38px; height: 38px; border-radius: 50%;
    background: #dceeff; color: var(--brand-hover);
    font-weight: 700; font-size: 13px;
    display: flex; align-items: center; justify-content: center;
    flex: none;
}
.user-name { font-weight: 500; }
.user-menu-panel {
    position: absolute; right: 0; top: calc(100% + 8px);
    min-width: 220px; background: #fff;
    border: 1px solid var(--line); border-radius: 12px;
    box-shadow: 0 12px 28px rgba(16,24,40,.12);
    overflow: hidden; z-index: 40;
}
.user-menu-head { padding: 14px 16px; border-bottom: 1px solid var(--line-soft); }
.user-menu-name { font-weight: 600; }
.user-menu-sub { font-size: 12px; color: var(--faint); }
.user-menu-panel form { margin: 0; }
.user-menu-item {
    display: flex; align-items: center; gap: 8px;
    width: 100%; padding: 11px 16px;
    background: none; border: none; cursor: pointer;
    font: inherit; color: var(--text); text-align: left;
    transition: background .15s ease;
}
.user-menu-item:hover { background: var(--line-soft); }
.user-menu-item.danger:hover { background: #fde8e8; color: #dc2626; }

/* Pemilih bahasa (dwibahasa fase 1): judul kelompok, penanda bahasa yang sedang dipakai, dan
   satu catatan kecil soal batas fase — pesan sistem & dokumen cetak masih Indonesia. */
.user-menu-section {
    padding: 10px 16px 4px;
    font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em;
    color: var(--faint);
}
.user-menu-item.is-selected { color: var(--brand); font-weight: 600; cursor: default; }
.user-menu-item[disabled] { opacity: 1; }
.user-menu-note {
    padding: 4px 16px 12px;
    font-size: 11px; line-height: 1.45; color: var(--faint);
    border-bottom: 1px solid var(--line-soft);
}

.content { padding: 24px; flex: 1; }

/* ---------- Header halaman ---------- */

.page-head-row { display: flex; justify-content: space-between; align-items: flex-end; gap: 20px; margin-bottom: 26px; }
.page-head { flex: 1; }
.page-title { margin: 0 0 6px; font-size: 26px; font-weight: 700; letter-spacing: -.04em; }
.page-sub { margin: 0; color: var(--muted); }
.head-actions { display: flex; gap: 10px; align-items: center; }
.head-actions .btn-primary, .head-actions .btn-outline,
.head-actions .btn-danger, .head-actions .btn-back { height: 40px; }

/* ---------- Kartu & panel ---------- */

.panel, .table-card, .filter-card, .stat-card {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
/* Padding kartu sekarang di .panel sendiri (membungkus head+body sekaligus, bukan dua
   lapis padding terpisah seperti sebelumnya). margin-top:20px dipertahankan di sini
   (tidak ada di snippet brief) supaya jarak antar blok tetap milik kelas, bukan halaman
   (§6 design-system.md). */
.panel { padding: 20px; margin-top: 20px; }
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 19px; }
.panel-title { margin: 0; font-size: 16px; font-weight: 600; letter-spacing: -.02em; }
/* Sub-judul opsional di bawah judul panel — kelas baru, belum dipakai halaman mana pun;
   tersedia untuk yang butuh nanti. */
.panel-sub { color: var(--muted); font-size: 12px; margin: 4px 0 0; }
/* .panel sudah punya padding sendiri (di atas) — .panel-body TIDAK menambah padding lagi,
   supaya tidak dobel. */
.panel-body p { margin: 0 0 10px; }
.panel-body p:last-child { margin-bottom: 0; }

/* margin-top sama dengan .panel/.table-card/.filter-card: seluruh blok setingkat halaman
   memakai irama 20px yang SAMA. Sebelum aturan ini ada, tiap halaman menambal sendiri
   dengan style="margin-top:20px" — dan yang lupa menambal jadi menempel ke kartu di atasnya. */
.stat-grid {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-top: 20px;
}
.stat-card { display: flex; align-items: center; gap: 14px; padding: 20px 22px; }
.stat-ico {
    width: 46px; height: 46px; border-radius: 12px;
    display: flex; align-items: center; justify-content: center;
}
.stat-num { font-size: 30px; font-weight: 700; line-height: 1.1; }
/* Varian kecil untuk kartu statistik di dalam halaman detail/laporan, di mana 30px
   terlalu besar untuk empat kartu berjajar — menggantikan font-size inline yang
   sebelumnya ditulis ulang per halaman. */
.stat-num-sm { font-size: 20px; }
.stat-label { font-size: 14px; color: var(--muted); }

/* ---------- Tint ikon (§4.1) ---------- */
/* Setiap family (blue/amber/teal/red/purple/green/gray) memakai pasangan yang SAMA dengan
   badge-nya (§4.2) — satu sumber pasangan per warna, dipakai ulang di tint dan badge.
   Tidak ada tint-orange/tint-yellow: asimetri ini sudah ada sebelum restyle (9 warna badge,
   7 tint, 4 ico) dan sengaja tidak dilengkapi — lihat §4 di design-system.md.
   Kontras (fix round 1): .tint-gray/.badge-gray TIDAK lagi memakai var(--muted) sebagai teks
   — var(--muted) di atas var(--line-soft) cuma 3.61:1 (gagal AA 4.5:1). Diganti pasangan
   tersendiri #5c6b80 di atas var(--line-soft) = 4.88:1. teal/purple/yellow/orange awalnya
   turun terlalu gelap (6.96–7.95:1) dibanding 4 pasangan sumber mockup (4.13–5.05:1) — teks
   dinaikkan lightness-nya (hue & saturasi tetap) supaya mendarat ~4.9:1, sepadan dengan
   family lain. Rincian rasio kesembilan pasangan ada di design-system.md §4.2 dan
   task-7-report.md.
   #5c6b80 (bukan #526171 milik .btn-outline/.btn-back di bawah) SENGAJA dipilih baru — lihat
   alasannya di design-system.md §4. */

.tint-blue   { background: var(--brand-soft); color: var(--brand-hover); }
.tint-amber  { background: #fff5dc; color: #a46b05; }
.tint-teal   { background: #e7f9f6; color: #09786e; }
.tint-red    { background: #fff0f0; color: #bd3d43; }
.tint-purple { background: #f6edff; color: #7e41e4; }
.tint-green  { background: #e8f8f1; color: #087c54; }
.tint-gray   { background: var(--line-soft); color: #5c6b80; }

/* ---------- Badge status (§4.2) ---------- */

.badge {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 4px 12px; border-radius: 999px;
    font-size: 12px; font-weight: 600;
}
.badge-green  { background: #e8f8f1; color: #087c54; }
.badge-amber  { background: #fff5dc; color: #a46b05; }
.badge-gray   { background: var(--line-soft); color: #5c6b80; }
.badge-blue   { background: var(--brand-soft); color: var(--brand-hover); }
.badge-teal   { background: #e7f9f6; color: #09786e; }
.badge-yellow { background: #fffac7; color: #9c5e04; }
.badge-purple { background: #f6edff; color: #7e41e4; }
.badge-red    { background: #fff0f0; color: #bd3d43; }
.badge-orange { background: #ffeee0; color: #ba3e0b; }

/* ---------- Ikon utilitas ---------- */
/* Hanya 4 dari 9 warna badge yang punya varian ico (asimetri lama, lihat catatan di atas).
   ico-* memakai token DASAR langsung (bukan pasangan tint/badge) karena ico tidak berpasangan
   dengan latar — glyph tunggal di atas latar apa pun. */

.ico-blue  { color: var(--brand); }
.ico-red   { color: var(--danger); }
.ico-green { color: var(--ok); }
.ico-amber { color: var(--warn); }

/* ---------- Tombol (§4.4) ---------- */

.btn-primary, .btn-outline, .btn-danger, .btn-back, .btn-search {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    padding: 0 18px; height: 40px;
    border-radius: var(--radius-control); font-size: 14px; font-weight: 600;
    cursor: pointer; font-family: inherit;
    transition: background .15s ease, color .15s ease, border-color .15s ease, box-shadow .15s ease;
    text-decoration: none;
    background: #fff; color: #526171; border: 1px solid var(--line);
}
.btn-primary, .btn-search {
    background: var(--brand); color: #fff; border-color: var(--brand);
    box-shadow: 0 6px 14px rgba(22,135,232,.30);
}
.btn-primary:hover, .btn-search:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.btn-outline, .btn-back {
    background: #fff; color: #526171; border: 1px solid var(--line);
}
.btn-outline:hover, .btn-back:hover { border-color: #b8c5d1; background: #f7fafc; text-decoration: none; }
/* Beda dari mockup: .btn-danger dipakai BERDIRI SENDIRI di pms_v2 (mis. tombol "Hapus" di 35
   berkas), bukan sebagai modifier di atas .btn dasar seperti mockup (class="btn danger").
   TIDAK cukup meniru mockup literal (`.btn-danger { color: var(--danger); }` saja): base
   gabungan di atas sudah punya border netral var(--line) sendiri, jadi kalau hanya `color`
   yang ditimpa, kondisi DIAM tombol Hapus dan tombol sekunder biasa jadi nyaris tidak bisa
   dibedakan (sama-sama putih + border abu, beda cuma warna teks) — dan `var(--danger)`
   `#df5c61` di atas putih cuma 3.60:1, gagal AA 4.5:1 untuk teks. Border DAN teks ditimpa ke
   `#bd3d43` (bukan token `--danger` — ini warna teks `.badge-red`, sudah ada di palet ini,
   5.37:1 di atas putih, lolos AA teks dan jauh di atas 3:1 komponen non-teks) supaya tombol
   destruktif punya sinyal visual di kondisi diam, bukan cuma saat hover — perangkat sentuh
   tidak punya hover. */
.btn-danger { color: #bd3d43; border-color: #bd3d43; }
.btn-danger:hover { background: #fff0f0; }
.btn-primary:disabled, .btn-danger:disabled, .btn-outline:disabled {
    background: #cbd5e1; color: #fff; border-color: transparent;
    cursor: not-allowed; box-shadow: none;
}
.btn-text {
    background: none; border: none; color: var(--brand);
    font: inherit; font-weight: 600; cursor: pointer; padding: 4px 6px;
}
.btn-text:hover { text-decoration: underline; }
/* Aksi merusak di dalam baris tabel. Kelas, bukan style= inline: warna aksi berbahaya
   harus satu sumber, bukan hex yang disalin ulang tiap halaman (anti-drift #11). */
.btn-text.danger { color: #bd3d43; }
/* Kolom aksi kanan pada baris tabel — sepadan dengan .num tapi untuk tombol, bukan angka. */
.col-actions { text-align: right; white-space: nowrap; }
/* Keterangan di bawah field form: batasan yang tidak terlihat dari labelnya sendiri. */
.form-hint { font-size: 12px; color: var(--faint); margin-top: 6px; line-height: 1.45; }
/* Penanda baris yang datanya belum lengkap namun sah — mis. faktor konversi masih kosong. */
.cell-missing { color: var(--faint); font-style: italic; }
/* Teks sekunder di dalam sel tabel (nama + kode, label + keterangan). Menggantikan
   style="font-size:12px;color:var(--faint)" yang disalin ulang di beberapa halaman. */
.cell-sub { font-size: 12px; color: var(--faint); }
/* Label checkbox sejajar isian lain di dalam .form-group. Menggantikan style= inline yang
   selama ini disalin ulang di tiap form. */
.form-check { display: flex; align-items: center; gap: 8px; padding: 11px 0; cursor: pointer; }

.icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--radius-control); padding: 5px 8px;
    background: #fff; border: 1px solid var(--line); cursor: pointer; color: var(--muted);
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.icon-btn:hover { color: var(--brand-hover); border-color: var(--brand); }

/* ---------- Filter bar ---------- */

.filter-card {
    display: flex; gap: 12px; align-items: center;
    padding: 14px 16px; margin-top: 20px; flex-wrap: wrap;
}
.search-field { position: relative; flex: 1; min-width: 220px; }
.search-field .lucide {
    position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
    color: var(--faint); pointer-events: none;
}
.search-field .form-input { padding-left: 42px; }

/* ---------- Tabel data ---------- */

.table-card { margin-top: 20px; overflow: hidden; }
/* Judul di atas tabel. .table-card sengaja TIDAK punya padding (tabelnya memang harus
   menempel tepi kartu), jadi .panel-head yang diletakkan di dalamnya tidak mewarisi padding
   dari mana pun: judulnya menempel ke tepi kartu sementara label kolom di bawahnya sudah
   masuk, dan keduanya tampak tidak sebaris. Padding samping disamakan dengan .table-foot
   (20px) — head dan foot adalah bingkai kartu, sejajar satu sama lain; isi tabel tetap
   memakai iramanya sendiri (10px). Bawahnya 0 karena th sudah membawa padding-top 20px. */
.table-card > .panel-head { padding: 20px 20px 0; margin-bottom: 0; }
.table-scroll { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; }
.data-table th {
    /* #9aa6b2 di target mockup ~ var(--faint) (#8f9bab) — token terdekat, sudah dipakai
       properti ini sebelum restyle ini juga. */
    color: var(--faint); font-size: 10px; font-weight: 700;
    letter-spacing: .07em; text-transform: uppercase;
    text-align: left; padding: 20px 10px;
    /* Tidak disebut snippet brief, dipertahankan: tanpa ini label header pada tabel padat
       (mis. /hris/attendance, 10 kolom) bisa pecah dua baris — regresi keterbacaan yang
       bukan maksud brief. */
    white-space: nowrap;
}
.data-table td {
    /* #495665 dipertahankan literal: tidak ada token --text/--muted/--faint yang mendekati
       nilai abu-gelap ini (lihat task-5-report.md §"selisih token"). */
    border-top: 1px solid var(--line-soft);
    padding: 13px 10px; color: #495665; font-size: 12px;
    vertical-align: middle; font-variant-numeric: tabular-nums;
}
.data-table tbody tr:hover { background: #f8fafc; }
/* Rekap absensi: akhir pekan ditandai sebaris penuh — Sabtu kuning, Minggu merah (permintaan user
   2026-10-22). Nilainya latar .badge-amber/.badge-red, warna yang sudah ada di palet. */
.data-table tbody tr.row-saturday,
.data-table tbody tr.row-saturday:hover { background: #fff5dc; }
.data-table tbody tr.row-sunday,
.data-table tbody tr.row-sunday:hover   { background: #fff0f0; }
/* Kolom pertama = identitas baris (kode, nomor, nama) → ditebalkan. Dikecualikan untuk
   .col-check: kolom checkbox murni tidak punya teks untuk ditebalkan (satu-satunya
   pemakaian nyata: PaymentForm.razor, daftar dokumen terbuka). JUGA dikecualikan baris
   .row-group/.row-total (fix round 2) — keduanya satu-satunya kelas yang pernah dipasang di
   <tr> di seluruh Components (dicek: `grep -rEn '<tr[^>]*\bclass="[^"]*"'`), dan keduanya
   sudah punya gaya td sendiri (lihat blok "Laporan keuangan" di bawah) yang HARUS tidak
   ketiban aturan ini. :not() di ancestor tr membuat exclusion ini struktural — bukan soal
   siapa menang lewat spesifisitas (lihat perhitungan di task-5-report.md §11). */
.data-table tr:not(.row-group):not(.row-total) td:first-child:not(.col-check) {
    color: var(--text); font-weight: 700;
}
.cell-link { color: var(--brand); font-weight: 600; }
.cell-link:hover { text-decoration: underline; }
.col-check { width: 1%; }
.table-empty { text-align: center; color: var(--faint); padding: 40px; }

/* Header sortir harus tidak terbedakan dari <th> biasa di baris yang sama (fix round 1) —
   <button> tidak mewarisi font-size/font-weight induknya secara bawaan, jadi semua properti
   tipografi th (§ .data-table th di atas) diulang literal di sini, bukan cuma `font: inherit`.
   .sort-th sengaja disamakan padding-nya dengan th polos (20px 10px, bukan 0 lagi) supaya
   kolom sortable dan non-sortable pada baris header yang sama sejajar persis. */
.sort-th { padding: 20px 10px; }
.sort-btn {
    display: flex; align-items: center; gap: 4px; width: 100%;
    padding: 0; background: transparent; border: 0; cursor: pointer;
    font: inherit; font-size: 10px; font-weight: 700;
    letter-spacing: .07em; text-transform: uppercase;
    color: var(--faint); text-align: left;
}
/* var(--faint) di sini SENGAJA sama dengan warna .data-table th (lihat catatan token di sana) —
   satu-satunya beda yang boleh terlihat antara header sortable dan header biasa adalah ikon
   arah sortir, plus warna brand saat hover/aktif. */
.sort-btn:hover { color: var(--brand-hover); }
.sort-btn svg { opacity: .35; flex-shrink: 0; }
.sort-btn.is-active { color: var(--brand-hover); }
.sort-btn.is-active svg { opacity: 1; }
.sort-th.num .sort-btn { justify-content: flex-end; text-align: right; }

/* Setara struktural mockup: mockup memisah `.pagination` (bar luar) dari `.pages`/`.page-btn`
   (grup tombol). pms_v2 memisah `.table-foot` (bar luar) dari `.pager`/`.pager-btn` — nilai
   target brief untuk selector `.pager` (bar luar, border-top + padding + space-between)
   diterapkan ke `.table-foot`; `.pager` (grup tombol, gap 8) TIDAK disentuh. */
.table-foot {
    display: flex; align-items: center; justify-content: space-between;
    padding: 15px 20px; border-top: 1px solid var(--line);
    color: var(--muted); font-size: 12px;
}
.table-foot-info { font-size: 13px; color: var(--faint); }
.pager { display: flex; align-items: center; gap: 8px; }
.pager-btn {
    width: 29px; height: 29px; border-radius: 6px;
    border: 1px solid var(--line); background: #fff; color: var(--muted);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
}
/* :not(:disabled) dipertahankan (bukan literal brief) — tanpa itu hover pada tombol
   prev/next yang disabled akan tetap berubah biru, padahal tidak bisa diklik.
   TIDAK ada ".pager-btn.active" (fix round 2, dihapus): Pager.razor hanya pernah merender dua
   tombol chevron prev/next ber-atribut disabled, tidak pernah tombol bernomor/kelas "active" —
   beda struktural dari mockup yang punya deretan tombol halaman, bukan sekadar beda nama kelas.
   Selector yang tidak pernah menyala menyesatkan pembaca berikutnya, jadi tidak ditulis. */
.pager-btn:hover:not(:disabled) { color: #fff; background: var(--brand); border-color: var(--brand); }
.pager-btn:disabled { color: #cbd5e1; cursor: not-allowed; }
.pager-info { font-size: 13px; color: var(--muted); }

/* ---------- Form ---------- */

.form-group { margin-bottom: 16px; }
.form-label {
    display: block; font-size: 13.5px; font-weight: 500;
    color: #334155; margin-bottom: 6px;
}
.form-label .req { color: #ef4444; }
.form-input, .form-select, .form-textarea {
    width: 100%; padding: 9px 11px; outline: none;
    border: 1px solid var(--line); border-radius: var(--radius-control);
    font-size: 14px; font-family: inherit; color: var(--text);
    background: #fff;
    transition: border-color .15s ease;
}
.form-input::placeholder { color: #cbd5e1; }
/* Fokus (klik ATAU keyboard): border-color saja, ikut mockup — tidak menambah geometri baru
   pada kontrol yang sudah berbingkai 1px. :focus-visible di bawah MENAMBAH ring box-shadow di
   atasnya (fix round 1, Ruling reviewer #2): tanpa itu tidak ada jaring pengaman bawaan browser
   sama sekali (outline sudah 'none'), dan pada form padat berjejer border-color 1px saja terlalu
   lemah sebagai penanda "ini yang sedang fokus".
   PENTING (task 9, koreksi klaim sebelumnya): untuk elemen entri teks, ring box-shadow di bawah
   BUKAN keyboard-saja — beda dari `.app-select-box:focus-visible` (tombol) di bawah. Heuristik
   `:focus-visible` browser membuat `<input>`/`<textarea>`/`<select>` SELALU cocok dengan
   `:focus-visible` kapan pun ia cocok `:focus`, termasuk fokus dari KLIK MOUSE — beda dengan
   `<button>` yang hanya cocok `:focus-visible` untuk fokus asal keyboard. Jadi ring ini muncul
   di setiap fokus (klik maupun keyboard) untuk ketiga kontrol ini, bukan cuma keyboard. */
.form-input:focus, .form-select:focus, .form-textarea:focus {
    border-color: var(--brand);
}
.form-input:focus-visible, .form-select:focus-visible, .form-textarea:focus-visible {
    box-shadow: 0 0 0 3px rgba(22,135,232,.18);
}
.form-input:disabled, .form-select:disabled {
    background: #f8fafc; color: #94a3b8; cursor: not-allowed;
}
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.form-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }
.form-span-2 { grid-column: span 2; }
.field-error { font-size: 12.5px; color: #ef4444; margin-top: 4px; }

/* Input berpasangan dengan satu tombol aksi di sebelahnya (mis. pilih pelanggan + buat baru). */
.input-with-action { display: flex; gap: 8px; align-items: center; }
.input-with-action > *:first-child { flex: 1; min-width: 0; }
.input-with-action > .btn-outline { flex: none; height: 40px; padding: 0 12px; }

/* Form singkat yang muncul di dalam form lain — sengaja beda latar supaya jelas ia lapisan
   tambahan, bukan bagian dari isian utama. */
.inline-form {
    display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
    margin-top: 10px; padding: 12px;
    background: var(--line-soft); border-radius: 10px;
}
.inline-form .form-input { flex: 1; min-width: 140px; }

/* Nilai yang sudah pasti dan tidak bisa dipilih lain (mis. company tunggal di layar module).
   Sengaja TIDAK menyerupai input: ia bukan kontrol, dan menampilkannya seperti dropdown mati
   hanya mengundang orang mengkliknya berulang kali. */
.picker-fixed {
    display: inline-flex; align-items: baseline; gap: 8px;
    padding: 9px 12px; border-radius: 10px;
    background: var(--soft); border: 1px solid var(--line-soft);
    font-size: 13.5px; color: #1e293b;
}
.picker-fixed .mono { color: var(--muted); font-size: 12.5px; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--brand); }

/* ---------- Banner form (§4.3) ---------- */

.form-banner-error, .form-banner-warn, .form-banner-success, .form-banner-ok, .form-banner-info {
    border-radius: 10px; padding: 10px 14px; font-size: 13px; margin-bottom: 14px;
}

/* Banner dipakai di DUA tempat: sebagai baris pertama .panel-body (form), dan sebagai blok
   setingkat halaman di antara kartu. Yang kedua butuh jarak atas; yang pertama tidak — kalau
   diberi jarak, ia menggantung di dalam panelnya sendiri. :not(:first-child) memisahkan
   keduanya tanpa menuntut tiap halaman memilih kelas yang berbeda. */
.form-banner-error:not(:first-child),
.form-banner-warn:not(:first-child),
.form-banner-success:not(:first-child),
.form-banner-ok:not(:first-child),
.form-banner-info:not(:first-child) {
    margin-top: 20px;
}
.form-banner-error   { background: #fff0f0; color: #bd3d43; border: 1px solid #ebbabc; }
.form-banner-warn    { background: #fff5dc; color: #a46b05; border: 1px solid #e4cc9c; }
/* .form-banner-success dan .form-banner-ok sama-sama "sukses" di §4.6 (hijau) — dua nama
   untuk kelas yang sama artinya (dipakai berdampingan hanya di galeri komponen), jadi
   pasangannya sengaja disamakan alih-alih diberi dua hijau berbeda. */
.form-banner-success { background: #e8f8f1; color: #087c54; border: 1px solid #a5d3c2; }
.form-banner-ok      { background: #e8f8f1; color: #087c54; border: 1px solid #a5d3c2; }
.form-banner-info    { background: var(--brand-soft); color: var(--brand-hover); border: 1px solid #a5caeb; }

/* ---------- Modal ---------- */

.modal-overlay {
    position: fixed; inset: 0; background: rgba(15,23,42,.45);
    display: flex; align-items: center; justify-content: center;
    z-index: 100; padding: 16px;
}
.modal {
    background: #fff; border-radius: 16px;
    width: 100%; max-width: 480px;
    box-shadow: 0 24px 60px rgba(16,24,40,.25);
    overflow: hidden;
}
.modal-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 22px 24px 0;
}
.modal-title { font-size: 19px; font-weight: 700; }
.modal-body { padding: 14px 24px; }
.modal-body p { margin: 0; color: var(--muted); }
.modal-foot {
    display: flex; justify-content: flex-end; gap: 10px;
    padding: 16px 24px 22px;
}
.modal-foot .btn-primary, .modal-foot .btn-outline, .modal-foot .btn-danger { width: 150px; }

/* ---------- Toast ---------- */

.toast-stack {
    position: fixed; right: 20px; bottom: 20px;
    display: flex; flex-direction: column; gap: 10px; z-index: 200;
}
.toast {
    display: flex; align-items: center; gap: 10px;
    min-width: 280px; max-width: 420px;
    background: #fff; border-radius: 12px;
    padding: 13px 16px;
    box-shadow: 0 12px 30px rgba(16,24,40,.18);
    animation: toast-in .18s ease;
}
.toast-success { border-left: 4px solid var(--ok); }
.toast-error { border-left: 4px solid var(--danger); }

/* Toggle jenis kas (keluar/masuk) di layar kas kecil — dua tombol besar berwarna kontras
   supaya salah arah langsung terlihat dari jauh (permintaan user 2026-10-06). Warna aktif
   memakai token semantik yang sama dengan toast: keluar = --danger, masuk = --ok. */
.cash-kind-toggle { display: flex; gap: 8px; }
.cash-kind-toggle button {
    flex: 1; min-height: 46px; padding: 0 16px;
    border: 1.5px solid var(--line); border-radius: var(--radius-control);
    background: #fff; color: var(--muted);
    font-size: 15px; font-weight: 700; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.cash-kind-toggle button.active-out { background: var(--danger); border-color: var(--danger); color: #fff; }
.cash-kind-toggle button.active-in  { background: var(--ok); border-color: var(--ok); color: #fff; }
@keyframes toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

/* ---------- Empty state ---------- */

.empty {
    min-height: 200px;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    text-align: center; gap: 6px; padding: 24px;
    color: #94a3b8; font-size: 14px;
}
.empty-ico { color: #cbd5e1; }
.empty-title { font-size: 20px; font-weight: 700; color: var(--text); margin: 8px 0 0; }
.empty p { margin: 4px 0 14px; }

/* ---------- Login split-screen (§9) — versi animasi penuh ----------
   Sumber desain: docs/design/login-preview.html (disetujui user).
   Semua gerakan non-intrusif dan mati otomatis lewat prefers-reduced-motion
   di blok responsif bawah file. Panel kiri punya 4 lapisan dekorasi:
   canvas partikel → orb cahaya → cincin conic → chip modul melayang. */

.login-page { display: flex; min-height: 100vh; }
.login-brand-panel {
    flex: 1;
    position: relative;
    overflow: hidden;
    background: var(--sidebar-bg);
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    padding: 48px;
}
.login-particles {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    opacity: .55;
}
/* @property diperlukan agar sudut conic-gradient bisa dianimasikan */
@property --login-spin { syntax: '<angle>'; inherits: false; initial-value: 0deg; }

.login-orb {
    position: absolute;
    border-radius: 50%;
    filter: blur(70px);
    pointer-events: none;
}
.login-orb-a {
    width: 480px; height: 480px;
    left: -120px; top: -140px;
    background: radial-gradient(circle at 30% 30%, rgba(22,135,232,.55), transparent 65%);
    animation: login-drift-a 14s ease-in-out infinite alternate;
}
.login-orb-b {
    width: 420px; height: 420px;
    right: -100px; bottom: -160px;
    background: radial-gradient(circle at 60% 60%, rgba(9,104,189,.35), transparent 65%);
    animation: login-drift-b 18s ease-in-out infinite alternate;
}
@keyframes login-drift-a {
    from { transform: translate(0,0) scale(1); }
    to   { transform: translate(90px, 60px) scale(1.15); }
}
@keyframes login-drift-b {
    from { transform: translate(0,0) scale(1.1); }
    to   { transform: translate(-80px,-70px) scale(.95); }
}

.login-spin-ring {
    position: absolute;
    width: 620px; height: 620px;
    border-radius: 50%;
    background: conic-gradient(from var(--login-spin),
        transparent 0%, rgba(22,135,232,.28) 12%,
        transparent 26%, rgba(255,255,255,.10) 38%,
        transparent 55%, rgba(22,135,232,.22) 72%, transparent 90%);
    -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1.5px));
            mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 1.5px));
    animation: login-spin 36s linear infinite;
    pointer-events: none;
}
@keyframes login-spin { to { --login-spin: 360deg; } }

/* Chip modul bisnis melayang. Hover = membesar (properti `scale`, BUKAN transform,
   agar tidak menimpa keyframes float yang juga menganimasikan transform) +
   keterangan singkat terbuka lewat trik grid rows 0fr→1fr. */
.float-chip {
    position: absolute;
    display: flex; flex-direction: column; align-items: flex-start;
    padding: 10px 14px;
    border-radius: 10px;
    background: rgba(255,255,255,.08);
    border: 1px solid rgba(255,255,255,.14);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: rgba(255,255,255,.85);
    font-size: 12.5px; font-weight: 600;
    letter-spacing: .02em;
    box-shadow: 0 10px 30px rgba(2,16,50,.35);
    cursor: default;
    transition:
        scale .28s cubic-bezier(.22,.9,.3,1),
        background .2s ease,
        border-color .2s ease,
        box-shadow .28s ease;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    pointer-events: auto;
}
.float-chip:hover {
    scale: 1.09;
    z-index: 3;
    background: rgba(255,255,255,.14);
    border-color: rgba(124,196,255,.55);
    box-shadow: 0 16px 44px rgba(2,16,50,.5);
    animation-play-state: paused;
}
.chip-head { display: flex; align-items: center; gap: 8px; }
.float-chip svg { width: 15px; height: 15px; stroke: #7cc4ff; flex: none; }

.chip-desc {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    max-width: 210px;
    transition: grid-template-rows .3s cubic-bezier(.22,.9,.3,1), opacity .25s ease .05s;
}
.chip-desc > small {
    overflow: hidden;
    font-weight: 400;
    font-size: 11.5px;
    line-height: 1.5;
    letter-spacing: 0;
    color: rgba(255,255,255,.72);
}
.float-chip:hover .chip-desc { grid-template-rows: 1fr; opacity: 1; }
.float-chip:hover .chip-desc > small { padding-top: 5px; }

.chip-water { left: 9%;  top: 20%;  animation-name: login-floaty-a; animation-duration: 11s; }
.chip-gym     { right: 11%; top: 26%;  animation-name: login-floaty-b; animation-duration: 13s; animation-delay: -4s; }
.chip-excav   { left: 13%; bottom: 18%; animation-name: login-floaty-b; animation-duration: 15s; animation-delay: -7s; }
.chip-coins   { right: 9%; bottom: 24%; animation-name: login-floaty-a; animation-duration: 12s; animation-delay: -2s; }

@keyframes login-floaty-a {
    from { transform: translateY(-14px) rotate(-1.2deg); }
    to   { transform: translateY(16px)  rotate(1.2deg); }
}
@keyframes login-floaty-b {
    from { transform: translateY(18px)  rotate(1.5deg); }
    to   { transform: translateY(-16px) rotate(-1.5deg); }
}

.login-brand-inner { max-width: 440px; position: relative; z-index: 2; }

.brand-chip {
    display: inline-flex; align-items: center; justify-content: center;
    background: #fff; color: var(--sidebar-bg);
    font-weight: 700; font-size: 15px;
    border-radius: var(--radius); padding: 10px 16px; margin-bottom: 28px;
    position: relative;
    animation: login-rise .7s cubic-bezier(.22,.9,.3,1) both;
}
/* denyut lembut sekali tiap 6 detik — penanda hidup tanpa mengganggu */
.brand-chip::after {
    content: '';
    position: absolute; inset: 0;
    border-radius: inherit;
    border: 2px solid rgba(124,196,255,.7);
    opacity: 0;
    animation: login-pulse-ring 6s ease-out 2s infinite;
}
@keyframes login-pulse-ring {
    0%   { opacity: .8; transform: scale(1); }
    18%  { opacity: 0;  transform: scale(1.25); }
    100% { opacity: 0;  transform: scale(1.25); }
}

.login-brand-inner .brand-eyebrow {
    margin-bottom: 12px; letter-spacing: .16em;
    animation: login-rise .7s .08s cubic-bezier(.22,.9,.3,1) both;
}
.brand-headline {
    font-family: 'Lora', Georgia, serif;
    font-size: 50px; font-weight: 600; line-height: 1.12;
    margin: 0 0 16px;
    animation: login-rise .7s .16s cubic-bezier(.22,.9,.3,1) both;
}
/* sorot cahaya menyapu headline secara berkala */
.brand-headline .sheen {
    background: linear-gradient(110deg, #fff 40%, #9fd4ff 50%, #fff 60%);
    background-size: 250% 100%;
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
    animation: login-sheen 3.2s ease 1s infinite;
}
@keyframes login-sheen {
    0%, 55%  { background-position: 120% 0; }
    85%,100% { background-position: -60% 0; }
}

.brand-sub { color: rgba(255,255,255,.75); font-size: 15px; line-height: 1.6; margin: 0; animation: login-rise .7s .24s cubic-bezier(.22,.9,.3,1) both; }

/* garis progres kecil di bawah sub — isyarat sistem aktif */
.brand-meter {
    margin-top: 34px; height: 3px; width: 220px;
    border-radius: 99px;
    background: rgba(255,255,255,.12);
    overflow: hidden;
    animation: login-rise .7s .32s cubic-bezier(.22,.9,.3,1) both;
}
.brand-meter i {
    display: block; height: 100%; width: 40%;
    border-radius: inherit;
    background: linear-gradient(90deg, transparent, #7cc4ff, transparent);
    animation: login-meter-slide 2.8s ease-in-out infinite;
}
@keyframes login-meter-slide {
    from { transform: translateX(-120%); }
    to   { transform: translateX(320%); }
}
@keyframes login-rise {
    from { opacity: 0; transform: translateY(22px); }
    to   { opacity: 1; transform: translateY(0); }
}

.login-form-panel {
    flex: 1; background: var(--page-bg);
    display: flex; align-items: center; justify-content: center;
    position: relative;
    overflow: hidden;
    padding: 48px 24px;
}
/* ornamen titik halus — sangat pelan, hampir tak terasa */
.form-dots {
    position: absolute; inset: 0;
    background-image: radial-gradient(rgba(22,135,232,.10) 1.2px, transparent 1.2px);
    background-size: 26px 26px;
    animation: login-dots-drift 60s linear infinite;
    pointer-events: none;
}
@keyframes login-dots-drift {
    from { background-position: 0 0; }
    to   { background-position: 260px 260px; }
}

.login-card { width: 100%; max-width: 392px; position: relative; z-index: 1; }
.login-title {
    font-family: 'Lora', Georgia, serif;
    font-size: 38px; font-weight: 600; margin: 0 0 6px;
    animation: login-rise .6s .05s cubic-bezier(.22,.9,.3,1) both;
}
.login-sub { color: var(--muted); margin: 0 0 24px; animation: login-rise .6s .12s cubic-bezier(.22,.9,.3,1) both; }
.login-label {
    display: block;
    font-family: 'Roboto Mono', monospace;
    font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
    color: var(--muted); margin-bottom: 7px;
    transition: color .15s ease;
}
.form-banner-error { animation: login-drop-in .35s cubic-bezier(.22,.9,.3,1) both; }
@keyframes login-drop-in {
    from { opacity: 0; transform: translateY(-10px); }
    to   { opacity: 1; transform: translateY(0); }
}
.login-card .form-group:nth-of-type(1) { animation: login-rise .6s .20s cubic-bezier(.22,.9,.3,1) both; }
.login-card .form-group:nth-of-type(2) { animation: login-rise .6s .27s cubic-bezier(.22,.9,.3,1) both; }
.login-card :focus-within > .login-label,
.login-card .form-group:focus-within .login-label { color: var(--brand); }
.login-card .form-input:focus { animation: login-focus-pop .25s ease; }
@keyframes login-focus-pop {
    0%   { box-shadow: 0 0 0 0 rgba(22,135,232,.25); }
    100% { box-shadow: 0 0 0 4px rgba(22,135,232,.15); }
}
.login-submit { width: 100%; margin-top: 8px; height: 44px; position: relative; overflow: hidden; animation: login-rise .6s .34s cubic-bezier(.22,.9,.3,1) both; }
/* kilau menyapu tombol saat hover */
.login-submit::before {
    content: '';
    position: absolute; top: 0; bottom: 0;
    left: -80px; width: 56px;
    background: linear-gradient(105deg, transparent, rgba(255,255,255,.45), transparent);
    transform: skewX(-18deg);
    transition: left .5s ease;
    pointer-events: none;
}
.login-submit:hover::before { left: calc(100% + 30px); }
.login-submit svg { transition: transform .2s ease; }
.login-submit:hover svg { transform: translateX(4px); }

/* Toggle lihat/sembunyikan password — vanilla JS (halaman static SSR, tanpa circuit).
   Ikon mata & mata-coret ditumpuk; satu yang terlihat di-switch lewat class. */
.pw-wrap { position: relative; }
.pw-wrap .form-input { padding-right: 44px; }
.pw-toggle {
    position: absolute; right: 6px; top: 50%;
    translate: 0 -50%;
    width: 34px; height: 34px;
    display: grid; place-items: center;
    border: none; background: none;
    border-radius: var(--radius-control);
    color: var(--faint);
    cursor: pointer;
    transition: color .15s ease, background .15s ease;
}
.pw-toggle:hover { color: var(--brand); background: var(--brand-soft); }
.pw-toggle:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.pw-toggle .icon-eye-off { display: none; }
.pw-toggle.revealed .icon-eye { display: none; }
.pw-toggle.revealed .icon-eye-off { display: block; }

.login-footnote {
    margin-top: 26px;
    font-size: 12px; color: var(--faint);
    text-align: center;
    animation: login-rise .6s .42s cubic-bezier(.22,.9,.3,1) both;
}

/* ---------- Pohon CoA (chart of accounts) ---------- */

.coa-grid {
    display: grid; grid-template-columns: 1fr 1fr; gap: 20px;
    align-items: start; margin-top: 20px;
}
.coa-panel { margin-top: 0; }
.coa-panel-head .panel-title { display: flex; align-items: center; gap: 8px; }
.coa-body { padding: 6px 0 10px; }
.coa-row {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 8px 24px; border-bottom: 1px solid #f4f6fa;
    font-size: 13.5px;
}
.coa-row:last-child { border-bottom: none; }
.coa-row:hover { background: #f8fafc; }
.coa-row.is-parent { cursor: pointer; }
.coa-name { display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1; }
.coa-name .cell-link { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.coa-parent-link { font-weight: 600; }
.coa-caret { color: var(--faint); flex: none; }
.coa-lock { color: var(--faint); flex: none; }
.coa-code { font-size: 11.5px; color: #cbd5e1; flex: none; }
/* leader titik-titik ala neraca — makin dalam makin panjang */
.coa-leader { flex: none; border-bottom: 2px dotted #cbd5e1; height: 1px; }
.depth-1 .coa-leader { width: 22px; }
.depth-2 .coa-leader { width: 48px; }
.depth-3 .coa-leader { width: 74px; }
.depth-4 .coa-leader { width: 100px; }
.coa-amount {
    font-family: 'Roboto Mono', monospace; font-size: 13px;
    font-variant-numeric: tabular-nums; white-space: nowrap;
    color: var(--text);
}
.coa-amount.muted { color: #cbd5e1; }
.coa-amount.neg { color: #dc2626; }

@media (max-width: 1100px) {
    .coa-grid { grid-template-columns: 1fr; }
}

/* ---------- Laporan keuangan (L/R, neraca, konsolidasi) ---------- */

/* baris judul seksi di dalam tabel laporan */
.data-table tr.row-group td {
    background: #f8fafc; color: var(--faint);
    font-size: 11.5px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em;
    padding: 10px 24px;
}
.data-table tr.row-total td { border-top: 1px solid var(--line-soft); font-weight: 600; }
.report-note {
    font-size: 12.5px; color: var(--faint);
    padding: 12px 24px; border-top: 1px solid var(--line-soft);
}
.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.num.neg { color: #dc2626; }
.num.pos { color: #047857; }
/* Angka "perlu perhatian" — dipakai bucket aging 61-90 hari; tonanya sama dengan
   teks amber .alert-warning agar satu keluarga warna perhatian. */
.num.warn { color: #a46b05; }

/* daftar checkbox multi-pilih (mis. company yang dikonsolidasi) */
.check-list {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px;
}
.check-item {
    display: flex; align-items: center; gap: 8px;
    border: 1px solid var(--line); border-radius: 10px;
    padding: 8px 12px; font-size: 13.5px; cursor: pointer;
}
.check-item:hover { background: #f8fafc; }

/* ---------- AppSelect: dropdown ber-pencarian (design-system §7.3) ---------- */

.app-select { position: relative; width: 100%; }
.app-select-box {
    width: 100%; display: flex; align-items: center; gap: 8px;
    padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius-control);
    background: #fff; font-size: 14px; color: var(--text);
    text-align: left; cursor: pointer;
    transition: border-color .15s ease;
}
.app-select-box:hover:not(:disabled) { border-color: #cbd5e1; }
/* Ring fokus keyboard-saja — BEDA dari .form-input:focus-visible di atas (yang sudah dikoreksi
   di task 9: itu tidak keyboard-saja). Di sini genuinely keyboard-saja karena `.app-select-box`
   adalah elemen `<button>`, bukan input teks: heuristik `:focus-visible` browser TIDAK
   menganggap klik mouse pada `<button>` sebagai fokus yang "kelihatan perlu ring" (beda
   perlakuan dari `<input>`/`<textarea>`/`<select>`), jadi ring ini hanya nyala saat fokus
   datang dari keyboard (Tab/panah), bukan klik. */
.app-select-box:focus-visible {
    outline: none; border-color: var(--brand);
    box-shadow: 0 0 0 3px rgba(22,135,232,.18);
}
/* Pola "custom-select": saat terbuka, kotak bersudut atas saja dan sambung tanpa celah ke
   panel di bawahnya (lihat top:100% + border-top:0 pada .app-select-panel) — bersama mereka
   membentuk satu bentuk menyatu berbingkai biru, bukan dua kotak terpisah. */
.app-select.open .app-select-box {
    outline: none;
    border-color: var(--brand);
    border-radius: var(--radius-control) var(--radius-control) 0 0;
}
/* Kebalikannya saat panel naik (kelas .drop-up dipasang js/app-select.js setelah mengukur
   ruang di bawah kotak): yang menyatu dengan panel adalah sisi ATAS kotak. */
.app-select.open.drop-up .app-select-box {
    border-radius: 0 0 var(--radius-control) var(--radius-control);
}
.app-select-box:disabled { background: #f8fafc; color: #94a3b8; cursor: not-allowed; }
.app-select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-select-value.is-placeholder { color: #cbd5e1; }
.app-select-caret { color: var(--faint); flex: none; transition: transform .15s ease; }
.app-select.open .app-select-caret { transform: rotate(180deg); }

/* Backdrop transparan menutup panel saat klik di luar — tanpa listener JS global. */
.app-select-backdrop { position: fixed; inset: 0; z-index: 40; }

.app-select-panel {
    position: absolute; z-index: 41; top: 100%; left: 0; right: 0;
    background: #fff; border: 1px solid var(--brand); border-top: 0;
    border-radius: 0 0 var(--radius-control) var(--radius-control);
    box-shadow: 0 8px 18px rgba(25,57,87,.12);
    overflow: hidden;
}
/* Panel naik: bertumpu pada sisi atas kotak, bingkai & sudutnya dicerminkan. */
.app-select.drop-up .app-select-panel {
    top: auto; bottom: 100%;
    border-top: 1px solid var(--brand); border-bottom: 0;
    border-radius: var(--radius-control) var(--radius-control) 0 0;
    box-shadow: 0 -8px 18px rgba(25,57,87,.12);
}
.app-select-search {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 12px; border-bottom: 1px solid var(--line-soft);
    color: var(--faint);
}
.app-select-search-input {
    flex: 1; min-width: 0; border: none; outline: none;
    font-size: 13.5px; color: var(--text); background: transparent;
}
.app-select-search-input::placeholder { color: #cbd5e1; }
.app-select-options { max-height: 280px; overflow-y: auto; padding: 4px; }
.app-select-option {
    width: 100%; display: flex; flex-direction: column; gap: 1px;
    padding: 8px 10px; border: none; border-radius: 8px;
    background: transparent; text-align: left; cursor: pointer;
    font-size: 13.5px; color: var(--text);
}
/* Brief target memakai ".app-select-option.selected" (nama mockup) — komponen nyata
   (AppSelect.razor) memasang ".is-selected", bukan ".selected"; dipetakan ke situ.
   Fix round 1 (Ruling reviewer #1): is-selected dan is-highlight TIDAK BOLEH identik.
   AppSelect.razor me-reset _highlight ke 0 setiap panel dibuka (bukan ke indeks item
   terpilih) — jadi item terpilih (mis. #5 dari 20) dan item tersorot-panah (mis. #1) bisa
   jadi DUA BARIS BERBEDA sekaligus. Kalau keduanya dapat gaya sama, tidak ada cara membedakan
   "ini yang sudah dipilih" dari "ini yang sedang disorot panah". Dipisah, dan sengaja lewat
   PROPERTI YANG TIDAK TUMPANG TINDIH (bukan urutan/spesifisitas) supaya tetap terbaca saat
   SATU baris kebetulan is-selected DAN is-highlight bersamaan (mis. panah mendarat tepat di
   item yang sudah terpilih): hover/highlight mengisi latar (background), is-selected menandai
   lewat bar kiri (box-shadow inset, pola sama dengan .nav-item.active di atas) + teks tebal,
   TANPA background sendiri. Baris yang ketiganya (hover+highlight+selected) menumpuk akan
   menampilkan latar DAN bar DAN tebal sekaligus — bukan konflik, murni gabungan. */
.app-select-option:hover, .app-select-option.is-highlight { background: var(--brand-soft); color: var(--brand-hover); }
.app-select-option.is-selected { color: var(--brand-hover); font-weight: 700; box-shadow: inset 3px 0 0 var(--brand); }
.app-select-option.is-placeholder { color: #94a3b8; }
.app-select-option-hint { font-size: 12px; color: var(--faint); }
.app-select-option.is-selected .app-select-option-hint { color: var(--brand); opacity: .75; }
.app-select-empty { padding: 16px 12px; text-align: center; font-size: 13px; color: var(--faint); }

/* Kontainer ber-overflow dilonggarkan saat panel terbuka agar tidak terpotong.
   .table-scroll ikut di sini (ISS-019): `overflow-x:auto` memaksa overflow-y jadi auto, dan
   itulah yang memangkas panel dropdown di baris dokumen (PR/PO, SO Quarry, penyesuaian stok)
   sampai setinggi sisa tabel — bukan soal z-index. .modal (overflow:hidden) sama nasibnya. */
.panel:has(.app-select.open),
.table-card:has(.app-select.open),
.table-scroll:has(.app-select.open),
.filter-card:has(.app-select.open),
.modal:has(.app-select.open) { overflow: visible; }

/* Panel dropdown lepas dari lebar kolomnya: di grid baris dokumen kolom item 220px dan kolom
   satuan 90px — terlalu sempit untuk kode + nama + hint yang sedang dicari. */
.app-select-panel { min-width: min(360px, 90vw); }

/* Matriks permission per module (halaman role) */
.perm-group { margin-top: 16px; }
.perm-group + .perm-group { margin-top: 20px; }
.perm-group-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.perm-group-title {
    font-size: 11.5px; font-weight: 700; text-transform: uppercase;
    letter-spacing: .04em; color: var(--faint);
}
.perm-group-count { font-size: 12px; color: #94a3b8; }
.perm-group-all {
    margin-left: auto; display: flex; align-items: center; gap: 6px;
    font-size: 12.5px; color: #475569; cursor: pointer; user-select: none;
}
.perm-code { font-family: 'Roboto Mono', monospace; font-size: 12.5px; }
.perm-name { font-size: 12px; color: var(--faint); }

/* ---------- State umum ---------- */

button:focus-visible, a:focus-visible, summary:focus-visible {
    outline: 2px solid var(--brand); outline-offset: 2px;
}
.inactive { color: #94a3b8; text-decoration: line-through; }

#blazor-error-ui {
    display: none;
    position: fixed; bottom: 0; left: 0; right: 0;
    background: #fef6e7; color: #92400e;
    border-top: 1px solid #f6dfae;
    padding: 12px 24px; z-index: 300; font-size: 13px;
}
#blazor-error-ui .dismiss { float: right; cursor: pointer; }

/* ---------- Responsive ---------- */

@media (max-width: 1100px) {
    .stat-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 1024px) {
    .sidebar {
        position: fixed; left: 0; top: 0;
        transform: translateX(-100%);
        transition: transform .2s ease;
    }
    .shell.drawer-open .sidebar { transform: translateX(0); }
    .shell.drawer-open .sidebar-backdrop {
        display: block; position: fixed; inset: 0;
        background: rgba(15,23,42,.45); z-index: 25;
    }
    .hamburger { display: inline-flex; }
    .user-name { display: none; }
}

@media (max-width: 900px) {
    .form-row, .form-row-3 { grid-template-columns: 1fr; }
    .form-span-2 { grid-column: auto; }
    .login-brand-panel { display: none; }
    .brand-headline { font-size: 34px; }
    .float-chip, .form-dots { display: none; }
}

@media (max-width: 768px) {
    .content { padding: 16px 12px; }
    .stat-grid { grid-template-columns: 1fr; }
    .page-title { font-size: 22px; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition: none !important; animation: none !important; }
    /* Lapisan dekorasi login tidak berguna tanpa gerakan — sembunyikan agar panel bersih.
       Canvas partikel disembunyikan dari sisi CSS; skrip js/login-page.js juga cek sendiri. */
    .login-particles, .login-orb, .login-spin-ring, .float-chip, .form-dots, .brand-meter i { display: none; }
}


/* ---------- Halaman cetak (PrintLayout) ---------- */

.print-page { background: #fff; min-height: 100vh; padding: 24px; }
.print-actions { display: flex; gap: 10px; margin-bottom: 20px; }

.print-sheet {
    max-width: 780px; margin: 0 auto;
    color: #111; font-size: 13px;
}
.print-head {
    display: flex; justify-content: space-between; align-items: flex-start; gap: 20px;
    border-bottom: 2px solid #111; padding-bottom: 12px;
}
.print-company { font-size: 18px; font-weight: 700; }
.print-doc-type { font-size: 12px; letter-spacing: .08em; color: #444; margin-top: 2px; }
.print-number { text-align: right; font-size: 13px; }
.print-number .mono { font-size: 15px; font-weight: 700; }

.print-meta { display: flex; gap: 40px; flex-wrap: wrap; margin: 16px 0; }
.print-label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: #666; }

.print-table { width: 100%; border-collapse: collapse; margin-top: 8px; }
.print-table th, .print-table td { padding: 7px 8px; border-bottom: 1px solid #ddd; text-align: left; }
.print-table th { font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: #444; }
.print-table .num { text-align: right; font-family: 'Roboto Mono', monospace; }
.print-table tfoot td { border-top: 2px solid #111; border-bottom: none; }

.print-notes { margin-top: 14px; }

/* Slip gaji 4c-3 memuat lebih dari satu tabel (komponen, iuran) plus penjelasan pajak, jadi
   ia butuh judul antar-bagian dan catatan kecil. Keduanya khusus lembar cetak — tidak dipakai
   di layar aplikasi. */
.print-section-title {
    margin: 18px 0 4px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: #444;
}
.print-note { margin-top: 8px; font-size: 12px; color: #555; }

/* Tanda tangan: tiga kolom dengan garis di ATAS nama perannya. */
.print-signatures { display: flex; gap: 30px; margin-top: 48px; }
.print-signatures > div { flex: 1; text-align: center; }
.print-signatures span {
    display: block; padding-top: 6px; border-top: 1px solid #111;
    font-size: 12px; color: #444;
}

/* Saat benar-benar dicetak: tombol dan margin layar tidak ikut. */
@media print {
    .print-page { padding: 0; }
    .print-actions { display: none; }
    /* `:not(.cf-sheet)` supaya batas 8 inci lembar continuous form tidak ikut dicabut di sini.
       Tanpa itu keduanya sama-sama satu kelas dan yang menang hanya urutan baris di berkas
       ini — pengecualian yang hilang diam-diam begitu ada yang menyusun ulang file. */
    .print-sheet:not(.cf-sheet) { max-width: none; }
}

/* ---------- Continuous form 9,5 x 5,5 inci (dot matrix, kertas polos) ----------
   Dipakai lembar cetak QO/SO Rental. Dot matrix tidak punya warna latar dan tidak menangani
   font proporsional dengan rapi untuk kolom angka — karena itu monospace dan tanpa background.

   PENTING soal `@page <nama>` + `page: <nama>`: setiap `@page` bernama HARUS punya nama
   sendiri dan tidak boleh ada `@page` tanpa nama di file ini — `@page` adalah at-rule
   tingkat DOKUMEN (bukan terbatas ke selector yang memakainya), jadi `@page` tanpa nama akan
   dipakai SEMUA halaman yang tidak menunjuk `page:` miliknya sendiri, termasuk lembar ini.
   Properti CSS `page:` sendiri HANYA didukung Chromium (Chrome/Edge) — Firefox dan Safari
   mengabaikannya, sehingga ukuran kertas di sana jatuh ke default peramban. Cetak lembar ini
   dari Chrome/Edge. */
@page cf {
    size: 9.5in 5.5in;
    margin: 0.3in 0.35in;
}
.cf-sheet {
    page: cf;
    font-family: 'Roboto Mono', monospace;
    /* 12px (dulu 10px), semua ukuran di bawah ikut naik x1,2 — permintaan user 2026-10-26:
       lembar SO yang umumnya 2-3 baris menyisakan banyak kertas kosong. SENGAJA tidak
       dipenuhkan: sisa tinggi itu cadangan untuk baris ke-3, Catatan, Alasan, dan blok
       rekening PPN, supaya lembar tidak tumpah ke halaman kedua. */
    font-size: 12px;
    color: #000;
    line-height: 1.35;
    /* ISS-025 — LEBARNYA DIBATASI 8 INCI DENGAN SENGAJA, walau kotak isi @page cf masih
       8,8 inci. Printer dot matrix untuk form 9,5 inci adalah mesin 80 kolom: area cetak
       efektifnya hanya +-8 inci, sisanya tidak pernah terjangkau kepala cetak berapa pun
       margin yang diminta CSS. Sebelum ini isi lembar memenuhi 8,8 inci penuh, jadi yang
       jatuh di luar jangkauan persis bagian paling kanan tiap baris: value meta (labelnya
       ikut tercetak, isinya tidak), kolom Sub Total, angka Total/Tax, dan ekor judul
       dokumen. Sisa 0,8 inci di kanan adalah zona aman itu — bukan ruang kosong yang boleh
       "dirapikan". Gejalanya hanya muncul di kertas: "Simpan sebagai PDF" tidak punya
       batas mekanis, jadi PDF-nya selalu terlihat benar. */
    max-width: 8in;
    /* Rata KIRI, bukan `margin: 0 auto` warisan .print-sheet: kalau ditengahkan, zona aman
       0,8 inci itu terbelah dua dan separuhnya pindah ke kiri — sisi yang justru tidak
       bermasalah, karena baris cetak selalu mulai dari kiri. */
    margin-left: 0;
    margin-right: auto;
}
.cf-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; }
.cf-head-left { display: flex; gap: 10px; align-items: flex-start; }
.cf-logo { max-height: 50px; max-width: 50px; }
.cf-company { font-size: 16px; font-weight: 700; letter-spacing: .02em; }
.cf-tagline { font-size: 12px; font-weight: 700; }
.cf-business { font-size: 11px; }
.cf-contact { font-size: 11px; }
.cf-doc-title { font-size: 17px; font-weight: 700; letter-spacing: .12em; white-space: nowrap; }

/* Dua kolom TETAP, bukan `space-between`: yang terakhir menempelkan blok meta kanan ke tepi
   kanan lembar, sehingga value-nya (bagian paling kanan tiap baris) yang pertama hilang saat
   terpotong. Dengan grid, blok kanan mulai di titik tetap dan tumbuh ke kanan — label DAN
   value-nya berhenti jauh sebelum tepi. Ini juga yang digambarkan sketsa spec §4: `Number :`
   berdiri di kolom tetap sekitar tengah halaman, bukan rata kanan. */
.cf-meta { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 10px; }
.cf-meta-row { display: flex; gap: 6px; }
.cf-meta-label { min-width: 84px; }

.cf-table { width: 100%; border-collapse: collapse; margin-top: 10px; }
.cf-table th, .cf-table td { padding: 2px 4px; text-align: left; vertical-align: top; }
.cf-table thead th { border-top: 1px solid #000; border-bottom: 1px solid #000; font-weight: 700; }
.cf-table .num { text-align: right; }
/* Baris nomor dokumen di dalam thead (F4): thead berulang di tiap halaman saat dicetak,
   jadi baris ini adalah penanda lembar lanjutan tanpa mengklaim nomor halaman yang bisa
   salah. Digayakan kecil & tanpa border tambahan supaya tidak mengganggu delapan kolom
   tabel di halaman pertama. */
.cf-table-continuation td { font-size: 10px; color: #444; border: 0; padding: 1px 4px; text-align: right; }
.cf-totals { margin-top: 6px; display: flex; justify-content: flex-end; }
.cf-totals td { padding: 1px 4px; }
/* Instruksi transfer dokumen ber-PPN; pre-line supaya baris yang diketik admin di setelan PPN
   tetap terpisah di kertas. */
.cf-payment { margin-top: 8px; white-space: pre-line; font-weight: 700; }
.cf-foot { display: flex; justify-content: space-between; margin-top: 18px; }
/* "By : ___" diturunkan dua baris (permintaan user 2026-10-27) supaya ada ruang tanda tangan di
   atas garisnya. Satuan em, bukan px: ikut ukuran font lembar bila kelak diubah lagi. */
.cf-sign { min-height: 48px; margin-top: calc(2 * 1.35em); }

/* ---------- Struk 80mm (printer thermal POS) ---------- */
/* Area cetak efektif kertas 80mm adalah ±72mm. Monospace supaya kolom angka rata tanpa
   tabel berbingkai — printer thermal tidak menggambar garis halus dengan baik.

   `@page receipt` DIBERI NAMA dengan sengaja (lihat catatan panjang di atas .cf-sheet):
   `@page` tanpa nama berlaku ke seluruh dokumen, jadi akan membajak ukuran kertas lembar
   cetak lain (mis. QO/SO Rental 9,5x5,5 inci) begitu admin.css dimuat di halaman itu.
   `page:` di bawah ini juga cuma jalan di Chromium — cetak struk ini dari Chrome/Edge; di
   peramban lain ukurannya jatuh ke default peramban, bukan 80mm. */
@page receipt {
    size: 80mm auto;
    margin: 0;
}
.receipt {
    page: receipt;
    width: 72mm; margin: 0 auto; padding: 4mm 0;
    background: #fff; color: #000;
    font-family: 'Roboto Mono', 'Courier New', monospace;
    font-size: 11px; line-height: 1.35;
}
.receipt-center { text-align: center; }
.receipt-company { font-size: 13px; font-weight: 700; }
.receipt-title { font-weight: 700; letter-spacing: .08em; margin-top: 2px; }
.receipt-rule { border: 0; border-top: 1px dashed #000; margin: 5px 0; }
.receipt-row { display: flex; justify-content: space-between; gap: 6px; }
.receipt-row > span:first-child { color: #333; }
.receipt-row > span:last-child { text-align: right; word-break: break-word; }
.receipt-line-name { font-weight: 600; }
.receipt-line-detail { display: flex; justify-content: space-between; padding-left: 6px; }
.receipt-line-discount { display: flex; justify-content: space-between; padding-left: 6px; color: #333; }
.receipt-total { display: flex; justify-content: space-between; font-size: 13px; font-weight: 700; margin-top: 2px; }
.receipt-warn { font-weight: 700; text-align: center; margin: 4px 0; }
.receipt-notes { margin-top: 4px; white-space: pre-wrap; }
.receipt-footer { margin-top: 8px; text-align: center; font-size: 10px; color: #333; }

@media print {
    .receipt { width: 72mm; margin: 0 4mm; padding: 2mm 0; }
}

/* ---------- Peringatan: lonceng topbar + panel halaman ---------- */
/* Warna mengikuti legenda yang sama dengan badge: amber=perhatian, merah=bahaya. */

.bell { position: relative; }
.bell-btn {
    position: relative;
    display: flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; border-radius: 10px;
    background: none; border: none; cursor: pointer;
    color: var(--muted);
    transition: background .15s ease, color .15s ease;
}
.bell-btn:hover { background: var(--line-soft); color: var(--text); }
.bell-badge {
    position: absolute; top: 2px; right: 2px;
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 999px; background: #dc2626; color: #fff;
    font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
}
.bell-panel {
    position: absolute; right: 0; top: calc(100% + 8px);
    width: 360px; max-height: 420px; overflow-y: auto;
    background: #fff; border: 1px solid var(--line); border-radius: 12px;
    box-shadow: 0 12px 28px rgba(16,24,40,.12);
    z-index: 40;
}
.bell-head {
    padding: 12px 16px; border-bottom: 1px solid var(--line-soft);
    font-weight: 600; font-size: 13px;
}
.bell-empty, .alert-empty {
    display: flex; align-items: center; gap: 8px;
    padding: 16px; color: var(--faint); font-size: 13px;
}
.alert-empty { padding: 4px 0; }

.alert-list { display: flex; flex-direction: column; gap: 8px; }
.bell-row, .alert-row {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 16px; color: var(--text); text-decoration: none;
    border-bottom: 1px solid var(--line-soft);
    transition: background .15s ease;
}
.alert-row {
    border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px;
}
.bell-row:last-child { border-bottom: none; }
.bell-row:hover, .alert-row:hover { background: var(--line-soft); text-decoration: none; }

.alert-count {
    flex: none;
    min-width: 30px; height: 30px; padding: 0 8px;
    border-radius: 8px; font-size: 13px; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
}
.alert-warning .alert-count { background: #fff5dc; color: #a46b05; }
.alert-danger  .alert-count { background: #fff0f0; color: #bd3d43; }
.alert-info    .alert-count { background: var(--brand-soft); color: var(--brand-hover); }

.alert-text { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.alert-title { font-size: 13px; font-weight: 600; }
.alert-detail {
    font-size: 12px; color: var(--faint);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- Pintasan module di halaman muka ---------- */

.shortcut-grid {
    display: grid; gap: 14px; margin-bottom: 20px;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}
.shortcut-card {
    display: flex; align-items: center; gap: 12px;
    padding: 16px; background: #fff; border: 1px solid var(--line);
    border-radius: var(--radius); color: var(--text); text-decoration: none;
    box-shadow: var(--shadow);
    transition: border-color .15s ease, transform .15s ease;
}
.shortcut-card:hover { border-color: var(--brand); text-decoration: none; transform: translateY(-1px); }
.shortcut-name { font-weight: 600; font-size: 14px; }
.shortcut-sub { font-size: 12px; color: var(--faint); }

/* ── Grid roster (HRIS) ───────────────────────────────────────────────────────
   Pegawai × tanggal — bukan tabel list, jadi ia punya kelasnya sendiri. Inline
   style dilarang sejak tahap 7 Accounting; seluruh warna memakai token yang ada. */
.roster-grid { width: 100%; border-collapse: collapse; font-size: 13px; }
.roster-grid th, .roster-grid td { border: 1px solid var(--line); padding: 0; text-align: center; }
.roster-grid th.roster-name, .roster-grid td.roster-name {
    text-align: left;
    padding: 8px 12px;
    min-width: 200px;
    position: sticky;
    left: 0;
    background: #fff;
    z-index: 1;
}
.roster-grid th.roster-date {
    padding: 6px 2px;
    min-width: 34px;
    font-weight: 600;
    color: var(--muted);
    background: var(--line-soft);
}
.roster-grid th.roster-date.is-holiday { background: #fde8e8; color: #b91c1c; }
.roster-cell {
    display: block;
    width: 100%;
    min-height: 30px;
    padding: 6px 2px;
    border: none;
    background: transparent;
    font: inherit;
    color: var(--muted);
    cursor: pointer;
}
.roster-cell:hover { background: var(--line-soft); }
.roster-cell.is-assigned { color: var(--text); font-weight: 600; }
.roster-cell.is-off { color: var(--faint); }
.roster-cell.is-empty { color: var(--faint); }
td.roster-day.is-holiday { background: #fef2f2; }
.roster-scroll { overflow-x: auto; }
.roster-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; color: var(--muted); font-size: 12px; }
