/* WA Rotator - Admin Stylesheet (Ant Design style) */
:root {
    /* color-scheme (§ 11 ARCHITECTURE.md Dark Mode Tahap 6, 2026-08-31) — BUG NYATA ditemukan
       lewat sensus: tanpa properti ini, browser TIDAK TAHU halaman kita "sengaja" gelap, jadi
       kontrol native (checkbox, radio, scrollbar, date/time picker bawaan) tetap dirender
       versi TERANG bawaan OS walau semua CSS kustom kita sudah gelap — kelihatan sebagai
       kotak putih kecil menyala sendiri (ketemu nyata di checkbox `admin/sticky_set.php`).
       `light dark` di sini artinya "app ini support dua-duanya, browser silakan pilih sesuai
       `prefers-color-scheme`" — dipasang cuma di base, TIDAK perlu diulang di blok
       `@media (prefers-color-scheme: dark)` (Tahap 2) karena bagian itu memang mengikuti OS.
       Override manual (Tahap 5, `[data-theme="dark"]`/`[data-theme="light"]`) HARUS declare
       ulang `color-scheme` eksplisit satu nilai (bukan `light dark`) di bloknya masing-masing
       supaya kontrol native ikut PAKSAAN manual, bukan balik nurut OS lagi. */
    color-scheme: light dark;
    --primary: #1677ff;
    --primary-hover: #4096ff;
    /* --primary-fill/--primary-fill-hover [2026-09-02, diminta Yusuf] — token TERPISAH khusus
       elemen SOLID berlatar biru dengan teks PUTIH di atasnya (.btn-primary, pagination aktif,
       range date-picker terisi, dst). Di light mode nilainya SAMA dengan --primary (tidak ada
       perubahan visual) — tapi di dark mode SENGAJA beda dari --primary biasa, lihat komentar
       di blok dark kenapa dipisah. */
    --primary-fill: #1677ff;
    --primary-fill-hover: #4096ff;
    --primary-bg: #e6f4ff;
    --green: #25d366;
    --bg: #f5f5f5;
    --card-bg: #ffffff;
    --border: #d9d9d9;
    --border-light: #f0f0f0;
    --text: #1f1f1f;
    --text-secondary: #595959;
    /* --tooltip-bg (§ 11 ARCHITECTURE.md Dark Mode Tahap 6, 2026-08-31) — BUG NYATA ditemukan
       dari laporan Yusuf: `.global-tooltip-bubble` dan `.icon-btn[data-tooltip]::after` dua-
       duanya pakai `background: var(--text); color: #fff;` — gaya "tooltip terbalik" yang
       SENGAJA (bubble gelap + teks putih, TIDAK ikut kartu di sekitarnya) supaya tetap kontras
       terhadap latar APA PUN. Tapi karena dipakai `var(--text)` (yang MEMANG dirancang buat
       flip ikut tema), begitu dark mode aktif `--text` jadi TERANG (#e6e6e6) — bubble-nya
       ikut jadi terang, teks putih di atasnya nyaris tidak kebaca (~1.05:1). Token BARU ini
       SENGAJA TIDAK didefinisikan ulang di blok dark manapun — nilainya harus tetap gelap di
       KEDUA tema, itu intinya "tooltip terbalik". Nilainya = `--text` versi light (kebetulan
       sama), tapi sekarang independen dari perubahan tema `--text`. */
    --tooltip-bg: #1f1f1f;
    /* --muted dinaikkan dari #8c8c8c (§ 10 Fase 4, 2026-08-31) — nilai lama cuma ~3.4:1 kontras
       di atas putih, di bawah standar WCAG AA teks normal (4.5:1). #767676 adalah abu-abu gelap
       yang PALING TERANG yang masih lolos 4.5:1 di atas putih (~4.54:1) — dipilih supaya
       perubahan visual seminimal mungkin sambil tetap lolos standar, bukan asal digelapkan.
       Di atas var(--bg) #f5f5f5 (dipakai badge-gray, hover row) rasio-nya sedikit lebih rendah
       (~4.2:1) — teks di context itu selalu pendek/tebal (badge, label), jadi tetap lolos
       ambang "teks besar" WCAG (3:1) dengan aman. */
    --muted: #767676;
    --danger: #ff4d4f;
    --danger-bg: #fff1f0;
    --success: #389e0d;
    --success-bg: #f6ffed;
    --success-border: #b7eb8f;
    --danger-border: #ffccc7;
    --warning: #fa8c16;
    --warning-bg: #fff7e6;
    --purple: #722ed1;
    --purple-bg: #f9f0ff;
    /* --leave/--leave-hover (§ 10 Fase 1, docs/DESIGN_SYSTEM.md 2026-08-31) — warna badge
       status "Izin" (operator lagi cuti), sebelumnya hex mentah #ad4e00/#8a3e00 langsung di
       .badge-leave, tidak ditokenisasi sejak ditambahkan 2026-08-30. */
    --leave: #ad4e00;
    --leave-hover: #8a3e00;
    /* --surface-subtle (§ 10 Fase 1) — "permukaan" abu sangat muda DI ATAS card putih: field
       readonly/disabled, table header sticky, row hover, kartu UTM/trend. Sebelumnya hex mentah
       #fafafa ditulis literal berulang belasan kali (5+ file PHP + beberapa rule di file ini) —
       semuanya nilai identik buat maksud yang sama (permukaan sedikit beda dari card putih di
       sekitarnya), jadi digabung satu token, bukan dibiarkan berulang. SENGAJA beda dari --bg
       (#f5f5f5, background halaman) — --bg dipakai di BELAKANG card, --surface-subtle di ATAS
       card, dua konteks berbeda walau mirip shade-nya. */
    --surface-subtle: #fafafa;
    --radius: 8px;
    --radius-sm: 6px;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.06);
    --shadow-md: 0 4px 16px rgba(0,0,0,0.08);
    /* --gap-sm/md/lg (§ 10 Fase 3, docs/DESIGN_SYSTEM.md 2026-08-31) — 3 grid utama
       (.stat-grid, .trend-grid, .two-col) sebelumnya pakai gap: 16px/12px/20px sebagai angka
       literal masing-masing, tanpa hubungan eksplisit satu sama lain. Ditokenisasi supaya jelas
       ini SATU skala kecil-sedang-besar yang disengaja, bukan tiga angka lepas yang kebetulan
       beda. TIDAK mengubah gap flexbox lain di file ini (form-row, checkbox-group, dst) — itu
       di luar cakupan Fase 3, lihat docs/DESIGN_SYSTEM.md § 10.3. */
    --gap-sm: 12px;
    --gap-md: 16px;
    --gap-lg: 20px;
    /* --chart-1..10 (§ 11 ARCHITECTURE.md, Dark Mode Tahap 4, 2026-08-31) — palet 10-warna
       chart per-operator di admin/stats.php, sebelumnya array JS hardcoded (`const palette =
       [...]`) tanpa token CSS sama sekali (dicatat sebagai backlog di
       docs/DESIGN_SYSTEM.md § 10.1 #5). Ditokenisasi SEKARANG karena momentumnya sama dengan
       kebutuhan Tahap 4: chart butuh varian gelap juga, jadi sekalian dibereskan daripada
       dikerjakan dua kali. Nilai LIGHT persis sama seperti array lama (tidak ada perubahan
       visual chart di light mode). Chart.js TIDAK baca `var(--...)` langsung — dibaca lewat
       `getComputedStyle()` di JS saat chart dibuat/tema berubah, lihat admin/stats.php dan
       operator/stats.php. */
    --chart-1: #1677ff;
    --chart-2: #25d366;
    --chart-3: #fa8c16;
    --chart-4: #722ed1;
    --chart-5: #eb2f96;
    --chart-6: #13c2c2;
    --chart-7: #faad14;
    --chart-8: #2f54eb;
    --chart-9: #fa541c;
    --chart-10: #a0d911;
}

/* ============================================================
   DARK MODE (§ 11 ARCHITECTURE.md, Tahap 2, 2026-08-31)
   ============================================================
   Aktivasi lewat `prefers-color-scheme` OS SAJA (keputusan Yusuf 2026-08-31, § 11.1 — mulai
   dari cara paling murah/tanpa risiko dulu, toggle manual TIDAK dibangun kecuali nanti ternyata
   dibutuhkan). Karena murni CSS media query, TIDAK ada JS, TIDAK ada localStorage, dan TIDAK ada
   risiko FOUC (browser sudah tahu skema warna SEBELUM paint pertama, beda dari toggle manual
   yang butuh script blocking di <head>).

   Setiap warna di bawah BUKAN cuma "invert warna terang" — tiap satu dicek kontras WCAG (formula
   luminance relatif, pola sama seperti § 10 Fase 4b) terhadap SEMUA konteks pakainya, karena
   beberapa token dipakai DUA cara berlawanan (contoh: --primary/--danger dipakai sebagai teks DI
   ATAS card gelap, TAPI JUGA sebagai background solid tombol/badge DENGAN teks putih di atasnya —
   dua kebutuhan itu saling tarik-menarik: warna makin terang, teks-di-atasnya makin gampang
   kebaca, TAPI putih-di-atasnya makin susah kebaca). Baseline pembanding yang dipakai BUKAN
   4.5:1 murni teoretis, tapi rasio yang SUDAH DITERIMA di versi light mode yang sudah jalan
   (contoh: --warning di light mode cuma ~2.2:1, di bawah AA — dark mode di sini JUSTRU lebih
   baik ~6.5:1, tapi tidak diklaim "sempurna WCAG AA di semua tempat" kalau baseline light-nya
   sendiri belum kesitu, supaya dokumentasi ini jujur bukan overclaim).

   --primary dark dipilih LEBIH TERANG dari yang "ideal" buat teks putih di atasnya (~3.54:1,
   sedikit di bawah baseline light ~4.10:1 tapi masih lolos ambang "teks besar" 3:1) KARENA
   --primary jauh lebih sering dipakai sebagai teks/ikon di atas card gelap (nav link, judul,
   banyak ikon) daripada sebagai background solid tombol (cuma beberapa tempat) — diprioritaskan
   kasus yang lebih sering, bukan asal pilih.

   --green (brand WhatsApp, § 10 Fase 4c) SENGAJA TIDAK punya varian gelap — identitas brand
   tidak ikut berubah ikut tema, sama seperti WhatsApp sendiri tidak mengubah warna hijaunya di
   dark mode resminya.

   Chart.js (grafik di admin/stats.php & operator/stats.php) TIDAK otomatis ikut blok ini —
   warnanya di-set lewat JS options, bukan CSS variable, jadi butuh penanganan terpisah di
   Tahap 4 (§ 11.2). Blok ini murni cakupan CSS/HTML.

   Shadow & border SUDAH diverifikasi visual di Tahap 3 (2026-08-31, browser beneran, bukan
   cuma hitungan) — popup/dropdown terbaca jelas "melayang" (didominasi `--surface-subtle`/
   `--card-bg` yang lebih terang + border, BUKAN shadow-nya sendiri yang kontribusinya minim
   secara visual di atas bg gelap — sesuai dugaan, bukan bug), border card/table terlihat
   sebagai garis tipis yang jelas ADA (sama karakter "tipis tapi terlihat" seperti light mode,
   bukan hilang/rusak). Kedua hal ini TIDAK diubah nilainya — sudah dikonfirmasi bekerja.

   Nilai yang sama dituliskan DUA KALI di bawah — sekali di dalam `@media` (guard
   `:not([data-theme="light"])` supaya override manual "Terang" tetap menang walau OS gelap),
   sekali lagi di `:root[data-theme="dark"]` (buat override manual "Gelap" walau OS terang) —
   pola CSS-variable-theming standar tanpa preprocessor, lihat § 11 Tahap 5 (2026-08-31).

   [Diralat 2026-09-02 ronde 1, diminta Yusuf] `--primary`/`--primary-hover` versi dark tadinya
   `#3c89e8`/`#69a6ee` (Tahap 2) dinaikkan ke nilai LIGHT mode (`#1677ff`/`#4096ff`) karena di
   tombol `.btn-primary` solid + teks putih warna lama kelihatan pudar/pucat "kayak lagi di-
   hover". Diterapkan waktu itu ke SEMUA pemakaian `var(--primary)` sekaligus.

   [Diralat 2026-09-02 ronde 2, diminta Yusuf] TERNYATA perubahan ronde 1 kebablasan — dicek
   lagi di UI beneran, warna terang itu bagus KHUSUS di elemen solid+teks putih, tapi elemen
   yang pakai `--primary` sebagai WARNA TEKS/BORDER/IKON di atas card gelap (nav link, judul
   section, border fokus, dll — MAYORITAS pemakaian token ini, lihat komentar di atas) jadi
   ikut berubah padahal tidak diminta. `--primary`/`--primary-hover` DIKEMBALIKAN ke nilai lama
   `#3c89e8`/`#69a6ee` (dipakai buat teks/border/ikon — non-fill), dan token BARU
   `--primary-fill`/`--primary-fill-hover` (`#1677ff`/`#4096ff`, warna hasil ronde 1) dipakai
   KHUSUS di elemen solid berlatar biru + teks putih (`.btn-primary`, pagination halaman aktif,
   date-picker range terisi) — lihat definisi `--primary-fill` di `:root` atas kenapa dipisah
   jadi token baru, bukan digabung lagi ke satu nilai. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg: #141414;
        --card-bg: #1f1f1f;
        --surface-subtle: #262626;
        --border: #434343;
        --border-light: #303030;
        --text: #e6e6e6;
        --text-secondary: #b3b3b3;
        --muted: #8c8c8c;
        --primary: #3c89e8;
        --primary-hover: #69a6ee;
        --primary-fill: #1677ff;
        --primary-fill-hover: #4096ff;
        --primary-bg: #102a4c;
        --danger: #e5484d;
        --danger-bg: #2c1618;
        --danger-border: #5c2223;
        --success: #49aa19;
        --success-bg: #162312;
        --success-border: #2b4416;
        --warning: #d89614;
        --warning-bg: #2b2111;
        --purple: #b18cea;
        --purple-bg: #22163a;
        --leave: #a85a1c;
        --leave-hover: #c96f24;
        --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
        --shadow-md: 0 4px 16px rgba(0,0,0,0.45);
        --chart-1: #4c90f0;
        --chart-2: #64d88f;
        --chart-3: #eda14f;
        --chart-4: #9569d3;
        --chart-5: #e25aa5;
        --chart-6: #5ae2e2;
        --chart-7: #edb84f;
        --chart-8: #5a75e2;
        --chart-9: #ed774f;
        --chart-10: #bce458;
    }
}
/* Override manual "Gelap" (§ 11 Tahap 5) — dipasang JS lewat `data-theme="dark"` di <html>
   kalau user pilih eksplisit di menu Tampilan, TERLEPAS dari `prefers-color-scheme` OS.
   Nilainya SAMA PERSIS blok @media di atas — lihat komentar di atas kenapa dua kali. */
:root[data-theme="dark"] {
    color-scheme: dark;
    --bg: #141414;
    --card-bg: #1f1f1f;
    --surface-subtle: #262626;
    --border: #434343;
    --border-light: #303030;
    --text: #e6e6e6;
    --text-secondary: #b3b3b3;
    --muted: #8c8c8c;
    --primary: #3c89e8;
    --primary-hover: #69a6ee;
    --primary-fill: #1677ff;
    --primary-fill-hover: #4096ff;
    --primary-bg: #102a4c;
    --danger: #e5484d;
    --danger-bg: #2c1618;
    --danger-border: #5c2223;
    --success: #49aa19;
    --success-bg: #162312;
    --success-border: #2b4416;
    --warning: #d89614;
    --warning-bg: #2b2111;
    --purple: #b18cea;
    --purple-bg: #22163a;
    --leave: #a85a1c;
    --leave-hover: #c96f24;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
    --shadow-md: 0 4px 16px rgba(0,0,0,0.45);
    --chart-1: #4c90f0;
    --chart-2: #64d88f;
    --chart-3: #eda14f;
    --chart-4: #9569d3;
    --chart-5: #e25aa5;
    --chart-6: #5ae2e2;
    --chart-7: #edb84f;
    --chart-8: #5a75e2;
    --chart-9: #ed774f;
    --chart-10: #bce458;
}
/* Override manual "Terang" (§ 11 Tahap 5) — semua token warna SUDAH benar tanpa perlu diulang
   (base `:root` di atas MEMANG nilai light, guard `:not([data-theme="light"])` di blok
   `@media` sudah mencegah dark override menimpanya) — satu-satunya hal yang perlu di-declare
   ulang di sini cuma `color-scheme`, supaya kontrol native (checkbox, scrollbar, dst, § 11
   Tahap 6) ikut PAKSAAN manual "Terang" walau OS-nya Gelap, bukan balik nurut OS lagi. */
:root[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; margin: 0; padding: 0; }

/* `* { margin: 0 }` di atas juga men-strip margin bawaan browser dari <p> — beberapa kali
   kejadian teks <p> polos (biasanya caption/keterangan muted sebelum box/form berikutnya)
   dibuat dengan cuma margin-top custom (mis. "margin-top:-6px" buat rapetin ke heading di
   atasnya) TANPA margin-bottom, jadi nempel MEPET ke elemen berikutnya — bukan cuma sekali,
   pola yang sama ketemu berulang di halaman berbeda (Edit Campaign, Cuti/Izin operator).
   Baseline ini pasang jarak bawah default yang wajar buat SEMUA <p>, biar kelalaian "lupa
   kasih margin-bottom" tidak lagi bikin teks nempel ke box/elemen berikutnya — inline style
   yang butuh nilai lain (termasuk override ke 0, lihat .sticky-locked-note p) tetap menang
   karena spesifisitasnya lebih tinggi dari selector tag polos ini. */
p { margin-bottom: 12px; }

body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background: var(--bg);
    color: var(--text);
    font-size: 14px;
    -webkit-font-smoothing: antialiased;
}
/* line-height:1.5 di elemen `p` (§ update 2026-09-02, diminta Yusuf setelah audit — awalnya
   diminta di `body` biar sekaligus benerin belasan `<p style="color:var(--muted)">` inline
   tersebar di banyak halaman (sticky_set.php, admin_accounts.php, operators.php,
   change_password.php, pengajuan.php, operator/stats.php, dst) yang kalimatnya cukup panjang
   buat wrap ke 2-3 baris, sama-sama tanpa line-height eksplisit seperti .section-note/
   .form-group small yang sudah dibetulkan duluan. DISEMPITKAN ke selector `p` saja (bukan
   `body`) setelah ditemukan risiko: tombol/input/badge/sel tabel pakai TINGGI dari
   padding+line-height (bukan height tetap), jadi kalau di-set di `body`, SEMUA elemen itu ikut
   sedikit lebih tinggi — perubahan visual yang jauh lebih luas dari yang diminta, dan tidak
   bisa diverifikasi visual di project ini (Browser tool dilarang dipakai buat wa-rotator).
   Semua kasus yang ditemukan di audit ini `<p>` atau sudah ke-cover class lain — scoping ke
   `p` dapat efek yang sama persis TANPA risiko itu. */
p { line-height: 1.5; }

/* Dark Mode Tahap 1 audit (§ 11 ARCHITECTURE.md, 2026-08-31): semua `background: #fff`/
   `#ffffff` literal di file ini (navbar, dropdown, input, tombol, popup kalender, login box,
   dst — 16 titik) diganti `var(--card-bg)`, DAN `.table code` (`#f5f5f5`) diganti `var(--bg)`.
   Nilai token-nya SAMA PERSIS seperti literal lamanya di light mode (`--card-bg: #ffffff`,
   `--bg: #f5f5f5`) — TIDAK ADA perubahan visual di tahap ini. Ini prasyarat murni: kalau dark
   mode dinyalakan (Tahap 3) SEBELUM ini dibereskan, elemen-elemen itu akan tetap putih terang
   di tengah halaman gelap karena nilainya tidak ikut token apa pun. */
/* NAVBAR */
.navbar {
    background: var(--card-bg);
    color: var(--text);
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 24px;
    height: 60px;
    position: sticky;
    top: 0;
    z-index: 100;
    border-bottom: 1px solid var(--border-light);
    box-shadow: 0 1px 4px rgba(0,0,0,0.03);
}
.nav-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 700;
    font-size: 16px;
    letter-spacing: 0.2px;
    color: var(--text);
    text-decoration: none;
}
.brand-icon {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--green);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    flex-shrink: 0;
}
.nav-logo {
    display: block;
    width: 24px;
    height: 24px;
    max-width: 24px;
    max-height: 32px;
    flex-shrink: 0;
}
.nav-links {
    display: flex;
    align-items: center;
    gap: 4px;
}
.nav-links a {
    color: var(--text-secondary);
    text-decoration: none;
    margin-left: 4px;
    padding: 8px 14px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    font-weight: 500;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    transition: background 0.15s, color 0.15s;
}
.nav-links a i { font-size: 13px; }
.nav-links a:hover { color: var(--primary); background: var(--primary-bg); }
.nav-links a.active { color: var(--primary); background: var(--primary-bg); font-weight: 600; }
.nav-logout { color: var(--danger) !important; margin-left: 12px !important; }
.nav-logout:hover { background: var(--danger-bg) !important; }

/* NAV RIGHT — wrapper buat .nav-links + settings dropdown + hamburger, supaya ketiganya
   selalu nempel kanan sebagai satu grup (§ 9.3 Tahap 1) — .navbar sendiri tetap
   justify-content:space-between antara .nav-brand vs grup ini. */
.nav-right { display: flex; align-items: center; gap: 8px; }

/* NAV TOGGLE (mobile hamburger) */
.nav-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    color: var(--text);
    font-size: 16px;
    cursor: pointer;
    flex-shrink: 0;
}
.nav-toggle:hover { color: var(--primary); border-color: var(--primary); }

/* SETTINGS DROPDOWN (§ 9.3 Tahap 1) — mengelompokkan Kelola Admin/Ganti Password/Logout
   (dianggap jarang dipakai) jadi satu ikon gear + dropdown, tampil di SEMUA ukuran layar
   (beda dari .nav-toggle yang cuma tampil di mobile) — dua mekanisme show/hide independen,
   sengaja tidak saling terkait state-nya. */
.settings-dropdown { position: relative; }
.settings-toggle.active { color: var(--primary); border-color: var(--primary); background: var(--primary-bg); }
.settings-menu {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    background: var(--card-bg);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-sm);
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
    min-width: 180px;
    padding: 6px;
    z-index: 101;
    flex-direction: column;
    gap: 2px;
}
.settings-menu.open { display: flex; }
.settings-menu a {
    color: var(--text-secondary);
    text-decoration: none;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: background 0.15s, color 0.15s;
}
.settings-menu a:hover { color: var(--primary); background: var(--primary-bg); }
.settings-menu a.active { color: var(--primary); background: var(--primary-bg); font-weight: 600; }
.settings-menu a.nav-logout { margin-left: 0 !important; }

/* Entry "Tampilan" (§ 11 ARCHITECTURE.md Tahap 5, 2026-08-31) — dipilih model AKORDION (expand
   di tempat, di dalam dropdown yang sama), BUKAN flyout ke samping seperti referensi screenshot
   Yusuf — flyout butuh logic posisi (kanan/kiri tergantung sisa ruang layar) yang jauh lebih
   rumit dan gampang pecah di layar sempit/mobile, sementara akordion mencapai tujuan yang sama
   (satu entry, buka jadi 3 pilihan) dengan interaksi lebih sederhana & konsisten di semua
   ukuran layar. Struktur `<button>` (bukan `<a>`) karena ini AKSI JS murni, bukan navigasi. */
.settings-theme-toggle {
    width: 100%;
    background: none;
    border: none;
    cursor: pointer;
    font: inherit;
    color: var(--text-secondary);
    text-decoration: none;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: background 0.15s, color 0.15s;
}
.settings-theme-toggle:hover { color: var(--primary); background: var(--primary-bg); }
.settings-theme-toggle .settings-theme-current { color: var(--muted); font-weight: 400; margin-left: auto; }
.settings-theme-toggle .settings-theme-arrow { transition: transform 0.15s; flex-shrink: 0; }
.settings-theme-toggle[aria-expanded="true"] .settings-theme-arrow { transform: rotate(90deg); }
.settings-theme-options {
    display: none;
    flex-direction: column;
    gap: 2px;
    padding: 2px 0 2px 20px;
}
.settings-theme-options.open { display: flex; }
.settings-theme-option {
    width: 100%;
    background: none;
    border: none;
    cursor: pointer;
    font: inherit;
    color: var(--text-secondary);
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: background 0.15s, color 0.15s;
}
.settings-theme-option:hover { color: var(--primary); background: var(--primary-bg); }
.settings-theme-option .settings-theme-check { margin-left: auto; opacity: 0; color: var(--primary); }
.settings-theme-option.active { color: var(--primary); font-weight: 600; }
.settings-theme-option.active .settings-theme-check { opacity: 1; }

@media (max-width: 768px) {
    .nav-toggle { display: flex; }
    .nav-links {
        display: none;
        position: fixed;
        top: 60px;
        left: 0;
        right: 0;
        background: var(--card-bg);
        border-bottom: 1px solid var(--border-light);
        box-shadow: 0 4px 12px rgba(0,0,0,0.08);
        flex-direction: column;
        align-items: stretch;
        padding: 8px;
        gap: 2px;
        z-index: 99;
    }
    .nav-links.open { display: flex; }
    .nav-links a {
        margin-left: 0;
        padding: 12px 14px;
        width: 100%;
    }
    .nav-links .nav-logout { margin-left: 0 !important; }
}

/* CONTAINER */
.container { max-width: 1200px; margin: 0 auto; padding: 28px 20px 48px; }
.container h2 { margin-bottom: 22px; font-size: 22px; font-weight: 700; display: flex; align-items: center; gap: 10px; }
.container h2 i { color: var(--primary); font-size: 19px; }

/* PAGE HEADER (judul halaman + tombol aksi utama sejajar di satu baris, misal "+ Buat X Baru")
   — konvensi: judul dan tombol SELALU sejajar (satu baris), bukan judul di baris sendiri lalu
   tombol jauh di bawahnya. Jarak ke section berikutnya diatur lewat margin-bottom di WRAPPER
   ini (nilainya sama seperti margin-bottom h2 biasa, 22px), BUKAN dengan bikin isi baris ini
   renggang. Pakai `.container h2` di dalamnya seperti biasa, cuma margin-bottom-nya di-nolkan
   karena wrapper yang sudah pegang jaraknya. */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 22px;
}
.page-header h2 { margin-bottom: 0; }

/* CARDS */
.card {
    background: var(--card-bg);
    border-radius: var(--radius);
    padding: 22px;
    margin-bottom: 20px;
    box-shadow: var(--shadow-sm);
    border: 1px solid var(--border-light);
}
.card h3 { font-size: 15px; margin-bottom: 16px; color: var(--text); font-weight: 600; display: flex; align-items: center; gap: 8px; }
.card h3 i { color: var(--primary); font-size: 14px; }
.card h4 { font-size: 13px; margin: 18px 0 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.4px; }

/* UTILITY CLASSES (§ 10 Fase 2, 2026-08-31, docs/DESIGN_SYSTEM.md § 4 "kalau satu kombinasi
   inline style muncul ≥3x, layak jadi utility class") — diekstrak dari admin/stats.php, yang
   ternyata pegang inline style `style="color:var(--muted)"` polos 5x, `style="color:var(--muted);
   font-size:13px;margin-bottom:14px"` 2x, dan `style="font-size:22px;color:var(--primary);
   margin-right:6px"` 2x — cukup sering buat layak jadi class daripada diketik ulang tiap kali. */
.text-muted { color: var(--muted); }
/* line-height:1.5 (§ update 2026-09-02, diminta Yusuf) — sebelumnya tidak diset sama sekali
   (fallback ke default browser ~1.2), lebih rapat dibanding pola teks penjelasan lain yang
   sudah ada line-height eksplisit (.sticky-mode-option small: 1.5, .info-disclosure-body p:
   1.7) — kentara pas .section-note dipakai multi-baris (mis. penjelasan Sticky/Rotasi di
   admin/stats.php & operator/stats.php, pakai <br> antar baris). */
.section-note { color: var(--muted); font-size: 13px; margin-bottom: 14px; line-height: 1.5; }
.stat-icon { font-size: 22px; color: var(--primary); margin-right: 6px; }

/* STAT GRID */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: var(--gap-md);
    margin-bottom: 20px;
}
.stat-card {
    background: var(--card-bg);
    border-radius: var(--radius);
    padding: 22px 20px;
    text-align: center;
    border: 1px solid var(--border-light);
    box-shadow: var(--shadow-sm);
    transition: box-shadow 0.15s;
}
.stat-card:hover { box-shadow: var(--shadow-md); }
.stat-card.highlight .stat-number { color: var(--primary); }
/* min-height + flex center (bukan cuma font-size) — beberapa kartu (mis. "Operator Paling
   Aktif" di admin/stats.php) override font-size inline jadi lebih kecil karena isinya nama,
   bukan angka. Tanpa tinggi baris yang dikunci, kartu itu jadi lebih pendek dari kartu angka
   di sebelahnya, dan .stat-label di bawahnya ikut naik — label antar-kartu jadi tidak sejajar
   walau .stat-card sendiri sama tinggi (grid stretch). Dikunci ke tinggi baris 32px font
   normal, style override manapun tetap dapat baris setinggi itu. */
.stat-number { font-size: 32px; font-weight: 700; letter-spacing: -0.5px; color: var(--text); min-height: 38px; line-height: 1; display: flex; align-items: center; justify-content: center; }
.stat-label { font-size: 12px; color: var(--muted); margin-top: 6px; font-weight: 500; }

@media (max-width: 480px) {
    .stat-grid { grid-template-columns: repeat(2, 1fr); gap: var(--gap-sm); }
    .stat-card { padding: 16px 12px; }
    .stat-number { font-size: 24px; min-height: 29px; }
}

/* TWO COL */
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-lg); }
.two-col > .card { min-width: 0; }
@media (max-width: 768px) { .two-col { grid-template-columns: 1fr; } }

/* TABLE */
/* [Diralat 2026-09-02, diminta Yusuf: "ukuran space baris ke 10 yang paling bawah itu
   terlalu sempit... ketutup sama alat gesernya itu"] Scrollbar horizontal `.table-wrap`
   SEBELUMNYA scrollbar BAWAAN BROWSER (belum pernah di-styling sama sekali di app ini,
   dicek — nol match `::-webkit-scrollbar`/`scrollbar-width` sebelum ini) — tebal (~15-17px)
   dan warnanya terang, jadi kelihatan "menutupi" baris terakhir tabel yang panjang (mis.
   Daftar Operator 10 baris). `padding-bottom` kecil dikasih supaya ada jarak napas antara
   baris terakhir dan track scrollbar-nya, SEKALIAN scrollbar di-styling tipis + warna ikut
   tema (`--border`) — dua-duanya saling melengkapi, bukan salah satu doang cukup. */
.table-wrap {
    overflow-x: auto;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-light);
    padding-bottom: 6px;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}
.table-wrap::-webkit-scrollbar { height: 8px; }
.table-wrap::-webkit-scrollbar-track { background: transparent; }
.table-wrap::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
.table-wrap::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table th, .table td { padding: 12px 14px; text-align: left; border-bottom: 1px solid var(--border-light); white-space: nowrap; }
.table th {
    font-weight: 600;
    color: var(--text);
    font-size: 12px;
    background: var(--surface-subtle);
    position: sticky;
    top: 0;
}
.table tbody tr { transition: background 0.12s; }
.table tbody tr:hover { background: var(--surface-subtle); }
.table tbody tr:last-child td { border-bottom: none; }
/* Link biasa (nama campaign/operator dsb) di dalam tabel jangan pakai warna default
   :link/:visited bawaan browser — TANPA ini, link ke halaman yang browser-nya sudah pernah
   dibuka (Edit Campaign, dst) berubah warna ungu (:visited) sementara link lain yang belum
   pernah dibuka tetap biru default, jadi kelihatan tidak konsisten padahal itu link yang sama
   jenisnya (dilaporkan Yusuf: "kenapa Automation Category warna ungunya beda sendiri").
   :visited SENGAJA disamakan ke warna :link, bukan dihapus total. `:not(.badge)` WAJIB —
   badge Rotasi/Sticky (`<a class="badge badge-purple/badge-blue">` di Detail Klik) juga <a>
   di dalam .table, TANPA exclude ini warnanya ikut ketiban var(--primary) biru, menimpa warna
   semantiknya sendiri (ditemukan regresi: badge "Rotasi" yang harusnya ungu jadi biru). */
.table a:not(.icon-btn):not(.btn):not(.badge) { color: var(--primary); }
.table a:visited:not(.icon-btn):not(.btn):not(.badge) { color: var(--primary); }
.table code { background: var(--bg); padding: 3px 7px; border-radius: 4px; font-size: 12px; color: var(--text-secondary); }

/* Kolom "Nama CS"/"Nama Sales" dibekukan (freeze) di tabel lebar (Daftar Operator,
   Operator Terpasang di edit campaign) — di layar sempit/mobile kolom nama kegeser hilang
   duluan pas scroll horizontal padahal itu yang paling penting buat identifikasi baris.
   Dipakai lewat class `.freeze-col` (BUKAN `:nth-child`) — posisi kolom ini GESER tergantung
   ada-tidaknya kolom checkbox bulk proporsi di sebelah kirinya (cuma muncul kalau operator > 1),
   jadi :nth-child akan salah pilih kolom begitu checkbox-nya absen. Kolom checkbox/"#" SENGAJA
   TIDAK ikut dibekukan (bukan krusial buat tetap kelihatan) — begitu discroll, dia lewat di
   belakang kolom nama yang background-nya solid, itu behavior normal freeze-column bukan bug. */
.table .freeze-col {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--card-bg);
    box-shadow: 2px 0 4px rgba(0,0,0,0.06);
}
.table th.freeze-col { z-index: 3; background: var(--surface-subtle); }
#operatorTable tbody tr:hover td:nth-child(2) { background: var(--surface-subtle); }

/* FORMS */
.form-group { margin-bottom: 14px; flex: 1; }
.form-group label { display: block; font-weight: 500; margin-bottom: 6px; font-size: 13px; color: var(--text); }
/* Tanda bintang wajib-isi — WAJIB pakai ini, bukan "*" polos di teks label, biar konsisten
   beda dari teks biasa (lihat docs/DESIGN_SYSTEM.md § 4). */
.required-mark { color: var(--danger); margin-left: 2px; }
.form-group input[type=text],
.form-group input[type=email],
.form-group input[type=number],
.form-group input[type=time],
.form-group input[type=date],
.form-group input[type=password],
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 14px;
    outline: none;
    transition: border 0.2s, box-shadow 0.2s;
    background: var(--card-bg);
    font-family: inherit;
    color: var(--text);
}
/* Arrow bawaan browser nempel mepet ke border tanpa jarak napas (paling kentara di
   Chrome) — matikan arrow native, ganti chevron custom dengan padding kanan yang cukup.
   Warna chevron pakai --muted biar konsisten sama ikon sekunder lain di halaman ini. */
.form-group select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 34px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%238c8c8c' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(22,119,255,0.12); }
/* line-height:1.5 (§ update 2026-09-02) — sama masalah yang ditemukan di .section-note:
   dipakai 2-baris dengan <br> di admin/_views/campaigns.php (hint "[oname]"), sebelumnya
   tanpa line-height sama sekali (fallback default ~1.2), lebih rapat dari standar 1.5. */
.form-group small { color: var(--muted); font-size: 12px; margin-top: 4px; display: block; line-height: 1.5; }
.form-row { display: flex; gap: 16px; flex-wrap: wrap; align-items: flex-start; }
.form-row .form-group { min-width: 160px; }

/* FORM ACTIONS (tombol simpan/ubah yang berdiri sendiri di baris terakhir sebuah form) —
   konvensi: tombol aksi UPDATE/EDIT ("Simpan Perubahan", "Ganti Password", "Ajukan
   Perubahan", dst) ditaruh RATA KANAN, bukan rata kiri (default block element). Kalau ada
   tombol sekunder (misal "Kembali"/"Batal"), keduanya masuk wrapper yang sama, urutan
   sekunder-lalu-primer, tetap nempel kanan sebagai satu grup. TIDAK berlaku untuk tombol
   "Tambah/Buat X Baru" (form CREATE) — itu tetap rata kiri seperti sebelumnya, beda konvensi. */
.form-actions { display: flex; justify-content: flex-end; gap: 12px; flex-wrap: wrap; }

.checkbox-group { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.checkbox-group label { font-weight: normal; display: flex; align-items: center; gap: 4px; cursor: pointer; }
.checkbox-inline { font-weight: 400 !important; font-size: 13px; display: flex; align-items: center; gap: 6px; cursor: pointer; }

/* .utm-col-* (§ update 2026-09-02, diminta Yusuf: "aku kurang suka karena checkbox content
   misah sendiri") — form pilih kolom UTM Breakdown (admin/stats.php & operator/stats.php)
   SEBELUMNYA menaruh tiap `.checkbox-inline` sebagai flex item LANGSUNG di `.form-row`
   bersebelahan dengan tombol Terapkan, jadi kalau lebar layar sempit item TERAKHIR (biasanya
   "Content") wrap sendirian ke baris baru, kelihatan terpisah/nyasar dari grup checkbox
   lainnya. Checkbox dibungkus SATU kotak `.utm-col-fieldset` (border + surface-subtle, pola
   sama seperti kotak filter lain di app) supaya walau isinya wrap ke beberapa baris di layar
   sempit, tetap kebaca sebagai SATU grup yang menyatu, bukan item lepas.
   [Diralat 2026-09-02, ronde 2, diminta Yusuf: "tombol terapkan itu masuk dalam box ini aja
   biar makin simpel, diganti icon aja"] Tombol Terapkan awalnya DI LUAR kotak — ternyata
   dianggap kurang ringkas, dipindah MASUK `.utm-col-fieldset` (elemen terakhir, `margin-left:
   auto` biar nempel ke kanan) sebagai `.icon-btn` (cuma ikon `fa-check` + tooltip "Terapkan",
   BUKAN lagi tombol berlabel teks) — hemat ruang, konsisten dengan pola icon-btn submit yang
   sudah ada di tempat lain (mis. `admin/_views/admin_accounts.php`). `.utm-col-form` disederhanakan
   jadi block biasa (dulu perlu flex buat sejajarkan kotak+tombol sebagai 2 elemen terpisah,
   sekarang cuma satu kotak). */
.utm-col-form { margin-bottom: 14px; }
.utm-col-fieldset { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; padding: 10px 14px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-subtle); }
.utm-col-fieldset-label { font-size: 13px; color: var(--muted); white-space: nowrap; }
.utm-col-options { display: flex; flex-wrap: wrap; gap: 8px 16px; }
.utm-col-apply-btn { margin-left: auto; }
/* [Diralat 2026-09-02, ronde 3, diminta Yusuf: "jangan warna hitam lah buttonnya, ga kelihatan,
   warna biru aja"] `.icon-btn` polos (border abu + ikon `--text-secondary`) kontrasnya terlalu
   rendah di atas `.utm-col-fieldset` yang sudah `--surface-subtle` (dua permukaan gelap mirip
   bertumpuk) — checkmark nyaris tidak kebaca. Diisi solid `--primary-fill` (biru terang, token
   KHUSUS elemen solid+putih, lihat definisi di `:root` § warna) + ikon putih, SAMA logika
   dengan `.btn-primary`. Disabled TETAP kelihatan nonaktif via `opacity` (pola sama
   `.btn-primary:disabled`), BUKAN balik ke abu polos — supaya biru-nya konsisten tampil,
   cuma diredupkan. Selector didahului `.utm-col-fieldset` (bukan cuma `.utm-col-apply-btn`
   sendiri) semata-mata buat menang spesifisitas CSS atas `.icon-btn`/`.icon-btn:disabled` yang
   generik (didefinisikan lebih jauh di bawah file ini). */
.utm-col-fieldset .utm-col-apply-btn { background: var(--primary-fill); border-color: var(--primary-fill); color: #fff; }
.utm-col-fieldset .utm-col-apply-btn:hover:not(:disabled) { background: var(--primary-fill-hover); border-color: var(--primary-fill-hover); color: #fff; }
.utm-col-fieldset .utm-col-apply-btn:disabled { opacity: 0.5; cursor: not-allowed; background: var(--primary-fill); border-color: var(--primary-fill); color: #fff; }

/* BUTTONS */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--card-bg);
    color: var(--text);
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
    font-family: inherit;
}
.btn:hover { color: var(--primary); border-color: var(--primary); }
.btn:active { opacity: 0.85; }
.btn:disabled,
.btn:disabled:hover { opacity: 0.5; cursor: not-allowed; color: var(--text); border-color: var(--border); background: var(--card-bg); }
.btn-primary:disabled,
.btn-primary:disabled:hover { background: var(--primary-fill); color: #fff; border-color: var(--primary-fill); opacity: 0.5; }
/* Tampak nonaktif TAPI tombolnya beneran tetap bisa diklik (bukan atribut `disabled`) — dipakai
   di form Buat Baru yang field wajibnya masih kosong, supaya validasi native browser (atribut
   `required`) tetap tampil ("Harap isi bidang ini") pas diklik. Beda dari `:disabled` di atas
   (buat form Edit yang belum ada perubahan) yang klik-nya beneran tidak berefek apa pun. */
.btn.looks-disabled,
.btn.looks-disabled:hover { opacity: 0.5; cursor: not-allowed; color: var(--text); border-color: var(--border); background: var(--card-bg); }
.btn-primary.looks-disabled,
.btn-primary.looks-disabled:hover { background: var(--primary-fill); color: #fff; border-color: var(--primary-fill); opacity: 0.5; }
.btn-primary { background: var(--primary-fill); color: #fff; border-color: var(--primary-fill); }
.btn-primary:hover { background: var(--primary-fill-hover); border-color: var(--primary-fill-hover); color: #fff; }
.btn-danger  { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn-danger:hover { background: #ff7875; border-color: #ff7875; color: #fff; }
.btn-sm { padding: 4px 11px; font-size: 12px; }
.btn-block { width: 100%; justify-content: center; padding: 10px; }

/* Nudge Ketimpangan Klik (§ 16, admin/stats.php "Ketimpangan Klik Terdeteksi" +
   admin/_views/operators.php detail per-baris) — warna & badge arah (§ update 2026-09-02,
   diminta Yusuf: "mainkan warna... kasih keterangan lebih/kurang juga... icon juga bisa
   seperti di dashboard operator itu"). Nama operator dikasih warna primary biar gampang
   dipindai per baris; angka aktual/ekspektasi dikasih warna arah (pakai token chart yang SUDAH
   ADA, bukan warna baru — --chart-1 biru buat "kurang", --chart-3 oranye buat "lebih", warna
   yang sama secara VISUAL dengan tren garis/bar chart lain di app, cuma dipakai ulang di teks).
   Badge arah pakai ikon SAMA PERSIS dengan Daftar Operator (§ 16.6, fa-arrow-trend-down/up) —
   biar 2 tempat yang nampilin ketimpangan yang sama konsisten. Tombol "Copy pesan WA" dikasih
   aksen hijau WhatsApp (--green, brand color yang sama dengan .brand-icon), tombol konfirmasi
   sales [Diralat 2026-09-02, diminta Yusuf: "ganti dari ornamen warna hijau jadi warna biru"]
   dikasih aksen --primary (biru, BUKAN --success hijau lagi) — dua tombol tetap beda warna
   sendiri-sendiri, bukan dua-duanya abu-abu polos identik, cuma pasangannya diganti. */
.nudge-operator-name { color: var(--primary); }
.nudge-direction-badge { margin-left: 6px; }
.nudge-value-lebih { color: var(--chart-3); }
.nudge-value-kurang { color: var(--chart-1); }
.nudge-copy-wa-btn { border-color: var(--green); color: var(--green); }
.nudge-copy-wa-btn:hover { background: var(--green); color: #fff; border-color: var(--green); }
.nudge-asked-btn { border-color: var(--primary); color: var(--primary); }
.nudge-asked-btn:hover { background: var(--primary-fill); color: #fff; border-color: var(--primary-fill); }

/* BADGES */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
}
.badge-green  { background: var(--primary-bg); color: var(--primary); }
.badge-gray   { background: var(--bg); color: var(--muted); }
.badge-amber  { background: var(--warning-bg); color: var(--warning); }
.badge-red    { background: var(--danger-bg); color: var(--danger); }
/* Badge notifikasi angka (§ nav "Pengajuan") — beda dari badge status (.badge-red/.badge-amber/
   dst) yang sengaja pill lembut buat label; ini butuh fill merah SOLID + teks putih biar
   kelihatan tegas dari jauh di navbar, sama tone-nya kayak .btn-primary yang fill biru + teks
   putih. Jangan ubah .badge-red buat ini — .badge-red masih dipakai/didokumentasikan sebagai
   pola badge status lembut di tempat lain. */
.badge-notify { background: var(--danger); color: #fff; font-weight: 700; }
/* Status Global "Izin" (Daftar Operator, § redesign 2026-08-30 form Cuti/Izin) — SENGAJA
   bukan .badge-amber biasa (pill pucat var(--warning-bg) #fff7e6, nyaris nyatu sama background
   putih tabel, sulit dibedakan sekilas). Pakai fill oranye tua solid + teks putih supaya
   tegas kelihatan sebagai status ketiga yang beda dari Aktif (hijau)/Nonaktif (abu-abu),
   sama alasannya kayak .badge-notify di atas. Jangan ubah .badge-amber/--warning-bg buat ini
   — keduanya masih dipakai di tempat lain (misal "Menunggu ACC") yang memang cocok pucat. */
.badge-leave { background: var(--leave); color: #fff; font-weight: 700; }
/* Badge status "Izin" juga dipakai sebagai trigger tooltip (Daftar Operator, detail "Cuti
   s/d ..." — sebelumnya baris kedua terpisah di bawah badge, sekarang cukup hover/focus badge-
   nya sendiri, reuse .tooltip-icon buat nempel ke event delegation .global-tooltip-bubble yang
   sudah ada, § fix 2026-08-31). `.tooltip-icon` sendiri override ukuran font/cursor/WARNA buat
   ikon info kecil — kalau ditempel apa adanya ke `.badge`, cursor berubah jadi "help" (padahal
   masih link toggle aktif/nonaktif) DAN teksnya ikut abu-abu `--muted` (bukan putih lagi).
   Selector gabungan `.badge.tooltip-icon` (spesifisitas 0,2,0) VS `.tooltip-icon` sendirian
   (0,1,0) menang buat font-size/cursor, TAPI utuk `color` speisifitasnya SAMA dengan
   `.badge-leave` (0,1,0 masing-masing) — dua percobaan fix sebelumnya SALAH DUA-DUANYA gara-gara
   ini: `color: inherit` (ambil warna ambient parent, bukan "batalkan override") lalu TIDAK
   nulis `color` sama sekali (fallback ke `.tooltip-icon` yang menang cuma karena definisinya
   lebih belakangan di file — abu-abu, bukan putih). Solusi yang BENAR: `color: #fff` ditulis
   eksplisit di selector 3-kelas `.badge.tooltip-icon.badge-leave` (0,3,0) — menang TELAK atas
   `.tooltip-icon`/`.badge-leave` yang mana pun, TANPA bergantung urutan definisi di file sama
   sekali (beda dari 2 percobaan sebelumnya yang diam-diam mengandalkan itu). */
.badge.tooltip-icon { font-size: inherit; cursor: pointer; margin-left: 0; }
.badge.tooltip-icon.badge-leave { color: #fff; }
/* Hover/focus badge-leave TETAP putih (bukan ikut biru dari `.tooltip-icon:hover` global) —
   cuma background yang gelap sedikit, biar tetap terasa "hover" tanpa keluar dari palet
   oranye/coklat badge ini. */
.badge.tooltip-icon.badge-leave:hover,
.badge.tooltip-icon.badge-leave:focus {
    color: #fff;
    background: var(--leave-hover);
    outline: none;
}
/* Textarea "Alasan (opsional)" di form Tolak Pengajuan — bare <textarea> TIDAK otomatis dapat
   padding/border kayak input di .form-group (itu butuh wrapper .form-group, textarea ini
   berdiri sendiri di dalam <details> kolom Aksi yang sempit). Tombolnya WAJIB display:block di
   HTML (bukan cuma diandalkan dari sini) — dua elemen inline-block bersebelahan di kolom
   sesempit ini gampang overflow ke luar .table-wrap alih-alih wrap ke baris baru. */
.pengajuan-reject-note {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 8px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 13px;
    font-family: inherit;
    margin-bottom: 8px;
    resize: vertical;
}
.badge-blue   { background: var(--primary-bg); color: var(--primary); }
.badge-purple { background: var(--purple-bg); color: var(--purple); }

/* ALERTS */
.alert { padding: 12px 16px; border-radius: var(--radius-sm); margin-bottom: 16px; font-weight: 500; display: flex; align-items: center; gap: 8px; }
.alert-success { background: var(--success-bg); color: var(--success); border: 1px solid var(--success-border); }
.alert-error   { background: var(--danger-bg); color: var(--danger); border: 1px solid var(--danger-border); }
/* § 32 ARCHITECTURE.md — tombol close (X) universal buat semua alert, diminta Yusuf supaya
   alert bisa di-dismiss manual (sebelumnya cuma hilang kalau reload tanpa parameter msg lagi).
   Ditaruh sebagai flex item TERAKHIR (.alert sudah display:flex dari atas), margin-left:auto
   dorong ke ujung kanan tanpa perlu positioning absolute. */
.alert-close { margin-left: auto; background: none; border: none; color: inherit; cursor: pointer; padding: 2px 6px; line-height: 1; opacity: 0.65; font-size: 14px; flex-shrink: 0; }
.alert-close:hover { opacity: 1; }

/* TABLE TOOLBAR (show entries + search) */
.table-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 14px;
}
.entries-control {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-secondary);
}
.entries-control select {
    padding: 6px 10px;
    padding-right: 28px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 13px;
    background: var(--card-bg);
    color: var(--text);
    /* Sama alasannya kayak .form-group select di atas — arrow native mepet ke border */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%238c8c8c' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
}
/* Input "Menuju [halaman]" (pagination, § 19.8/20) — sebelumnya TANPA styling sama sekali
   (fallback ke default browser polos, kontras banget sama select/tombol di sebelahnya yang
   semua sudah ditokenisasi) — ditemukan lewat laporan visual Yusuf, bukan dari baca kode.
   Selector ini HARUS diupdate tiap kali markup "Menuju" pindah wadah (sudah kejadian 2x:
   .entries-control -> .pagination § 2026-09-01, lalu .pagination -> .jump-to-page § 2026-09-02
   — ronde kedua ini SEMPAT KELUPAAN, box "hal." balik polos tanpa styling di produksi sampai
   ditemukan lewat laporan visual lagi. Kalau markup pindah wadah lagi, selector di 3 rule
   blok ini WAJIB ikut diperbarui bareng, jangan cuma pindah HTML-nya doang). */
.entries-control input[type=number],
.jump-to-page input[type=number] {
    padding: 5px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 13px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
}
/* Sembunyikan tombol spinner naik/turun native browser (§ update 2026-09-01, ditemukan lewat
   laporan visual Yusuf — box "Menuju" kelihatan beda warna dari kontrol lain) — spinner bawaan
   `<input type=number>` di Chrome/Safari punya kotak sendiri yang TIDAK ikut warna custom
   `background` di atas, bikin box ini kelihatan "belang" dibanding tombol pagination di
   sebelahnya. Kotak ini murni buat ketik nomor halaman, tidak butuh tombol naik/turun 1-per-1. */
.entries-control input[type=number]::-webkit-outer-spin-button,
.entries-control input[type=number]::-webkit-inner-spin-button,
.jump-to-page input[type=number]::-webkit-outer-spin-button,
.jump-to-page input[type=number]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.entries-control input[type=number],
.jump-to-page input[type=number] {
    -moz-appearance: textfield;
}
/* Dark Mode Tahap 3 audit (§ 11 ARCHITECTURE.md, 2026-08-31) — sama kasus seperti
   .grade-inline/.operator-login-inline di atas: `background`/`color` tidak pernah di-set,
   ketemu lewat sensus terprogram bukan baca kode. Dipakai di halaman Operator DAN Campaign
   (search box "Cari operator.../Cari campaign..."), jadi 1 fix ini membereskan 2 halaman
   sekaligus. */
.search-control input {
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 13px;
    width: 220px;
    max-width: 100%;
    outline: none;
    transition: border 0.2s, box-shadow 0.2s;
    font-family: inherit;
    background: var(--card-bg);
    color: var(--text);
}
.search-control input:focus { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(22,119,255,0.12); }

/* TABLE FOOTER (pagination) — grid 3 kolom (§ update 2026-09-01 & 2026-09-02, diminta Yusuf 3x).
   Ronde 1: flex justify-content:space-between + .pagination flex-grow:1 — cuma center di SISA
   ruang setelah entries-control ambil lebar alaminya, bukan center beneran.
   Ronde 2: grid 3 kolom (1fr/auto/1fr) dengan "Menuju" DIGABUNG ke .pagination (kolom tengah) —
   ini bikin BLOK GABUNGAN (nomor+Menuju) center matematis, TAPI Yusuf sadar nomor halamannya
   sendiri kelihatan condong kiri karena "Menuju" di ujung kanan geser titik tengah blok gabungan
   itu ke kanan. Mata fokus ke deretan angka, bukan ke blok gabungannya.
   Ronde 3 (final): "Menuju" DIPISAH lagi jadi kolom ke-3 sendiri (`.jump-to-page`, justify-self
   end) — supaya kolom tengah (`.pagination`, isinya CUMA panah+nomor halaman) yang jadi
   patokan center, bukan blok gabungan. Grid `1fr auto 1fr` tetap dipakai karena sifatnya (dua
   kolom tepi OTOMATIS SAMA lebar) menjamin kolom tengah center matematis terhadap lebar total
   baris — TERLEPAS dari lebar konten kolom kiri MAUPUN kanan, bahkan kalau keduanya beda lebar
   (entries-control vs jump-to-page tidak perlu simetris). SEMUA halaman berpagination (Detail
   Klik x2, Riwayat Aktivitas, Daftar Campaign) WAJIB struktur 3 kolom yang sama:
   `.entries-control` (kolom 1) + `.pagination` panah/nomor SAJA (kolom 2, center) +
   `.jump-to-page` (kolom 3, end) — jangan gabung "Menuju" balik ke .pagination lagi. */
.table-footer {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 12px;
    margin-top: 14px;
}
/* justify-self:center (BUKAN start/end) di kedua kolom tepi (§ update 2026-09-02, diminta
   Yusuf — start/end bikin "[X]/halaman" & "Menuju" nempel mojok ke tepi luar box, dekat sekali
   ke tepi kartu tapi jauh dari nomor halaman). Center di dalam kolom masing-masing otomatis
   simetris kiri-kanan (dua kolom 1fr sama lebar) DAN dapat jarak seimbang dari tepi kartu
   maupun dari klaster nomor halaman di tengah — tanpa perlu margin manual yang gampang beda
   nilai kiri vs kanan. */
.table-footer > .entries-control { grid-column: 1; justify-self: center; }
.table-footer > .pagination { grid-column: 2; justify-self: center; }
.table-footer > .jump-to-page { grid-column: 3; justify-self: center; }
@media (max-width: 700px) {
    /* Grid 3 kolom kaku kurang cocok buat layar sempit (kolom kiri/kanan bisa kepepet) — balik
       ke flex biasa yang boleh wrap ke banyak baris, tetap center sebisanya. */
    .table-footer { display: flex; flex-wrap: wrap; justify-content: center; }
    .table-footer > .entries-control,
    .table-footer > .pagination,
    .table-footer > .jump-to-page { justify-self: unset; }
}
.pagination { display: flex; gap: 4px; flex-wrap: wrap; justify-content: center; }
.jump-to-page { display: flex; align-items: center; gap: 8px; }
.jump-to-page form { display: flex; align-items: center; gap: 8px; }
.pagination button, .pagination a {
    padding: 5px 12px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    color: var(--text);
    font-size: 13px;
    cursor: pointer;
    font-family: inherit;
    text-decoration: none;
    display: inline-block;
}
.pagination button:hover:not(:disabled), .pagination a:hover:not(.disabled) { color: var(--primary); border-color: var(--primary); }
.pagination button.active, .pagination a.active { background: var(--primary-fill); border-color: var(--primary-fill); color: #fff; }
.pagination button:disabled, .pagination a.disabled { opacity: 0.4; cursor: not-allowed; }

/* RIWAYAT AKTIVITAS — diff before/after (§ 13 Tahap 3) */
.audit-diff-list { margin: 0; padding-left: 18px; font-size: 13px; }
.audit-diff-list li { margin-bottom: 4px; }
.audit-row:hover { background: var(--surface-subtle); }

/* CAMPAIGN NAME CELL */
.camp-name { font-weight: 600; margin-bottom: 3px; }
.camp-url { display: flex; align-items: center; gap: 6px; }
.camp-url code { font-size: 12px; }

/* TRAFFIC */
.traffic-count { color: var(--text); font-weight: 500; }
.traffic-count.has-traffic { color: var(--warning); font-weight: 700; }

/* UTM LINK CARDS (halaman edit campaign) */
.utm-link-list { display: flex; flex-direction: column; gap: 10px; }
.utm-link-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 16px;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-sm);
    background: var(--surface-subtle);
}
.utm-link-info { min-width: 0; flex: 1; }
.utm-link-label { font-weight: 600; font-size: 14px; margin-bottom: 5px; }
.utm-link-url {
    display: block;
    font-size: 12px;
    color: var(--text-secondary);
    background: var(--card-bg);
    border: 1px solid var(--border-light);
    border-radius: 4px;
    padding: 4px 8px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}
@media (max-width: 600px) {
    .utm-link-card { flex-direction: column; align-items: stretch; }
}

/* ICON BUTTONS (actions column) */
.actions-cell { display: flex; gap: 6px; }
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--card-bg);
    color: var(--text-secondary);
    cursor: pointer;
    text-decoration: none;
    font-size: 13px;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.icon-btn:hover { color: var(--primary); border-color: var(--primary); background: var(--primary-bg); }
.icon-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.icon-btn:disabled:hover { color: var(--text-secondary); border-color: var(--border); background: var(--card-bg); }
.icon-btn-danger:hover { color: var(--danger); border-color: var(--danger); background: var(--danger-bg); }

/* ICON BUTTON TOOLTIP — tombol icon-only WAJIB punya penjelasan singkat saat di-hover,
   supaya nggak perlu klik dulu buat tau fungsinya. Pakai atribut `data-tooltip` (bukan
   cuma andalkan `title=` bawaan browser) supaya tampilannya konsisten & sesuai brand di
   semua browser — `title=` tetap dipasang bareng buat aksesibilitas (screen reader). */
.icon-btn[data-tooltip] { position: relative; }
.icon-btn[data-tooltip]::after {
    content: attr(data-tooltip);
    position: absolute;
    bottom: 130%;
    left: 50%;
    transform: translateX(-50%);
    background: var(--tooltip-bg);
    color: #fff;
    padding: 5px 9px;
    border-radius: var(--radius-sm);
    font-size: 11px;
    font-weight: 400;
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.12s;
    transition-delay: 0.15s;
    z-index: 200;
    pointer-events: none;
    box-shadow: var(--shadow-md);
}
.icon-btn[data-tooltip]:hover::after,
.icon-btn[data-tooltip]:focus-visible::after { opacity: 1; visibility: visible; }

/* EDIT PANEL */
.edit-panel { border: 1px solid var(--primary); }
.edit-panel .card-close { float: right; color: var(--muted); text-decoration: none; font-size: 13px; }
.edit-panel .card-close:hover { color: var(--danger); }
/* Dark Mode Tahap 3 audit (§ 11 ARCHITECTURE.md, 2026-08-31) — `.proporsi-inline input`
   (nama sebelumnya `.grade-inline`, § update 2026-09-02) dan `.operator-login-inline input`
   (dua form compact di dalam baris tabel) sama-sama TIDAK PERNAH punya
   `background`/`color`/`border` sendiri, cuma padding — sama root cause seperti
   `.assign-campaign-select` di atas (bare input di luar `.form-group`, jatuh ke default putih
   browser, kebetulan tidak kentara di light mode). Ditemukan lewat sensus terprogram
   (`getComputedStyle` semua elemen, cari `background-color: rgb(255,255,255)` yang tersisa
   saat dark mode aktif), bukan cuma baca kode. */
.proporsi-inline { display: flex; align-items: center; gap: 6px; }
.proporsi-inline input[type=number] {
    width: 60px;
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    color: var(--text);
}

.operator-login-inline { display: flex; align-items: center; gap: 6px; }
.operator-login-inline input[type=text] {
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
    color: var(--text);
}

/* SECTION DIVIDER (pemisah visual antar bagian halaman, mis. section tren yang independen dari filter) */
.section-divider {
    display: flex;
    align-items: center;
    gap: 16px;
    margin: 8px 0 20px;
}
.section-divider::before,
.section-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
}
.section-divider span {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-secondary);
    white-space: nowrap;
}

/* TREND (statistik per sales) */
.trend-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--gap-sm);
    margin-bottom: 20px;
}
.trend-card {
    border: 1px solid var(--border-light);
    border-radius: var(--radius-sm);
    padding: 14px 16px;
    background: var(--surface-subtle);
}
.trend-name { font-weight: 600; font-size: 13px; margin-bottom: 4px; }
.trend-total { font-size: 20px; font-weight: 700; margin-bottom: 8px; }
.trend-total span { font-size: 12px; font-weight: 500; color: var(--muted); }
.trend-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 600;
}
.trend-up   { background: var(--success-bg); color: var(--success); }
.trend-down { background: var(--danger-bg); color: var(--danger); }
.trend-flat { background: var(--bg); color: var(--muted); }
/* § update 2026-09-03, diminta Yusuf: "Baru mulai dapat klik" BUKAN tren naik beneran (tidak
   ada baseline buat dibandingkan), jadi warnanya jangan disamakan dengan .trend-up (hijau,
   khusus buat kenaikan % yang benar-benar terhitung) — dibedakan jadi kuning/amber. */
.trend-new  { background: var(--warning-bg); color: var(--warning); }

/* Tabel panjang (banyak baris tanggal) — scroll vertikal, header tetap terlihat */
.table-wrap-tall { max-height: 420px; overflow-y: auto; }
.table-wrap-tall thead th { position: sticky; top: 0; z-index: 1; }
.table-wrap-tall tfoot td { position: sticky; bottom: 0; background: var(--surface-subtle); }

/* Daftar tag campaign (kolom "Campaign yang Ikut", Grup Sticky & Rotasi — § update 2026-09-02,
   diminta Yusuf: "batesin, jangan ke samping terus... maksimal jangan sampai 10") — SEBELUMNYA
   badge cuma `<span>` polos berderet, mewarisi `white-space:nowrap` dari `.table td` (default
   semua tabel di app ini) jadi tidak pernah wrap ke baris baru, numpuk ke SAMPING TANPA BATAS
   kalau campaign-nya banyak (bisa kepotong keluar kolom kalau ada 15+). `white-space:normal`
   eksplisit di sini nge-override warisan itu SPESIFIK buat wadah tag ini saja, `flex-wrap:wrap`
   yang beneran bikin badge pindah ke baris baru begitu penuh (bukan cuma "boleh secara CSS
   text-wrap tapi badge-nya sendiri tetap inline sejajar"). */
.campaign-tag-list { display: flex; flex-wrap: wrap; gap: 4px; white-space: normal; }
/* CAMPAIGN DETAIL (panel expand di kolom Campaign, tabel Operator) */
.campaign-detail summary {
    cursor: pointer;
    color: var(--primary);
    font-weight: 500;
    list-style: none;
}
.campaign-detail summary::-webkit-details-marker { display: none; }
.campaign-detail summary::before {
    content: '▸';
    display: inline-block;
    margin-right: 4px;
    transition: transform 0.15s;
}
.campaign-detail[open] summary::before { transform: rotate(90deg); }
.campaign-detail-list {
    margin-top: 8px;
    padding: 8px 10px;
    background: var(--bg);
    border-radius: var(--radius-sm);
    min-width: 200px;
}
.campaign-detail-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 4px 0;
    white-space: nowrap;
}
.campaign-detail-row + .campaign-detail-row { border-top: 1px solid var(--border-light); }

/* Varian ringkas trigger "Assign ke Campaign Lain" (Kelola Operator) — icon-only button
   (bukan link teks "+ Assign ke Campaign Lain" yang makan baris sendiri), ditaruh sejajar
   di sebelah kanan "N campaign" biar tetap 1 baris. Beda class dari .campaign-detail supaya
   TIDAK ikut dapat prefix panah "▸" yang cuma cocok buat trigger teks. */
.campaign-assign-toggle summary { list-style: none; }
.campaign-assign-toggle summary::-webkit-details-marker { display: none; }
/* Ukuran & warna DIKECILKAN dari .icon-btn dasar (30x30, netral) — di sini disandingkan
   langsung dengan teks "N campaign" (13px, warna --primary), jadi ukurannya disamakan
   sedekat mungkin ke tinggi baris teks itu, TANPA fill (border tipis + transparan) biar
   tidak terlihat seberat tombol Aksi biasa, dan warnanya ikut biru sama seperti teksnya
   (bukan abu-abu netral seperti .icon-btn lain). Tetap pakai class .icon-btn dasar (bukan
   diganti total) supaya tooltip `[data-tooltip]` bawaannya tetap jalan tanpa duplikasi CSS. */
.campaign-assign-toggle .icon-btn {
    width: 18px;
    height: 18px;
    font-size: 10px;
    color: var(--primary);
    border-color: var(--primary);
    background: transparent;
}
.campaign-assign-toggle .icon-btn:hover { background: var(--primary-bg); }

/* TOOLTIP (ikon info kecil, hover/focus untuk lihat penjelasan) */
.tooltip-icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    color: var(--muted);
    cursor: help;
    font-size: 11px;
    margin-left: 3px;
    vertical-align: middle;
}
.tooltip-icon:hover,
.tooltip-icon:focus { color: var(--primary); outline: none; }
/* Konten sumber tooltip — TIDAK PERNAH ditampilkan langsung lagi (§ fix 2026-08-30, lihat
   .global-tooltip-bubble di bawah). Dulu ini elemen yang benar-benar muncul (absolute,
   relatif ke .tooltip-icon) — tapi kalau ikonnya ada di dalam tabel lebar (`.table-wrap`
   punya overflow-x:auto, yang bikin overflow-y ikut auto juga per spek CSS), bubble-nya
   kepotong/terpotong teksnya begitu ikonnya dekat tepi kiri/kanan area scroll — z-index
   tinggi TIDAK menyelamatkan dari overflow clipping ancestor, dua urusan CSS yang beda.
   Sekarang cuma jadi "data source": textContent-nya dibaca JS (lihat admin/navbar.php &
   operator/navbar.php) buat dirender ke .global-tooltip-bubble yang position:fixed relatif
   viewport, bukan relatif ancestor manapun, jadi kebal dari overflow clipping apa pun. */
.tooltip-icon .tooltip-bubble { display: none; }

/* Bubble tooltip SEBENARNYA (§ fix 2026-08-30) — satu elemen dipakai ULANG buat SEMUA
   `.tooltip-icon` di halaman (event delegation, lihat navbar.php), posisinya dihitung JS dari
   getBoundingClientRect() ikon yang di-hover/focus lalu di-clamp ke batas viewport kiri/kanan/
   bawah — makanya position:fixed (relatif viewport), BUKAN absolute (yang balik kena masalah
   overflow clipping yang sama). */
.global-tooltip-bubble {
    position: fixed;
    background: var(--tooltip-bg);
    color: #fff;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 400;
    white-space: normal;
    width: max-content;
    max-width: 220px;
    text-align: left;
    line-height: 1.4;
    z-index: 9999;
    pointer-events: none;
    box-shadow: var(--shadow-md);
}
.global-tooltip-bubble[hidden] { display: none; }

/* Kotak info "terkunci ke Grup Sticky" (Edit Campaign, § redesign 2026-08-30) — murni
   informasi, BUKAN form-group, jadi dipisah dari .form-row biar tidak ikut lebar-sempit
   nyesuaiin field lain di sampingnya (itu penyebab layout lama kelihatan berantakan — tombol
   Simpan mengambang jauh di kanan dengan celah kosong besar). */
.sticky-locked-note {
    background: var(--primary-bg);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    margin-bottom: 16px;
    font-size: 14px;
}

/* STICKY MODE OPTIONS (halaman Sticky Set — pilihan radio + deskripsi). Selector DISENGAJA
   diberi awalan `.sticky-mode-options` (bukan cuma `.sticky-mode-option` sendirian) — labelnya
   ada di dalam `.form-group`, dan `.form-group label { display:block }` (spesifisitas 0,1,1)
   lebih tinggi dari `.sticky-mode-option { display:flex }` sendirian (0,1,0), jadi kalau cuma
   pakai class tunggal, block itu MENANG dan bikin radio+teks kepisah jadi 2 baris (radio di
   atas, judul+deskripsi di bawahnya) alih-alih sejajar satu baris. Ditemukan & diperbaiki
   2026-08-30 — kalau nambah komponen sejenis (radio-card di dalam .form-group), selalu scope
   ke parent wrapper-nya juga, jangan andalkan satu class saja. */
.sticky-mode-options { display: flex; flex-direction: column; gap: 10px; }
.sticky-mode-options .sticky-mode-option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}
.sticky-mode-option:hover { border-color: var(--primary); }
.sticky-mode-option:has(input:checked) { border-color: var(--primary); background: var(--primary-bg); }
.sticky-mode-option input[type=radio] { margin-top: 3px; flex-shrink: 0; }
.sticky-mode-option strong { display: block; margin-bottom: 2px; }
.sticky-mode-option small { color: var(--muted); font-size: 12px; line-height: 1.5; }

/* Dark Mode Tahap 3 audit (§ 11 ARCHITECTURE.md, 2026-08-31) — `.assign-campaign-select`
   (dropdown "+ Tambah Campaign" per baris grup, admin/_views/sticky_set.php) tidak dibungkus
   `.form-group`, jadi tidak dapat styling `.form-group select` sama sekali dan jatuh ke
   default browser (putih polos). Di light mode ini kebetulan tidak kentara (default putih ≈
   background app yang juga putih) — baru terlihat rusak begitu dark mode aktif (kotak putih
   nyala sendiri di tengah halaman gelap). Ketemu lewat laporan visual langsung, bukan dari
   baca kode — bukti kenapa Tahap 3 (audit visual di browser beneran) penting, bukan cuma
   hitungan kontras token. Styling-nya disamakan persis pola `.entries-control select` di atas. */
.assign-campaign-select {
    padding: 6px 10px;
    padding-right: 28px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 13px;
    background: var(--card-bg);
    color: var(--text);
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%238c8c8c' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
}

/* INFO DISCLOSURE (penjelasan panjang, collapsed by default — dipakai admin/sticky_settings.php)
   — konvensi: bacaan penjelasan yang panjang tapi bukan hal pertama yang perlu dibaca tiap buka
   halaman ditaruh di sini (bawah, collapsed), bukan di atas/selalu terbuka. Lihat DESIGN_SYSTEM.md § 4. */
.info-disclosure summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.info-disclosure summary::-webkit-details-marker { display: none; }
.info-disclosure summary i { color: var(--primary); margin-right: 8px; }
.info-disclosure .disclosure-arrow { color: var(--muted); transition: transform 0.15s; }
.info-disclosure[open] .disclosure-arrow { transform: rotate(90deg); }
.info-disclosure[open] summary { margin-bottom: 16px; }
.info-disclosure-body p { margin: 0 0 14px; line-height: 1.7; }
.info-disclosure-body p:last-child { margin-bottom: 0; }

/* PROGRESS BAR (kolom Total Klik di tabel Per Operator) */
.progress-bar-wrap { display: flex; align-items: center; gap: 8px; min-width: 140px; }
.progress-bar-track { flex: 1; height: 8px; background: var(--border-light); border-radius: 4px; overflow: hidden; }
.progress-bar-fill { height: 100%; background: var(--primary); border-radius: 4px; }
.progress-bar-label { font-size: 12px; font-weight: 600; min-width: 34px; text-align: right; flex-shrink: 0; }

/* DATE RANGE PICKER */
.daterange-group { position: relative; }
.daterange-btn { width: 100%; min-width: 240px; justify-content: flex-start; }
.daterange-btn span { flex: 1; text-align: left; }

.daterange-panel {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 200;
    background: var(--card-bg);
    border: 1px solid var(--border-light);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    overflow: hidden;
}
.daterange-panel.open { display: flex; }
.daterange-panel.align-right { left: auto; right: 0; }

.daterange-presets {
    display: flex;
    flex-direction: column;
    width: 170px;
    border-right: 1px solid var(--border-light);
    padding: 8px;
    gap: 2px;
    flex-shrink: 0;
}
.daterange-preset {
    text-align: left;
    padding: 8px 10px;
    border: none;
    background: transparent;
    border-radius: var(--radius-sm);
    font-size: 13px;
    color: var(--text);
    cursor: pointer;
    font-family: inherit;
    white-space: nowrap;
}
.daterange-preset:hover { background: var(--bg); }
.daterange-preset.active { background: var(--primary-bg); color: var(--primary); font-weight: 600; }

.daterange-calendars { display: flex; flex-direction: column; min-width: 520px; }
.daterange-cal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px 0;
}
.daterange-cal-months {
    display: flex;
    flex: 1;
    justify-content: space-around;
    font-weight: 600;
    font-size: 13px;
}
.daterange-cal-grids { display: flex; padding: 8px 16px 16px; gap: 24px; }
.daterange-cal-grid { flex: 1; }

.daterange-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); margin-bottom: 4px; }
.daterange-weekdays span { text-align: center; font-size: 11px; color: var(--muted); font-weight: 600; padding: 4px 0; }

.daterange-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.daterange-day {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 30px;
    font-size: 13px;
    border-radius: 4px;
    cursor: pointer;
    color: var(--text);
}
.daterange-day:hover { background: var(--primary-bg); }
.daterange-day.empty { cursor: default; visibility: hidden; }
.daterange-day.today { box-shadow: inset 0 0 0 1px var(--primary); }
.daterange-day.in-range { background: var(--primary-bg); border-radius: 0; }
.daterange-day.range-start,
.daterange-day.range-end {
    background: var(--primary-fill);
    color: #fff;
    font-weight: 600;
    box-shadow: none;
}

.daterange-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-top: 1px solid var(--border-light);
    gap: 12px;
}
.daterange-selected-text { font-size: 13px; color: var(--text-secondary); }
.daterange-footer-actions { display: flex; gap: 8px; flex-shrink: 0; }

@media (max-width: 768px) {
    .daterange-panel {
        position: fixed;
        top: 70px;
        left: 50%;
        transform: translateX(-50%);
        flex-direction: column;
        width: 94vw;
        max-height: 80vh;
        overflow-y: auto;
    }
    .daterange-presets {
        flex-direction: row;
        flex-wrap: wrap;
        width: auto;
        border-right: none;
        border-bottom: 1px solid var(--border-light);
    }
    .daterange-calendars { min-width: 0; }
    .daterange-cal-grids { flex-direction: column; gap: 16px; padding: 8px 12px 12px; }
    .daterange-footer { flex-direction: column; align-items: stretch; }
}

/* LOGIN PAGE */
.login-page { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: var(--bg); }
.login-box {
    background: var(--card-bg);
    padding: 40px 36px;
    border-radius: var(--radius);
    width: 100%;
    max-width: 380px;
    border: 1px solid var(--border-light);
    box-shadow: var(--shadow-md);
    text-align: center;
}
.login-box .login-logo { width: 52px; height: 52px; border-radius: 50%; background: var(--green); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 26px; margin: 0 auto 16px; }
.login-box h2 { font-size: 22px; margin-bottom: 4px; font-weight: 700; }
.login-box p  { color: var(--muted); margin-bottom: 24px; font-size: 13px; }
.login-box .form-group label { text-align: left; }
.login-box .alert { text-align: left; }

/* PASSWORD TOGGLE (lihat/sembunyikan isi kotak password) — reusable, tidak khusus login-page.
   Password tetap default TERSEMBUNYI; "lihat" auto-kembali sembunyi lewat JS (lihat pemakaian
   di admin/login.php), CSS di sini cuma urusan tata letak ikon di dalam kotak input. */
.password-input-wrap { position: relative; }
.password-input-wrap input { padding-right: 40px !important; }
.password-toggle-btn {
    position: absolute;
    top: 50%;
    right: 4px;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: var(--muted);
    cursor: pointer;
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
}
.password-toggle-btn:hover { color: var(--primary); background: var(--primary-bg); }

/* DATE PICKER CUSTOM (form Cuti/Izin operator, operator/_views/index.php) — dibuat sendiri
   (bukan native <input type=date>) supaya bisa punya tombol OK + Enter buat nutup kalender dan
   quick-select Besok/Lusa. Ditambahkan belakangan (2026-08-30) — HTML/JS-nya sempat kepasang
   duluan TANPA CSS ini sama sekali, jadi kalendernya sempat tampil sebagai daftar tombol
   mentah tanpa tata letak grid (pelajaran: SELALU screenshot komponen baru di browser
   sebelum dianggap selesai, jangan cuma percaya "kodenya sudah lengkap"). */
.date-picker { position: relative; }
.date-picker-input { cursor: pointer; background: var(--card-bg); }
/* min-width (§ update 2026-09-02, ditemukan lewat laporan visual Yusuf) — .date-range-picker
   nampilin dua tanggal sekaligus ("DD/MM/YYYY – DD/MM/YYYY", ~23 karakter), lebih panjang dari
   input teks biasa. `.form-row .form-group { min-width: 160px }` bawaan kelewat sempit buat
   teks itu, apalagi kalau berbagi baris sama banyak filter lain (Status/Asal/Jenis/Pengaju/
   Tanggal sekaligus di admin/_views/pengajuan.php) — tahun di ujung kanan kepotong ("31/01/20"
   bukan "31/01/2020", readonly input tidak scroll/ellipsis, konten yang kepanjangan cuma
   ke-clip diam-diam di tepi kanan box). */
.date-range-picker .date-picker-input { min-width: 210px; }
/* [Ronde 2, ditemukan lewat laporan visual Yusuf] Fix di atas TERNYATA cuma pindahin bug, bukan
   nyelesein: min-width di INPUT doang bikin box-nya kelebaran dari `.form-group` pembungkusnya
   sendiri (yang masih `min-width:160px`, LEBIH SEMPIT dari 210px), jadi flexbox TIDAK tahu
   harus alokasi ruang seberapa buat kolom Tanggal ini — input akhirnya numpuk/nabrak ke kolom
   filter sebelahnya (Status) alih-alih flex-wrap ke baris baru. Fix beneran: kasih min-width ke
   `.form-group` PEMBUNGKUSNYA juga (bukan cuma ke input di dalamnya), supaya flexbox
   ngalokasiin ruang yang cukup dari awal — filter lain di baris yang sama baru wrap ke bawah
   kalau memang kepepet, bukan numpuk/overlap. */
.form-row .form-group:has(.date-range-picker) { min-width: 230px; }
.date-picker-popup {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 30;
    width: 264px;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
    padding: 12px;
}
.date-picker-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
    font-weight: 600;
    font-size: 14px;
}
.date-picker-nav {
    border: none;
    background: none;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 4px 10px;
    color: var(--muted);
    border-radius: var(--radius-sm);
}
.date-picker-nav:hover { color: var(--primary); background: var(--primary-bg); }
.date-picker-weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    text-align: center;
    font-size: 11px;
    color: var(--muted);
    margin-bottom: 6px;
}
.date-picker-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 3px;
}
.date-picker-day {
    position: relative;
    border: none;
    background: none;
    padding: 7px 0;
    font-size: 13px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    text-align: center;
}
.date-picker-day:hover:not(:disabled) { background: var(--primary-bg); color: var(--primary); }
.date-picker-day.selected { background: var(--primary-fill); color: #fff; font-weight: 600; }
.date-picker-day:disabled { color: var(--border); cursor: not-allowed; }
.date-picker-day-blank { visibility: hidden; cursor: default; }
/* Range picker (2 klik = Mulai+Selesai, § redesign 2026-08-30 form Cuti/Izin) — range-single
   dipakai kalau tanggal yang sama diklik 2x (izin 1 hari), range-start/range-end buat ujung
   rentang beda hari, range-mid buat hari DI ANTARA keduanya (biar rentangnya kelihatan
   nyambung, bukan cuma dua tanggal terpisah). */
.date-picker-day.range-single,
.date-picker-day.range-start,
.date-picker-day.range-end { background: var(--primary-fill); color: #fff; font-weight: 600; }
.date-picker-day.range-mid { background: var(--primary-bg); color: var(--primary); }
/* Jembatan visual antar sel yang berdekatan dalam rentang yang sama — isi celah `gap:3px` grid
   dengan warna biru muda (SELALU var(--primary-bg), apa pun warna dua sel yang disambung: tua-
   muda, muda-muda, ataupun tua-tua) supaya band-nya kelihatan menyambung utuh, bukan kotak-
   kotak terpisah (diminta 2026-08-30, sebelumnya tiap sel kelihatan lepas-lepas). Lebar 3px
   pas nutup celah `gap` .date-picker-grid — kalau gap-nya diubah, ini ikut disesuaikan. */
.date-picker-day.connect-right::after,
.date-picker-day.connect-left::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 3px;
    background: var(--primary-bg);
}
.date-picker-day.connect-right::after { right: -3px; }
.date-picker-day.connect-left::before { left: -3px; }
/* Status "Mulai: ... Selesai: ..." di atas footer popup — cek cepat tanpa perlu lihat highlight
   di grid (penting waktu pindah bulan, highlight tanggal Mulai di bulan sebelumnya sudah tidak
   kelihatan sama sekali). */
.date-picker-selection {
    margin-top: 10px;
    padding: 8px 10px;
    background: var(--bg);
    border-radius: var(--radius-sm);
    font-size: 12px;
    color: var(--text);
}
.date-picker-footer {
    display: flex;
    justify-content: space-between;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border-light);
}
.leave-quick-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px; }
/* Toggle "Atur jam tertentu" + dua kotak jam di dalam popup kalender (§ redesign 2026-08-30) —
   defaultnya disembunyikan (`hidden`), muncul begitu checkbox dicentang. Jam Mulai/Jam Selesai
   otomatis mengikuti tanggal Mulai/Selesai yang sudah dipilih di kalender atasnya. */
.leave-time-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border-light);
    font-size: 13px;
    cursor: pointer;
}
.leave-time-detail { display: flex; gap: 8px; margin-top: 8px; }
.leave-time-detail > div { flex: 1; }
.leave-time-detail label { display: block; font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.leave-time-detail input[type=time] {
    width: 100%;
    box-sizing: border-box;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 13px;
}
