/* =========================================================
   DAILY EGG — DESIGN SYSTEM
   Dipakai bersama di semua halaman (admin/kurir/affiliator).
   Palet warna & tipografi mengikuti referensi UI dashboard kurir.
   ========================================================= */

:root{
  --bg:#FFFFFF;
  --surface:#FFFFFF;
  --surface-alt:#E4EDEA;
  --border:#DAE5E1;

  --green-900:#0E3B2E;
  --green-700:#1B6F54;
  --green-100:#DCEDE5;

  /* Varian "dingin" khusus gradasi hero (.hero & .hero-native) - tetap
     berbasis HIJAU primary (bukan biru/teal dominan seperti percobaan
     sebelumnya), cuma undertone-nya digeser tipis ke arah dingin & jarak
     lightness kedua stop-nya sengaja dibikin lebih DEKAT dari
     --green-900/700 asli supaya gradasinya tidak terlalu kontras (lebih
     halus/menyatu). Dipisah dari --green-900/700 biasa karena keduanya
     masih dipakai di tempat lain (teks, badge, banner) yang tidak perlu
     ikut berubah. */
  --green-cold-900:#0F332A;
  --green-cold-700:#1B5B4A;

  --orange-500:#E8871E;
  --orange-600:#BE650D;
  --orange-100:#FCEBD6;

  --blue-600:#356E88;
  --blue-100:#E1EEF2;

  --amber-600:#8C6A2A;
  --amber-100:#F2EEDD;

  --red-600:#B23A28;
  --red-100:#F8E2DC;

  --ink:#1C2A26;
  --ink-muted:#54655D;
  --ink-soft:#7A8B83;

  /* Alias teks/ikon dari --green-900 dipakai di elemen yang duduk DI ATAS
     --surface/--bg (bukan di atas warna solid hijau) - contoh: judul topbar,
     angka statistik, ikon tombol. Dipisah dari --green-900 "asli" (yang
     tetap dipakai sebagai BACKGROUND solid di hero/login/tombol, warnanya
     TIDAK perlu ikut berubah di dark mode) karena keduanya butuh arah
     override yang beda pas dark mode: --green-900 tetap gelap (background),
     --green-900-fg jadi terang (supaya kebaca di atas background gelap). */
  --green-900-fg:var(--green-900);

  --shadow-sm:0 1px 2px rgba(14,59,46,.04);
  --shadow-md:0 4px 12px rgba(14,59,46,.06);

  --radius-sm:10px;
  --radius-md:10px;
  --radius-lg:22px;
  --radius-full:999px;

  --font-display:'Manrope',sans-serif;
  --font-body:'Inter',sans-serif;
}

/* =========================================================
   MODE GELAP
   Diaktifkan lewat atribut data-theme="dark" di <html> (dirender
   server-side dari kolom users.theme, lihat includes/layout-top.php &
   includes/auth.php - jadi sudah aktif SEBELUM CSS ini sempat kebaca,
   tidak ada kedipan mode terang sekejap). Ditoggle dari dropdown profil
   topbar (lihat .dropdown-toggle-row/.theme-switch di bawah &
   handler-nya di script.js), disimpan ke akun lewat
   actions/simpan-tema.php.

   Base warna sesuai request: hitam pekat (#000) buat --bg. --surface &
   --surface-alt SENGAJA dibuat SEDIKIT lebih terang dari #000 murni
   (bukan #000 juga) - kalau card/dropdown/bottom-nav dibuat PERSIS sama
   hitamnya dengan halaman di belakangnya, batas antar elemen jadi tidak
   kelihatan sama sekali (semua "menyatu" jadi satu bidang hitam polos),
   jadi tetap dikasih perbedaan gelap tipis supaya elemen² itu tetap
   kebaca sebagai lapisan terpisah (comfortable, bukan cuma "hitam semua").

   Warna aksen (hijau/oranye/biru/amber/merah) yang tadinya dipakai
   sebagai TEKS/IKON (kombinasi *-600/700/900 di atas *-100 tint, atau di
   atas --surface) diterangkan supaya kontrasnya cukup di atas latar
   gelap - versi lama terlalu gelap (dirancang buat teks di atas putih)
   jadi nyaris tidak kebaca kalau dipertahankan apa adanya di atas hitam.
   --green-900 "asli" (dipakai background hero/login/tombol) SENGAJA
   TIDAK diubah - hijau tua itu sudah cukup kontras di atas hitam sebagai
   BLOK warna solid, dan tetap menjaga identitas hijau brand yang sama di
   kedua mode.

   REVISI: --green-900-fg (teks utama seperti judul, angka statistik,
   nilai) sebelumnya hijau terang (#5FE3AC) yang kerasa "ijo-ijo" dan
   norak dipakai di banyak tempat - sekarang diganti putih-lembut
   (#F2F2F0) senada dengan --ink, jadi teks utama netral kayak di mode
   terang (item hitam -> putih), warna hijau/oranye/dll disisakan cuma
   buat elemen aksen (badge, ikon, tombol) bukan teks utama. Semua warna
   aksen (green-700/orange-500-600/blue-600/amber-600/red-600) juga
   diturunkan sedikit saturasi & kecerahannya (tidak neon) supaya lebih
   nyaman dipandang lama-lama di atas latar gelap, termasuk tombol oranye
   (.btn-primary/.hero-btn) yang sebelumnya kelewat terang/menyilaukan di
   atas hitam. --bg/--surface/--surface-alt juga digeser dikit dari hitam
   pekat murni (#000) ke abu sangat gelap supaya kontras dengan teks putih
   tidak terlalu tajam. */
:root[data-theme="dark"]{
  --bg:#0A0A0A;
  --surface:#151515;
  --surface-alt:#202020;
  --border:#2C2C2C;

  --green-900-fg:#F2F2F0;
  --green-700:#3FA980;
  --green-100:rgba(63,169,128,.16);

  --orange-500:#D9894A;
  --orange-600:#E3A462;
  --orange-100:rgba(217,137,74,.16);

  --blue-600:#6FA8C9;
  --blue-100:rgba(111,168,201,.16);

  --amber-600:#D1AD6B;
  --amber-100:rgba(209,173,107,.16);

  --red-600:#D97F6C;
  --red-100:rgba(217,127,108,.18);

  --ink:#EDEDED;
  --ink-muted:#A8A8A8;
  --ink-soft:#797979;

  --shadow-sm:0 1px 2px rgba(0,0,0,.5);
  --shadow-md:0 4px 14px rgba(0,0,0,.6);
}

*{ box-sizing:border-box; margin:0; padding:0; 
    -webkit-user-select: none; /* Safari/Chrome lama */
    -moz-user-select: none;    /* Firefox lama */
    -ms-user-select: none;     /* IE/Edge lama */
    user-select: none;
    -webkit-tap-highlight-color: transparent;
 }
html{ -webkit-text-size-adjust:100%; overscroll-behavior:none; touch-action:manipulation; }
/* Sembunyikan scrollbar di semua elemen yang bisa di-scroll (halaman utama,
   modal-box, bottom-sheet, dst) - app native gak ada garis/scrollbar abu-abu
   nempel di pinggir layar, ini salah satu tanda paling ketara "ini web"
   kalau masih kelihatan (apalagi di desktop/tablet). Scroll TETAP jalan
   normal, cuma indikator visualnya yang dibuang. */
*{ scrollbar-width:none; -ms-overflow-style:none; } /* Firefox & IE/Edge lama */
*::-webkit-scrollbar{ display:none; width:0; height:0; } /* Chrome/Safari/Edge modern */
body{
  font-family:var(--font-body);
  background:var(--bg);
  color:var(--ink);
  -webkit-font-smoothing:antialiased;
  line-height:1.5;
  min-height:100vh;
  overscroll-behavior:none; /* no rubber-band bounce/pull-to-refresh browser - salah satu hal paling ketara "ini web" */
  /* touch-action:manipulation mematikan double-tap-to-zoom (delay 300ms
     tap juga ikut hilang) - salah satu tanda paling ketara "ini web"
     kalau masih nyala. Pinch-zoom sendiri sudah dimatikan lewat
     "user-scalable=no" di meta viewport (lihat includes/layout-top.php),
     tapi iOS Safari sejak versi 10 SENGAJA tetap izinkan pinch-zoom demi
     aksesibilitas walau viewport bilang begitu - makanya masih dibantu
     script kecil di script.js yang cegah gesture pinch & double-tap. */
  touch-action:manipulation;
}
a{ text-decoration:none; color:inherit; }
button{ font:inherit; cursor:pointer; border:none; background:none; color:inherit; }
ul{ list-style:none; }
img{ max-width:100%; display:block; }

.icon{ width:20px; height:20px; stroke:currentColor; fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; flex-shrink:0; }
.icon-sm{ width:15px; height:15px; }
.icon-xs{ width:11px; height:11px; }
.icon-lg{ width:26px; height:26px; }
.tier-badge svg{ fill:currentColor; stroke:none; }
/* Ikon titik 3 (trigger dropdown profil di topbar) dibuat dari 3
   <circle>, jadi perlu FILLED (bukan cuma outline stroke seperti ikon
   lain) supaya kelihatan sebagai titik padat, bukan lingkaran kosong. */
.icon-dots{ fill:currentColor; stroke:none; }

a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible{
  outline:2px solid var(--orange-500);
  outline-offset:2px;
  border-radius:var(--radius-sm);
}

@media (prefers-reduced-motion: reduce){
  *{ transition-duration:.01ms !important; animation-duration:.01ms !important; }
}

/* ============ LAYOUT ============ */
.app{ display:flex; min-height:100vh; position:relative; }

/* .nav-item/.nav-count di bawah ini AWALNYA dibuat buat sidebar (sudah
   dihapus - lihat .bottom-nav di bawah, gantinya), tapi classnya masih
   dipakai di tempat lain sebagai daftar baris yang bisa diklik (dikombinasi
   dengan .list-row, lihat admin-panel.php, customer-detail.php dkk) -
   SENGAJA TIDAK dihapus. */
.nav-item{
  display:flex; align-items:center; gap:12px;
  padding:11px 12px; border-radius:var(--radius-sm);
  color:rgba(247,243,236,.68); font-size:.9rem; font-weight:500;
  transition:background .15s ease, color .15s ease;
}
.nav-item svg{ opacity:.85; }
.nav-item:hover{ background:rgba(255,255,255,.06); color:#fff; }
.nav-item.is-active{
  background:rgba(255,255,255,.1); color:#fff;
}
.nav-count{
  margin-left:auto; background:var(--orange-500); color:var(--green-900);
  font-size:.68rem; font-weight:700; padding:2px 7px; border-radius:var(--radius-full);
}

/* ============ MAIN ============ */
.main{ flex:1; min-width:0; display:flex; flex-direction:column; }

/* ============ BOTTOM NAVBAR (v6 — adaptive-width soft pill) ============
   Tab bar persistent ala app native, cuma buat admin & kurir (lihat
   $bottomNavItems di includes/layout-top.php). Affiliator tidak punya ini -
   menunya ada di dropdown-profile topbar. Semua tab (termasuk "Tambah")
   dirender SAMA - tidak ada lagi tab spesial berbentuk bulat/FAB.

   Konsep: bar NEMPEL rata ke tepi kiri/kanan/bawah layar, full width, garis
   tipis di atas. Icon-only secara default. Tab yang lagi aktif dapat
   "pill" background LEMBUT (bukan warna solid) yang muncul dengan SCALE IN
   dan hilang dengan SCALE OUT di tempatnya sendiri (bukan geser/translate
   pindah slot).

   Pill-nya (.bottom-nav-item-inner) inline-flex shrink-to-fit - lebarnya
   SELALU ngikutin isinya persis (icon-only pas nonaktif, icon+label pas
   aktif), TIDAK PERNAH dipatok ke angka tebakan. Label direveal lewat
   trik grid-template-columns 0fr -> 1fr (BUKAN max-width ke angka
   hardcode kayak versi sebelumnya) - teknik ini otomatis tahu lebar ASLI
   teksnya berapa pun panjangnya, jadi label PANJANG SEKALIPUN tidak akan
   pernah kepotong, sekaligus tetap bisa di-transition mulus.

   Label tab aktif muncul DI SAMPING icon (bukan di bawah). SLOT tiap tab
   (.bottom-nav-item) content-sized (flex:0 0 auto, BUKAN flex-grow) -
   lebarnya juga murni ngikutin pill di dalamnya. Jarak antar tab dibentuk
   justify-content:space-between di .bottom-nav-track: begitu 1 pill
   melebar (label panjang), sisa ruang gap otomatis berkurang -> tab lain
   ikut geser mendekat SEPERLUNYA SAJA sesuai panjang label yang aktif
   (label pendek = geser dikit, label panjang = geser lebih jauh) - efek
   "adaptif" ini murni hasil flexbox+grid biasa, TANPA JS ngukur/ngitung
   lebar teks apa pun. Trigger pill/label-nya cuma class .is-active yang
   ditoggle PHP (render awal) atau script.js (SPA - lihat
   applyPageContent() di script.js, itu HANYA toggle class, tidak pernah
   sentuh CSS ini). */
.bottom-nav{
  position:fixed; left:0; right:0; bottom:0;
  z-index:45;
  display:flex; align-items:stretch;
  background:var(--surface);
  border-top:1px solid var(--border);
  /* Padding kiri-kanan SENGAJA 0 di sini (bukan angka tebakan seperti
     8px sebelumnya) - "padding tepi" yang keliatan sekarang justru
     dihasilkan otomatis oleh justify-content:space-evenly di
     .bottom-nav-track di bawah, jadi jaraknya OTOMATIS SAMA PERSIS
     dengan gap antar icon (bukan dua angka beda yang harus disamakan
     manual tiap kali salah satunya diubah). */
  padding:11px 0 calc(11px + env(safe-area-inset-bottom));
  height:calc(68px + env(safe-area-inset-bottom));
  transform:translateY(0); opacity:1;
  transition:transform .34s cubic-bezier(.34,1.56,.64,1), opacity .22s ease;
}
/* Easing masuknya .bottom-nav pakai "back" curve (cubic-bezier(.34,1.56,.64,1))
   yang SENGAJA overshoot - nilai translateY sempat lewat 0 (dikit minus)
   sebelum settle balik, itu yang bikin efek "muncul lalu mantul". Karena
   navbar ini position:fixed & bottom:0, saat overshoot dia sempat kegeser
   ke ATAS melebihi posisi restnya sepersekian detik - dan karena bawahnya
   HANYA setinggi element ini (tidak lebih), momen itu bikin celah tipis
   nongol di tepi paling bawah layar yang nembus ke konten di baliknya
   (bocor). Fix: ::after nempel pas di bawah .bottom-nav, warna SAMA
   (surface), ikut translateY bareng induknya (inherit lewat parent
   transform, bukan dianimasikan sendiri) - jadi begitu navbar "mantul"
   naik dikit, sisa ruang di bawahnya tetap ketutup warna solid ini,
   BUKAN nembus. Tingginya digenerasikan lebih dari cukup (24px) karena
   overshoot bezier ini secara matematis maksimal cuma exceed ~15-20%
   dari total jarak translate, jauh di bawah itu. */
.bottom-nav::after{
  content:''; position:absolute; left:0; right:0; top:100%;
  height:24px; background:var(--surface);
}
.bottom-nav.bottom-nav-hidden{
  transform:translateY(100%); opacity:0;
  pointer-events:none;
  transition:transform .26s cubic-bezier(.4,0,.2,1), opacity .18s ease;
}
/* space-evenly (bukan space-between lagi) - flexbox otomatis kasih
   ruang yang SAMA BESAR di kedua tepi track DAN di antara tiap tab,
   jadi "padding" kiri-kanan yang kelihatan = gap antar icon, murni
   hasil flexbox (tidak perlu dihitung/disamakan manual). */
.bottom-nav-track{ position:relative; display:flex; flex:1; justify-content:space-evenly; }

.bottom-nav-item{
  position:relative;
  flex:0 0 auto; min-width:0;
  display:flex; align-items:center; justify-content:center;
  color:var(--ink-soft);
  transition:color .22s ease;
}
/* Slot tiap tab CONTENT-SIZED (flex-basis:auto, grow/shrink 0) - lebarnya
   murni ngikutin isinya sendiri (icon-only pas nonaktif, icon+label pas
   aktif), BUKAN dipaksa rata pakai flex-grow. Jarak antar tab dibentuk
   justify-content:space-between di .bottom-nav-track: begitu satu pill
   melebar buat nampilin label panjang, sisa ruang buat gap otomatis
   berkurang -> tab-tab lain ikut geser mendekat SEPERLUNYA SAJA (gak lebih
   dari yang dibutuhkan). Label pendek -> pill dikit lebih lebar -> tab
   lain geser dikit doang. Semua ini murni hasil flexbox biasa, bukan
   angka lebar yang di-hardcode. */
.bottom-nav-item.is-active{ color:var(--green-900-fg); }

/* Pembungkus icon+label - inline-flex shrink-to-fit, jadi acuan bentuk &
   ukuran pill (padding SIMETRIS di semua sisi). Lebarnya otomatis
   ngikutin .bottom-nav-label di bawah, yang di-reveal pakai trik grid
   (BUKAN max-width tebakan) - jadi pill SELALU pas dengan panjang teks
   apa pun, tidak pernah kepotong. */
.bottom-nav-item-inner{
  position:relative; z-index:1;
  display:inline-flex; align-items:center; justify-content:center;
  padding:12px 20px;
  border-radius:var(--radius-full);
  font-family:var(--font-display); font-weight:700; font-size:.64rem;
}
/* Pill lembut - warna tint soft (bukan solid), MUNCUL/HILANG dengan scale
   di tempatnya sendiri (bukan geser pindah posisi). Di-inset 0 dari inner
   (bukan dari slot), jadi otomatis selalu SEPAS icon+label + padding di
   atas, konsisten di semua sisi. */
.bottom-nav-item-inner::before{
  content:''; position:absolute; inset:0; z-index:-1;
  border-radius:inherit;
  background:var(--green-100);
  opacity:0; transform:scale(.6);
  transition:opacity .24s ease, transform .3s cubic-bezier(.34,1.56,.64,1);
}
.bottom-nav-item.is-active .bottom-nav-item-inner::before{ opacity:1; transform:scale(1); }

.bottom-nav-item svg{
  width:20px; height:20px; flex-shrink:0;
  transition:transform .3s cubic-bezier(.34,1.56,.64,1);
}
.bottom-nav-item.is-active svg{ transform:scale(1.05); }
.bottom-nav-item:active svg{ transform:scale(.82); transition-duration:.12s; }
/* Label di SAMPING kanan icon (bukan di bawah) - direveal lewat
   grid-template-columns 0fr -> 1fr (BEDA dari max-width ke angka
   tebakan seperti sebelumnya: teknik grid ini "tahu" persis lebar asli
   teksnya - berapa pun panjangnya - jadi labelnya SELALU tampil utuh,
   tidak pernah kepotong).
   PENTING (perf): grid-template-columns & margin-left itu LAYOUT
   properties - meng-animasikannya bikin browser reflow ULANG SELURUH
   .bottom-nav-track (semua tab lain ikut dihitung ulang posisinya)
   di SETIAP FRAME animasi. Ini SELALU terjadi bersamaan dengan momen
   paling berat di app (fetch + swap #page-content + re-exec script
   halaman baru saat pindah page - lihat navigateTo() di script.js),
   jadi di HP low-end numpuk jadi jank/lag kerasa banget. Makanya
   sengaja DIBIKIN INSTAN (tanpa transition) - cuma properti transform/
   opacity di bawah (pill & icon) yang tetap dianimasikan karena itu
   compositor-only, murah, tidak memicu reflow. */
.bottom-nav-label{
  display:grid; grid-template-columns:0fr; margin-left:0; overflow:hidden;
}
.bottom-nav-item.is-active .bottom-nav-label{ grid-template-columns:1fr; margin-left:6px; }
.bottom-nav-label span{
  min-width:0; overflow:hidden; white-space:nowrap; font-size:.68rem;
  opacity:0; transition:opacity .16s ease;
}
.bottom-nav-item.is-active .bottom-nav-label span{ opacity:1; }

/* Ruang di bawah konten supaya baris terakhir tidak ketutupan bottom navbar
   fixed. */
body.has-bottom-nav .content{ padding-bottom:calc(88px + env(safe-area-inset-bottom)); }
/* Halaman peta full-layar (mis. rute.php) sembunyikan topbar-nya sendiri
   demi tampilan penuh - bottom navbar ikut disembunyikan di sini juga. */
body.page-fullscreen-map .bottom-nav{ display:none; }



.topbar{
  position:sticky; top:0; z-index:30;
  display:flex; align-items:center; gap:14px;
  padding:14px 18px;
  background:var(--bg);
  border-bottom:1px solid var(--border);
}
.icon-btn{
  width:38px; height:38px; display:flex; align-items:center; justify-content:center;
  border-radius:var(--radius-full); color:var(--green-900-fg); position:relative;
  transition:background .15s ease; flex-shrink:0;
}
.icon-btn:hover{ background:var(--surface-alt); }

.ping{
  position:absolute; top:8px; right:9px; width:7px; height:7px;
  background:var(--orange-500); border-radius:50%;
}
/* Badge angka notif belum dibaca di atas icon lonceng topbar - beda dari
   .ping (cuma titik) karena sekarang ikon notif langsung ke notifikasi.php
   (bukan dropdown lagi), jadi jumlahnya perlu kelihatan tanpa harus diklik. */
.icon-btn-badge{
  position:absolute; top:2px; right:2px; min-width:16px; height:16px; padding:0 4px;
  display:flex; align-items:center; justify-content:center;
  background:var(--orange-500); color:#fff; font-size:.62rem; font-weight:700;
  border-radius:var(--radius-full); line-height:1; border:2px solid var(--bg);
}

.topbar-logo{ width:32px; height:32px; border-radius:9px; flex-shrink:0; object-fit:cover; transition:transform .15s ease; }
.topbar-logo-btn{ display:flex; padding:0; line-height:0; flex-shrink:0; border-radius:9px; }
.topbar-logo-btn:active .topbar-logo{ transform:scale(.92); }

.topbar-date{ display:flex; align-items:baseline; gap:6px; font-size:.84rem; color:var(--ink-muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.topbar-day{ font-weight:700; color:var(--green-900-fg); font-family:var(--font-display); }
.topbar-dot{ opacity:.5; }

.topbar-title{ font-family:var(--font-display); font-weight:800; font-size:1rem; color:var(--green-900-fg); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

.topbar-actions{ margin-left:auto; display:flex; align-items:center; gap:8px; flex-shrink:0; }
.dropdown-wrap{ position:relative; }

.dropdown{
  position:fixed; right:10px; top:75px;
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--radius-md);
  min-width:230px; padding:8px;
  max-width: calc(100% - 20px);
  opacity:0; visibility:hidden; transform:translateY(-6px);
  transition:opacity .18s ease, transform .18s ease, visibility .18s;
  z-index:45;
}
.dropdown.is-open{ opacity:1; visibility:visible; transform:translateY(0); }
.dropdown-head{ font-family:var(--font-display); font-weight:700; font-size:.84rem; padding:8px 10px 10px; color:var(--green-900-fg); }

.notif-item{ display:flex; gap:10px; padding:9px 10px; border-radius:var(--radius-sm); align-items:flex-start; }
.notif-item:hover{ background:var(--surface-alt); }
.notif-item-icon{ width:32px; height:32px; border-radius:50%; background:var(--surface-alt); color:var(--ink-soft); display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.notif-dot{ width:7px; height:7px; margin-top:6px; margin-left:auto; border-radius:50%; background:var(--orange-500); flex-shrink:0; }
.notif-body{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.notif-title{ font-size:.81rem; font-weight:600; color:var(--ink); }
.notif-desc{ font-size:.77rem; color:var(--ink-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.notif-time{ font-size:.7rem; color:var(--ink-soft); margin-top:2px; }
.notif-empty{ font-size:.8rem; color:var(--ink-muted); padding:14px 10px; text-align:center; }

.dropdown-link{ display:flex; align-items:center; gap:10px; padding:9px 10px; border-radius:var(--radius-sm); font-size:.84rem; font-weight:500; color:var(--ink); }
.dropdown-link svg{ color:var(--ink-muted); width:17px; height:17px; }
.dropdown-link:hover{ background:var(--surface-alt); }
.dropdown-link-danger{ color:var(--red-600); }
.dropdown-link-danger svg{ color:var(--red-600); }
.dropdown-sep{ height:1px; background:var(--border); margin:6px 4px; }

/* Baris toggle "Mode Gelap" di dropdown profil - bentuknya sengaja disamakan
   persis dengan .dropdown-link (padding/radius/gap sama) supaya kelihatan
   seperti satu keluarga item menu, cuma di ujung kanan ada switch (bukan
   chevron/link biasa) karena ini aksi toggle instan, bukan navigasi ke
   halaman lain. <label> membungkus semuanya (bukan cuma switch-nya) supaya
   area klik/tap lebih lega - tap di mana saja di barisnya langsung toggle
   checkbox di dalamnya. */
.dropdown-toggle-row{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:9px 10px; border-radius:var(--radius-sm); cursor:pointer;
}
.dropdown-toggle-row:hover{ background:var(--surface-alt); }
.dropdown-toggle-label{ display:flex; align-items:center; gap:10px; font-size:.84rem; font-weight:500; color:var(--ink); }
.dropdown-toggle-label svg{ color:var(--ink-muted); width:17px; height:17px; }

/* Switch kecil ala iOS - input asli disembunyikan visual tapi tetap ada di
   DOM (accessible via <label for>, tetap bisa di-tab/spasi lewat keyboard). */
.theme-switch{ position:relative; flex-shrink:0; width:38px; height:22px; }
.theme-switch input{ position:absolute; width:1px; height:1px; opacity:0; pointer-events:none; }
.theme-switch-track{
  position:absolute; inset:0; border-radius:var(--radius-full);
  background:var(--surface-alt); border:1px solid var(--border);
  transition:background .18s ease, border-color .18s ease;
}
.theme-switch-thumb{
  position:absolute; top:2px; left:2px; width:16px; height:16px; border-radius:50%;
  background:var(--surface); box-shadow:0 1px 3px rgba(0,0,0,.25);
  transition:transform .22s cubic-bezier(.34,1.56,.64,1), background .18s ease;
}
.theme-switch input:checked + .theme-switch-track{ background:var(--green-700); border-color:var(--green-700); }
.theme-switch input:checked + .theme-switch-track .theme-switch-thumb{ transform:translateX(16px); background:#fff; }
.theme-switch input:focus-visible + .theme-switch-track{ outline:2px solid var(--orange-500); outline-offset:2px; }

/* ============ CONTENT ============ */
.content{
  /* --content-px/--content-pt = padding kiri-kanan & atas .content saat ini,
     dijadikan custom property (bukan cuma angka polos di `padding` di
     bawah) supaya .hero-native (lihat bagian Hero) bisa ngitung bleed-nya
     sendiri lewat calc() - otomatis ikut menyesuaikan kalau breakpoint
     desktop di bawah mengubah angka ini, tanpa perlu disetel manual dobel
     di dua tempat. */
  --content-px:18px; --content-pt:18px;
  padding:var(--content-pt) var(--content-px) 18px; max-width:1180px; width:100%; margin:0 auto;
  display:flex; flex-direction:column; gap:10px;
  transition:padding-bottom .28s ease;
}
.content-narrow{ max-width:720px; }

/* Wrapper div AJAX-refresh (dashboard-live/affiliator-live/statistik-live -
   lihat index.php & statistik.php) SEBELUMNYA cuma <div> polos, jadi
   section-section di dalamnya (hero, bento-card, banner-stack, dst) TIDAK
   ikut kena `gap` punya .content sama sekali - selama ini jaraknya beda-beda
   tergantung margin-top manual tiap section (atau malah 0 kalau lupa
   ditambahin, kayak di affiliator-stats.php/statistik-*.php). Disamakan ke
   flex column + gap yang SAMA dengan .content, supaya jadi satu sumber jarak
   antar-card untuk seluruh app (dashboard & statistik, semua role) - section
   baru yang ditambah nanti otomatis dapat jarak yang benar tanpa perlu
   margin-top manual lagi. */
#dashboard-live, #affiliator-live, #statistik-live{
  display:flex; flex-direction:column; gap:10px;
}

/* Transisi masuk konten tiap kali navigasi SPA (lihat applyPageContent() di
   script.js) - crossfade singkat, biar swap konten kerasa seperti
   "berpindah layar" ala app native, bukan potong instan kayak ganti
   halaman web biasa. Sengaja ditaruh di .content (BUKAN #page-content) -
   <header class="topbar"> juga anak langsung #page-content, kalau
   animasinya di situ header ikut fade tiap navigasi padahal harusnya
   kerasa "tetap ada" (persisten) dari sudut pandang pengguna. Fade-only
   (opacity, TANPA transform/translate) - beberapa halaman punya elemen
   position:fixed di dalam .content (modal, toast, FAB) dan transform pada
   leluhurnya bakal bikin containing block baru buat elemen fixed itu,
   jadi mereka bisa "meloncat" sekejap selagi animasi jalan.
   .page-entering-back dibedakan dari -forward (easing beda) supaya kalau
   nanti mau dibikin lebih kaya arahnya, tinggal di sini yang diubah -
   JS-nya sudah kirim tahu ini navigasi maju atau kembali.
   prefers-reduced-motion sudah otomatis ditangani lewat aturan global "*"
   di atas (animation-duration dipaksa .01ms). */
.content.page-entering-forward{ animation:page-fade-in .18s ease-out; }
.content.page-entering-back{ animation:page-fade-in .18s ease-in; }
@keyframes page-fade-in{ from{ opacity:0; } to{ opacity:1; } }

/* Hero
   Ringkas & rendah - tidak ada lagi lingkaran foto profil. Identitas
   (sapaan/nama/role) berbagi baris atas dengan badge tier (kalau ada),
   baris saldo/target ada di bawahnya - jadi hero tetap 1 kartu pendek
   walau isinya sedikit. */
.hero{
  position:relative; overflow:hidden;
  background:linear-gradient(160deg,var(--green-cold-900) 0%,var(--green-cold-700) 130%);
  border-radius:var(--radius-lg); padding:18px 20px; color:#fff;
  display:flex; flex-direction:column; gap:14px;
}
/* Glow "dingin" - dulu pakai radial oranye (kesan hangat), sekarang
   diganti aksen hijau-teal tipis (BUKAN cyan/biru terang seperti
   percobaan sebelumnya - itu kelihatan kebiruan banget) + highlight putih,
   supaya tetap senada dengan --green-cold-900/700 hijau primary di atas,
   cuma kerasa sedikit lebih dingin. */
.hero-glow{
  position:absolute; inset:0; pointer-events:none;
  background:
    radial-gradient(160px 160px at 88% -10%, rgba(45,180,140,.16), transparent 70%),
    radial-gradient(240px 240px at 105% 65%, rgba(56,120,120,.12), transparent 70%),
    radial-gradient(220px 220px at 105% 60%, rgba(255,255,255,.05), transparent 70%);
}
.hero-profile{ position:relative; z-index:1; display:flex; align-items:center; justify-content:space-between; gap:12px; }
.hero-text{ min-width:0; }
.greeting{ font-size:.78rem; color:rgba(255,255,255,.65); font-weight:500; }
.hero-name{ font-family:var(--font-display); font-weight:800; font-size:1.2rem; letter-spacing:-.01em; margin:2px 0 0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.hero-role{ font-size:.78rem; color:rgba(255,255,255,.65); font-weight:500; }
.hero-role a{ text-decoration:underline; text-underline-offset:2px; }

/* Pill tier - dulu lingkaran menempel di bawah foto profil, sekarang
   badge mandiri di pojok kanan atas hero (foto profil sudah dihapus). */
.tier-badge{
  position:relative; flex-shrink:0;
  display:flex; align-items:center; gap:5px;
  padding:7px 12px;
  border-radius:var(--radius-full);
  font-size:.74rem; font-weight:700; font-family:var(--font-display);
  white-space:nowrap;
}
.tier-pemula{ background:rgba(255,255,255,.16); color:#fff; }
.tier-silver{ background:linear-gradient(135deg,#D8DEDC,#A8B5B1); color:#33403C; }
.tier-gold{ background:linear-gradient(135deg,#F2C14E,#D4A017); color:#5C4300; }
.tier-platinum{ background:linear-gradient(135deg,#BFE3EE,#7FBFD6); color:#123A44; }
/* Pill NETRAL - dipakai buat label ROLE (Admin/Kurir) di pojok kanan atas
   hero admin/kurir, beda konsep dari tier gamifikasi affiliator di atas
   (walau visualnya sama persis dengan .tier-pemula) - dipisah class-nya
   supaya gak rancu kalau nanti salah satunya diubah sendiri-sendiri. */
.tier-neutral{ background:rgba(255,255,255,.16); color:#fff; }

.hero-target{ position:relative; z-index:1; }
.target-top{ display:flex; align-items:baseline; justify-content:space-between; margin-bottom:9px; gap:10px; }
.target-label{ display:flex; align-items:center; gap:7px; font-size:.8rem; font-weight:600; color:rgba(255,255,255,.85); }
.target-label svg{ color:var(--orange-500); }
.target-value{ font-family:var(--font-display); font-weight:800; font-size:.98rem; white-space:nowrap; }
.target-of{ font-family:var(--font-body); font-weight:500; font-size:.76rem; color:rgba(255,255,255,.6); }
.target-track{ height:8px; border-radius:var(--radius-full); background:rgba(255,255,255,.16); overflow:hidden; }
.target-fill{ height:100%; background:linear-gradient(90deg,var(--orange-500),#F2A94E); border-radius:var(--radius-full); transition:width 1s cubic-bezier(.4,0,.2,1); }

/* Baris nama + pill tier/rank BERSEBELAHAN (affiliator) - beda dari
   .hero-profile di bawah (yang misahin teks kiri vs badge pojok kanan
   lewat justify-content:space-between). Di sini pill-nya nempel LANGSUNG
   di samping nama ("Nama [Rank]"), bukan lagi di pojok kanan atas. */
.hero-name-row{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.hero-name-row .hero-name{ margin:0; min-width:0; flex-shrink:1; }

/* Hero variant: saldo besar (dulu dipakai affiliator - sekarang cuma
   dipakai kurir/saldo-kurir.php & affiliator/saldo.php, halaman detail
   yang memang ingin nominalnya jadi fokus utama. Dashboard affiliator
   sendiri sekarang pakai .hero-balance-row di bawah, bukan ini lagi). */
.hero-amount-row{
  position:relative; z-index:1; display:flex; align-items:center; justify-content:space-between; gap:14px; flex-wrap:wrap;
}
.hero-profile + .hero-amount-row{
  padding-top:14px; border-top:1px solid rgba(255,255,255,.14);
}
.hero-amount-label{ font-size:.76rem; color:rgba(255,255,255,.65); font-weight:500; margin-bottom:2px; }
.hero-amount-value{ font-family:var(--font-display); font-weight:800; font-size:1.5rem; letter-spacing:-.01em; }
.hero-btn{
  background:var(--orange-500); color:#fff; font-weight:700; font-size:.83rem;
  padding:9px 16px; border-radius:var(--radius-full); transition:background .15s ease;
  white-space:nowrap;
}
.hero-btn:hover{ background:var(--orange-600); }
/* Varian kecil - dipakai di dashboard affiliator (.hero-balance-row di
   bawah) supaya ukurannya seimbang dengan saldo yang sekarang dikecilkan
   ala app bank, bukan tombol besar seperti di halaman detail saldo. */
.hero-btn-sm{ padding:7px 13px; font-size:.74rem; }

/* Baris saldo ala APLIKASI BANK - dipakai KHUSUS di dashboard affiliator
   (partials/affiliator-stats.php), beda dari .hero-amount-row di atas:
   nominalnya sengaja kecil (bukan angka besar yang jadi fokus utama hero),
   dan bisa disembunyikan/ditampilkan lewat tombol mata (.hero-balance-eye)
   - baik nominal maupun tombol tarik saldo di sebelahnya sama-sama
   dikecilkan (lihat .hero-btn-sm) supaya proporsinya seimbang. */
.hero-balance-row{
  position:relative; z-index:1; display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding-top:14px; border-top:1px solid rgba(255,255,255,.14);
}
/* Sebelumnya .hero-balance-text pakai flex:1, jadi lebar penuh & mata
   (.hero-balance-eye) yang ditaruh sebagai elemen TERPISAH di sebelahnya
   ketarik jauh ke ujung kanan (nempel tombol Tarik Saldo, bukan di
   samping nominal) - ga "pantes". Sekarang lebar cuma sebesar isinya
   (tanpa flex:1), dan mata dipindah JADI BAGIAN dari baris nominal itu
   sendiri (lihat .hero-balance-amount di bawah) supaya benar-benar
   nempel di sebelah angka saldo. Tombol Tarik Saldo yang gantian didorong
   ke ujung kanan lewat margin-left:auto. */
.hero-balance-text{ min-width:0; }
.hero-balance-label{ font-size:.7rem; color:rgba(255,255,255,.6); font-weight:500; margin-bottom:1px; }
.hero-balance-amount{ display:flex; align-items:center; gap:5px; font-family:var(--font-display); font-weight:700; font-size:.92rem; letter-spacing:-.01em; }
/* Nominal asli vs versi disamarkan - cuma satu yang tampil sekaligus,
   ditoggle lewat class .is-hidden di .hero-balance-row (lihat tombol mata
   di partials/affiliator-stats.php - onclick-nya toggle class ini). Class
   ditaruh di ELEMEN INDUKnya (bukan di tiap span) supaya sekali toggle
   ikut mengganti ikon mata juga di bawah, tanpa JS tambahan.
   DEFAULT-nya section di PHP sudah dikasih class "is-hidden" langsung -
   saldo tersamarkan dulu saat halaman baru dibuka, user harus tap mata
   buat menampilkan nominal aslinya.
   .hero-balance-mask SENGAJA tidak override font-family/weight/size lagi
   (dulu ada letter-spacing beda sendiri) - supaya persis sama kayak
   .hero-balance-value saat toggle, cuma isinya beda. Karakter "*" pada
   .mask-stars turun sedikit lewat position:relative+top, karena glyph
   bintang di kebanyakan font render lebih tinggi dari titik tengah angka
   (defaultnya kelihatan "ngambang" kalau disandingkan sebaris dengan Rp). */
.hero-balance-mask{ display:none; }
.hero-balance-mask .mask-stars{ position:relative; top:.14em; letter-spacing:1px; }
.hero-balance-row.is-hidden .hero-balance-value{ display:none; }
.hero-balance-row.is-hidden .hero-balance-mask{ display:inline; }
.hero-balance-eye{
  width:24px; height:24px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  border-radius:50%; color:rgba(255,255,255,.8); transition:background .15s ease;
}
.hero-balance-eye:hover{ background:rgba(255,255,255,.12); }
.hero-balance-eye .eye-hide{ display:none; }
.hero-balance-row.is-hidden .hero-balance-eye .eye-show{ display:none; }
.hero-balance-row.is-hidden .hero-balance-eye .eye-hide{ display:inline; }
.hero-balance-row .hero-btn-sm{ margin-left:auto; }

/* Hero "native" - varian TANPA kartu (dipakai semua role: admin & kurir
   lewat index.php, affiliator lewat partials/affiliator-stats.php).
   Bedanya dari .hero biasa: background-nya sengaja BOCOR sampai nempel ke
   topbar & ke tepi kiri-kanan .content (ala header app native, bukan
   kartu mengambang lagi dengan margin putih di sekelilingnya) - dicapai
   lewat margin negatif seukuran padding .content (var(--content-px)/
   --content-pt di atas), jadi kalau breakpoint desktop mengubah padding
   itu, bleed di sini otomatis ikut menyesuaikan tanpa perlu disetel
   manual dobel di sini.
   Padding-bottom-nya sengaja dilebihkan (bukan dibikin seragam kayak
   sisi lain) - itu "ruang hijau" tempat .bento-card-overlap di bawah
   nanti ditarik naik lewat margin negatif miliknya sendiri, supaya
   badannya kelihatan separuh nutupin ekor hero ini, separuh lagi lanjut
   ke background putih normal di bawahnya. */
.hero-native{
  position:relative; overflow:hidden;
  background:linear-gradient(160deg,var(--green-cold-900) 0%,var(--green-cold-700) 130%);
  width:calc(100% + var(--content-px) * 2);
  margin:calc(-1 * var(--content-pt)) calc(-1 * var(--content-px)) 0;
  padding:18px var(--content-px) 54px; color:#fff;
  display:flex; flex-direction:column; gap:14px;
}

/* Varian "solo" - KHUSUS hero admin/kurir di index.php (cuma berisi
   .hero-profile, tanpa baris saldo kayak punya affiliator). Padding
   atas-bawahnya dikecilkan (18px -> 13px, 54px -> 53px) DAN disamakan:
   jarak dari header ke .hero-profile (padding-top) dibikin PERSIS sama
   dengan jarak dari .hero-profile ke .bento-card-overlap di bawahnya
   (padding-bottom 53px dikurangi margin -40px milik .bento-card-overlap
   = 13px juga) - biar keliatan seimbang atas-bawah. Affiliator SENGAJA
   TIDAK pakai varian ini (padding .hero-native default di atas tetap
   dipakai apa adanya) karena heronya beda struktur (ada baris saldo di
   antara profile & kartu ringkasan), jarak atas/bawahnya tidak
   sepadan/sebanding kalau dipaksa disamakan seperti punya admin/kurir.

   Jarak atas-bawahnya SEKARANG 10px (dulu 13px/53px) - disamakan dengan
   --content-gap (lihat `gap:10px` di .content & #dashboard-live) supaya
   jarak header->hero-profile dan hero-profile->bento-card-overlap di
   bawahnya kerasa konsisten dengan jarak antar-card lain di dashboard,
   bukan angka sendiri yang beda.

   Sisi ATAS: header ada DI LUAR .content (lihat #page-content di
   layout-top.php), jadi gap:10px milik .content TIDAK ikut kena di sini -
   padding-top 10px di atas SUDAH LANGSUNG jadi jarak yang kelihatan.

   Sisi BAWAH beda ceritanya: .hero-native-solo & #dashboard-live SAMA-SAMA
   flex child langsung dari .content, jadi gap:10px milik .content IKUT
   nambahin jarak di antara keduanya (dulu kelewat kehitung - dikira cuma
   padding vs margin negatif .bento-card-overlap doang, padahal sesudah
   padding-bottom masih ada `gap:10px` lagi sebelum ketemu #dashboard-live).
   padding-bottom karena itu dihitung dari 40px (margin-top negatif
   .bento-card-overlap) SAJA, BUKAN 10px + 40px lagi - 10px-nya sudah
   otomatis disumbang oleh gap:10px tadi. Hasil akhirnya: 40px
   (padding-bottom) + 10px (gap .content) - 40px (tarikan
   .bento-card-overlap) = 10px kelihatan, sama persis dengan sisi atas. */
.hero-native-solo{ padding:10px var(--content-px) 40px; }

/* Ikon telur besar & samar di pojok kanan hero - dekorasi supaya hero
   admin/kurir (yang isinya cuma nama+ID, tanpa saldo/statistik) tidak
   kerasa kosong. Ditaruh SEBELUM .hero-profile di markup (z-index:0, di
   bawah konten) & overflow:hidden di induknya yang motong bagian yang
   meluber. */
.hero-decor{
  position:absolute; right:-18px; bottom:-22px; z-index:0;
  width:130px; height:130px; color:#fff; opacity:.07;
  pointer-events:none;
}

/* Baris avatar inisial + nama/ID - avatar BUKAN foto profil (cuma
   monogram 2 huruf di lingkaran tipis), dipakai buat mengisi ruang
   kosong di sisi kiri hero admin/kurir supaya sebaris dengan pill role
   di kanan terasa seimbang, bukan cuma teks polos menggantung. */
.hero-id{ position:relative; z-index:1; display:flex; align-items:center; gap:11px; min-width:0; }
.hero-avatar{
  flex-shrink:0; width:42px; height:42px; border-radius:50%;
  background:rgba(255,255,255,.14); border:1px solid rgba(255,255,255,.2);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-display); font-weight:800; font-size:.92rem; color:#fff;
}

/* Kartu ringkasan yang "mengambang" di antara hero hijau (.hero-native)
   dan konten putih di bawahnya - dipakai bareng .bento-card (BUKAN
   pengganti, tinggal ditambah di class="" yang sama). Margin negatifnya
   dibikin LEBIH BESAR dari sisa padding-bottom .hero-native di atas,
   supaya beneran "naik" menutupi sebagian hero, bukan cuma pas
   menghilangkan jaraknya. SENGAJA TANPA box-shadow - design system ini
   konsisten tidak pakai box-shadow di kartu manapun (lihat .bento-card
   dkk, cuma pakai border tipis), z-index saja sudah cukup buat kesan "di
   atas" hero. */
.bento-card-overlap{
  position:relative; z-index:2;
  margin-top:-40px;
}

/* Baris kecil di bawah .bento-card-overlap buat link "Lihat statistik
   lengkap" - dulu nempel di .section-head DI ATAS kartu, sekarang harus
   dipindah ke BAWAH kartu karena kartu ini sekarang section tersendiri
   yang langsung bersebelahan dengan .hero-native (supaya margin
   negatifnya narik pas ke ekor hero, bukan malah ketutup teks judul
   kalau judulnya taruh di antara keduanya). */
.hero-native-linkrow{ display:flex; justify-content:flex-end; padding:0 2px; }

/* Custom pull-to-refresh (dashboard beranda admin/kurir) - lihat
   initPullToRefresh (gesture di script.js, bagian "PULL-TO-REFRESH
   KUSTOM") untuk penjelasan lengkap perilakunya. Pull-to-refresh bawaan
   browser sudah dimatikan (overscroll-behavior:none di atas), ini
   gantinya: #dashboard-live ditarik turun via transform (di-set inline
   lewat JS), #heroNative (position:relative + overflow:hidden, lihat
   .hero-native di atas) memanjang lewat padding-bottom bertambah supaya
   background hijaunya ikut menutup celah yang terbentuk - indikator di
   bawah ini otomatis kelihatan begitu hero memanjang, karena posisinya
   nempel ke bagian BAWAH kotak hero (bukan ke #dashboard-live).
   CATATAN PENTING: `will-change:transform` SENGAJA TIDAK dipasang statis di
   sini lagi (dulu ada `#dashboard-live{ will-change:transform; }` di baris
   ini) - walau cuma "hint" performa, will-change:transform tetap bikin
   elemen ini jadi containing block BARU buat semua descendant
   position:fixed di dalamnya (sama persis efeknya kayak transform
   sungguhan) - PERSIS SEPERTI overlay modal (.modal-overlay, lihat
   konfirmasiBatalOverlay di kurir-dashboard.php ada di DALAM
   #dashboard-live) jadi ikut "terkurung" ke ukuran #dashboard-live, BUKAN
   nempel ke viewport lagi (nggak full-layar & jadi ikut ke-scroll,
   bukannya diam mengambang). Sekarang will-change cuma di-set via JS
   (dashboardEl.style.willChange) TEPAT saat gesture tarik lagi aktif, dan
   dilepas lagi begitu gesture selesai - lihat initPullToRefresh() di
   script.js. */
.ptr-indicator{
  position:absolute; left:50%; bottom:12px; z-index:3;
  width:26px; height:26px; margin-left:-13px;
  display:flex; align-items:center; justify-content:center;
  opacity:0; transform:scale(.6);
  pointer-events:none;
}
.ptr-spinner{
  width:18px; height:18px; border-radius:50%;
  border:2.5px solid rgba(255,255,255,.3);
  border-top-color:#fff;
}
.ptr-indicator.is-loading .ptr-spinner{ animation:ptr-spin .6s linear infinite; }
@keyframes ptr-spin{ to{ transform:rotate(360deg); } }

/* Banners */
.banner{ display:flex; align-items:center; gap:12px; padding:14px 16px; border-radius:var(--radius-md); font-size:.85rem; font-weight:600; }
.banner-icon{ font-size:1.2rem; flex-shrink:0; }
.banner-success{ background:var(--green-100); color:var(--green-900-fg); }
.banner-warning{ background:var(--orange-100); color:var(--orange-600); }
.banner-danger{ background:var(--red-100); color:var(--red-600); }
.banner-info{ background:var(--blue-100); color:var(--blue-600); }
.banner-link{ display:flex; align-items:center; gap:12px; padding:14px 16px; border-radius:var(--radius-md); background:var(--surface); border:1px solid var(--border); transition:transform .15s ease; }
.banner-link:hover{ transform:translateY(-1px);  }
.banner-link .banner-text{ display:flex; flex-direction:column; gap:1px; flex:1; min-width:0; }
.banner-link .banner-title{ font-size:.85rem; font-weight:700; color:var(--ink); }
.banner-link .banner-sub{ font-size:.77rem; color:var(--ink-muted); }
.banner-link.banner-urgent{ border-color:#F0C7B5; background:var(--red-100); }
.banner-stack{ display:flex; flex-direction:column; gap:10px; }

/* Stats */
.stats-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:14px; }
.stats-grid-3{ grid-template-columns:repeat(2,1fr); }
.stat-card{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
  padding:17px; display:flex; flex-direction:column; gap:10px; 
}
.stat-icon{ width:36px; height:36px; border-radius:var(--radius-sm); display:flex; align-items:center; justify-content:center; }
.stat-icon svg{ width:18px; height:18px; }
.stat-icon-orange{ background:var(--orange-100); color:var(--orange-600); }
.stat-icon-green{ background:var(--green-100); color:var(--green-700); }
.stat-icon-amber{ background:var(--amber-100); color:var(--amber-600); }
.stat-icon-blue{ background:var(--blue-100); color:var(--blue-600); }
.stat-icon-red{ background:var(--red-100); color:var(--red-600); }
.stat-value{ font-family:var(--font-display); font-weight:800; font-size:1.45rem; color:var(--green-900-fg); line-height:1; }
.stat-unit{ font-size:.82rem; font-weight:600; color:var(--ink-muted); margin-left:2px; }
.stat-label{ font-size:.79rem; color:var(--ink-muted); font-weight:500; }

/* Bento stats gabungan - dipakai KHUSUS di ringkasan dashboard (lihat
   partials/dashboard-live.php, kurir-dashboard.php, affiliator-stats.php)
   supaya beda bentuk dari .menu-grid/.menu-card yang ada tepat di
   bawahnya - orang sempat ngira statistik ini bisa diklik kayak menu.
   SATU bingkai card gabungan (bukan kotak per-item terpisah kayak
   .stat-card di atas), item dipisah garis/strip tipis, bukan border
   individual per kotak. Halaman lain (statistik.php dkk) masih pakai
   .stats-grid/.stat-card biasa di atas - sengaja TIDAK diubah, karena di
   situ tidak ada .menu-grid bersebelahan yang bikin ambigu. */
.bento-card{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
  overflow:hidden;
}
.bento-row-3{ display:grid; grid-template-columns:repeat(3,1fr); }
.bento-row-3 .bento-item{
  padding:14px 10px; display:flex; flex-direction:column; align-items:center; gap:7px;
  text-align:center; position:relative;
}
/* strip pemisah PENDEK di tengah (bukan garis penuh atas-bawah) - kesannya
   pemisah data di dalam 1 panel, bukan garis pembatas antar "kotak" */
.bento-row-3 .bento-item::after{
  content:''; position:absolute; top:50%; right:0; transform:translateY(-50%);
  width:1px; height:28px; background:var(--border);
}
.bento-row-3 .bento-item:last-child::after{ display:none; }
/* Baris atas: icon (background rounded-rect, konsisten dengan
   .bento-wide-icon di baris "Harga Hari Ini" tepat di bawahnya) + angka
   sejajar dalam 1 baris. Keterangan/label TETAP di baris terpisah di
   bawahnya (bukan di samping icon). */
.bento-item-top{ display:flex; align-items:center; justify-content:center; gap:9px; }
.bento-item-icon-wrap{
  width:32px; height:32px; border-radius:var(--radius-sm); flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
}
.bento-item-icon-wrap svg{ width:16px; height:16px; }
.bento-item-icon-wrap.is-green{ background:var(--green-100); color:var(--green-700); }
.bento-item-icon-wrap.is-orange{ background:var(--orange-100); color:var(--orange-600); }
.bento-item-icon-wrap.is-blue{ background:var(--blue-100); color:var(--blue-600); }
.bento-item-icon-wrap.is-amber{ background:var(--amber-100); color:var(--amber-600); }
.bento-item-value{ font-family:var(--font-display); font-weight:800; font-size:1.15rem; color:var(--green-900-fg); line-height:1.1; }
.bento-item-unit{ font-size:.68rem; font-weight:600; color:var(--ink-muted); }
.bento-item-label{ font-size:.7rem; color:var(--ink-muted); font-weight:500; line-height:1.25; }

/* Baris lebar penuh di bawah bento-row-3 (mis. "Harga Hari Ini" admin) -
   SENGAJA beda orientasi (horizontal: ikon+label kiri, angka kanan) dari
   3 kolom di atasnya (vertikal) supaya transisinya kerasa jelas, dipisah
   garis PENUH (beda dari strip pendek di atas). */
.bento-row-wide{
  border-top:1px solid var(--border);
  padding:14px 16px; display:flex; align-items:center; gap:12px;
}
.bento-wide-icon{
  width:34px; height:34px; border-radius:var(--radius-sm); flex-shrink:0;
  background:var(--amber-100); color:var(--amber-600); display:flex; align-items:center; justify-content:center;
}
.bento-wide-icon svg{ width:17px; height:17px; }
.bento-wide-text{ display:flex; flex-direction:column; gap:1px; }
.bento-wide-label{ font-size:.74rem; color:var(--ink-muted); font-weight:500; }
.bento-wide-value{ font-family:var(--font-display); font-weight:800; font-size:1.28rem; color:var(--green-900-fg); line-height:1.1; }

/* Grafik bar mingguan - dipakai KHUSUS dashboard kurir (lihat
   partials/kurir-dashboard.php), nampilin total pengiriman SELESAI per
   hari (Senin-Minggu minggu berjalan). Sengaja custom CSS murni (bukan
   pakai lib chart eksternal semacam chart.js) - cuma 7 bar sederhana,
   konsisten dengan filosofi design system ini yang komponennya semua
   dibikin sendiri lewat CSS, bukan dependency baru buat 1 grafik kecil.
   Datanya dihitung ulang tiap kali partial ini di-render - bar HARI INI
   (dibedakan warnanya lewat .is-today) otomatis naik begitu kurir
   menyelesaikan pengiriman baru, ikut ter-refresh lewat polling AJAX
   #dashboard-live (lihat startAutoRefresh() di index.php/script.js)
   tanpa perlu reload manual. */
.week-chart{
  display:flex; align-items:flex-end; gap:8px;
  height:110px; padding:18px 16px 16px;
}
.week-chart-bar-wrap{
  flex:1; display:flex; flex-direction:column; align-items:center; justify-content:flex-end;
  gap:6px; height:100%;
}
.week-chart-value{ font-size:.7rem; font-weight:700; color:var(--ink-muted); }
.week-chart-bar{
  width:100%; max-width:26px; min-height:4px; border-radius:5px 5px 2px 2px;
  background:var(--green-100); transition:height .3s ease;
}
.week-chart-bar.is-today{ background:var(--orange-500); }
.week-chart-label{ font-size:.68rem; color:var(--ink-muted); font-weight:600; }
.week-chart-label.is-today{ color:var(--orange-600); }

/* Section heads */
.section-title{ font-family:var(--font-display); font-weight:700; font-size:1.02rem; color:var(--green-900-fg);}
.section-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; gap:10px; }
.see-all{ display:flex; align-items:center; gap:4px; font-size:.8rem; font-weight:600; color:var(--orange-600); white-space:nowrap; }
.see-all:hover{ text-decoration:underline; }
.menu-section .section-title{ margin-bottom:14px; }

/* Menu grid */
.menu-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:12px; }
.menu-grid.menu-grid-1{ grid-template-columns:1fr; }
.menu-card{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
  padding:16px 14px; display:flex; flex-direction:column; gap:10px;
  font-size:.83rem; font-weight:600; color:var(--ink); 
  transition:transform .15s ease, border-color .15s ease;
  position:relative;
}
.menu-card:hover{ transform:translateY(-2px); border-color:var(--green-100); }
.menu-icon{ width:36px; height:36px; border-radius:var(--radius-sm); background:var(--green-100); color:var(--green-700); display:flex; align-items:center; justify-content:center; }
.menu-icon svg{ width:18px; height:18px; }
.menu-card-badge{ position:absolute; top:12px; right:12px; background:var(--orange-500); color:#fff; font-size:.68rem; font-weight:700; padding:2px 7px; border-radius:var(--radius-full); }
.menu-card-desc{ font-size:.74rem; font-weight:500; color:var(--ink-muted); margin-top:-6px; }

/* Varian LEBAR PENUH dari .menu-card - dipakai shortcut "Saldo Komisi" di
   dashboard kurir (lihat partials/kurir-dashboard.php), ditaruh SETELAH
   kartu-kartu menu biasa supaya .menu-grid yang tadinya cuma 2 kotak rata
   kelihatan kayak "bento" (ukuran variatif), bukan grid rata biasa.
   SEKARANG <a> biasa (persis seperti .menu-card lain, tinggal klik kartunya
   langsung menuju halaman Komisi & Pencairan di navbar) - dulu sengaja
   <div> karena ada tombol "Ajukan Pencairan" nested di dalamnya (2 elemen
   interaktif bersarang, anchor di dalam anchor, itu HTML tidak valid), tapi
   tombol itu sudah dihapus dari kartu ini (pengajuan pencairan tetap bisa
   dilakukan dari dalam halaman Komisi & Pencairan itu sendiri). */
.menu-card-wide{
  grid-column:1 / -1;
  flex-direction:row; align-items:center; justify-content:space-between; gap:12px;
}
.menu-card-wide-info{ display:flex; align-items:center; gap:12px; min-width:0; }
.menu-card-wide-text{ display:flex; flex-direction:column; gap:1px; min-width:0; }
.menu-card-wide-label{ font-size:.74rem; color:var(--ink-muted); font-weight:500; }
.menu-card-wide-value{ font-family:var(--font-display); font-weight:800; font-size:1.05rem; color:var(--green-900-fg); }
.menu-card-wide-chevron{ flex-shrink:0; color:var(--ink-muted); }

/* Orders / list cards */
.orders-list{ display:grid; grid-template-columns:1fr; gap:12px; }
.order-card{
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
  padding:16px; display:flex; flex-direction:column; gap:8px;
}
.order-card.is-urgent{ border-color:#F0C7B5; }
.order-top{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.order-qty{ font-family:var(--font-display); font-weight:800; font-size:1.02rem; color:var(--green-900-fg); }
.status-badge{ font-size:.7rem; font-weight:700; padding:4px 10px; border-radius:var(--radius-full); white-space:nowrap; }
.status-waiting{ background:var(--orange-100); color:var(--orange-600); }
.status-sent{ background:var(--blue-100); color:var(--blue-600); }
.status-done{ background:var(--green-100); color:var(--green-700); }
.status-cancel{ background:var(--red-100); color:var(--red-600); }

.order-name{ font-weight:700; font-size:.9rem; color:var(--ink); }
.order-address{ display:flex; align-items:flex-start; gap:6px; font-size:.79rem; color:var(--ink-muted); line-height:1.4; }
.order-address svg{ margin-top:2px; color:var(--ink-soft); flex-shrink:0; }
.order-urgent-note{ font-size:.77rem; font-weight:600; color:var(--red-600); }

.order-bottom{ display:flex; align-items:center; justify-content:space-between; margin-top:4px; padding-top:10px; border-top:1px dashed var(--border); }
.order-time{ display:flex; align-items:center; gap:5px; font-size:.77rem; color:var(--ink-soft); font-weight:500; }
.detail-btn{
  display:flex; align-items:center; gap:4px; font-size:.79rem; font-weight:700;
  color:var(--green-900-fg); padding:6px 12px; border-radius:var(--radius-full);
  background:var(--green-100); transition:background .15s ease;
}
.detail-btn:hover{ background:#D3E2D8; }
.rute-btn{
  display:flex; align-items:center; gap:4px; font-size:.79rem; font-weight:700;
  color:var(--blue-600); padding:6px 12px; border-radius:var(--radius-full);
  background:var(--blue-100); transition:background .15s ease;
}
.rute-btn:hover{ background:#D6E2E8; }
.order-bottom-actions{ display:flex; align-items:center; gap:8px; }
/* Pill merah soft - dipakai KHUSUS tombol "Batalkan Pesanan" di card
   "Pengiriman Sedang Berlangsung" (dashboard kurir), bentuk & ukurannya
   sengaja disamakan persis dengan .rute-btn/.detail-btn, cuma warnanya beda
   (merah lembut) supaya kesannya aksi yang beda sifat (membatalkan). */
.batal-btn{
  display:flex; align-items:center; gap:4px; font-size:.79rem; font-weight:700;
  color:var(--red-600); padding:6px 12px; border-radius:var(--radius-full);
  background:var(--red-100); transition:background .15s ease;
  border:none; cursor:pointer; font-family:inherit;
}
.batal-btn:hover{ background:#F2D2C6; }

/* Card "Pengiriman Sedang Berlangsung" - dashboard kurir, peta di dalamnya
   statis/dikunci (lihat kurir-dashboard.php & script init-nya di index.php).
   Body-nya SENGAJA pakai class .order-card yang sama (.order-top,
   .status-badge, .order-bottom, .order-bottom-actions, .rute-btn,
   .detail-btn) biar konsisten - baik tampilan maupun tombolnya - dengan
   card pesanan di halaman lain, cuma peta di atasnya yang beda. */
.delivery-live-map-wrap{ padding:12px 14px 0; }
.delivery-live-map{
  width:100%; aspect-ratio:16/9; background:var(--surface-alt);
  position:relative; overflow:hidden; border-radius:var(--radius-md);
}
.delivery-live-map-empty{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  text-align:center; padding:16px; font-size:.78rem; color:var(--ink-muted);
}
.delivery-live-body{ padding:12px 16px 16px; display:flex; flex-direction:column; gap:8px; }


/* Generic list card (rows) */
.list-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); overflow:hidden; }
.list-row{ display:flex; align-items:center; justify-content:space-between; gap:12px; padding:14px 16px; }
.list-row + .list-row{ border-top:1px solid var(--border); }
.list-row-label{ font-size:.83rem; color:var(--ink-muted); font-weight:500; }
.list-row-value{ font-size:.87rem; color:var(--ink); font-weight:700; text-align:right; }
.list-row.nav-item{ cursor:pointer; }
.list-row.nav-item:hover{ background:var(--surface-alt); }

/* Target progress mini card (affiliator) */
.target-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); padding:16px; display:flex; flex-direction:column; gap:10px; }
.target-card-top{ display:flex; align-items:center; justify-content:space-between; gap:10px; }
.target-card-name{ font-weight:700; font-size:.87rem; color:var(--ink); }
.target-card-track{ height:8px; border-radius:var(--radius-full); background:var(--surface-alt); overflow:hidden; }
.target-card-fill{ height:100%; background:linear-gradient(90deg,var(--orange-500),#F2A94E); border-radius:var(--radius-full); transition:width .8s ease; }
.target-card-fill.is-done{ background:linear-gradient(90deg,var(--green-700),var(--green-900)); }
.target-card-bottom{ display:flex; align-items:center; justify-content:space-between; font-size:.79rem; color:var(--ink-muted); }
.target-card-reward{ font-size:.78rem; color:var(--amber-600); font-weight:600; }
.target-card-reward.is-done{ color:var(--green-700); }

/* Referral */
.referral-card{ display:flex; align-items:center; gap:10px; background:var(--surface); border:1px dashed var(--border); border-radius:var(--radius-md); padding:12px 14px; }
.referral-link-box{ flex:1; font-size:.79rem; color:var(--ink-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Preview Promo Affiliator (dashboard affiliator + admin) */
.promo-preview-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); padding:16px; display:flex; flex-direction:column; gap:14px; }
.promo-preview-image{ width:100%; max-width:100%; display:block; aspect-ratio:1/1; object-fit:cover; background:var(--green-100); border-radius:var(--radius-sm); }
.promo-preview-text{ font-size:.85rem; line-height:1.65; color:var(--ink); white-space:pre-wrap; word-break:break-word; }
.promo-preview-text strong{ font-weight:700; }
.promo-preview-text em{ font-style:italic; }
.promo-preview-text s{ text-decoration:line-through; }
.promo-preview-text code.wa-mono{ font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background:var(--surface-alt); padding:1px 4px; border-radius:4px; font-size:.92em; }
@keyframes promoHighlightPulse{
  0%, 100% { box-shadow:0 0 0 0 rgba(0,0,0,0); }
  25%, 75% { box-shadow:0 0 0 4px var(--orange-500); }
}
.promo-highlight{ border-radius:var(--radius-md); animation:promoHighlightPulse 1.1s ease-in-out 2; }

/* Price preview (tambah pesanan) */
.price-preview{ display:flex; align-items:center; justify-content:space-between; background:var(--green-100); border-radius:var(--radius-md); padding:14px 16px; gap:10px; }
.price-preview-label{ font-size:.79rem; color:var(--green-900-fg); font-weight:500; }
.price-preview-total{ font-family:var(--font-display); font-weight:800; font-size:1.15rem; color:var(--green-900-fg); }

/* Notification page list */
.notif-page-item{ display:flex; gap:12px; align-items:flex-start; padding:14px 16px; }
.notif-page-item + .notif-page-item{ border-top:1px solid var(--border); }
.notif-page-item:hover{ background:var(--surface-alt); }
.notif-page-item.is-unread{ background:var(--orange-100); }
.notif-page-icon{ font-size:1.3rem; flex-shrink:0; width:36px; height:36px; border-radius:50%; background:var(--surface); display:flex; align-items:center; justify-content:center; }
.notif-page-body{ flex:1; min-width:0; }
.notif-page-title{ font-size:.86rem; font-weight:700; color:var(--ink); display:flex; align-items:center; gap:6px; }
.notif-page-desc{ font-size:.8rem; color:var(--ink-muted); margin-top:2px; }
.notif-page-time{ font-size:.72rem; color:var(--ink-soft); margin-top:4px; }
.push-banner{ background:var(--green-100); border-radius:var(--radius-md); padding:16px; display:flex; flex-direction:column; gap:10px; font-size:.82rem; color:var(--green-900-fg); font-weight:500; }

/* Login page — versi baru terinspirasi referensi "Clover Club":
   hero solid var(--green-900) di atas (BUKAN gradasi warna), full-bleed
   sampai tepi layar, tinggi ~65% viewport. Card putih nempel di
   bawahnya TANPA border-radius sama sekali, jadi transisi hijau ke
   putihnya garis lurus tegas (bukan lengkung). Full-bleed juga (nggak
   ada max-width/gutter di kiri-kanan). Semua warna dari variabel di
   :root, tanpa box-shadow di mana pun. */
.login-page{
  min-height:100vh; min-height:100dvh;
  background:var(--green-900);
  overflow:hidden; /* jaga2 pas animasi transisi keluar halaman */
}
.login-page-inner{
  width:100%;
  min-height:100vh; min-height:100dvh;
  display:flex; flex-direction:column;
}

/* ---- Hero atas: solid, tanpa gradasi, ~65% tinggi layar ---- */
.login-hero{
  position:relative; flex:0 0 auto;
  min-height:65vh; min-height:65dvh;
  background:var(--green-900);
  padding:max(28px, calc(env(safe-area-inset-top) + 20px)) 26px 40px;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  transition:min-height .6s cubic-bezier(.61,.02,.28,1);
}
/* Watermark petal/ring/dot tetap dikunci di dalam hero */
.login-shapes{ position:absolute; inset:0; overflow:hidden; pointer-events:none; z-index:0; }
/* Cabang daun SENGAJA tidak diclip - bagian yang lewat batas bawah hero
   akan otomatis ketutup solid oleh .login-card (z-index lebih tinggi),
   jadi kelihatan seperti "mengalir" masuk ke tepi atas card. */
.login-leaves{ position:absolute; inset:0; overflow:visible; pointer-events:none; z-index:0; }
.lshape{ position:absolute; display:block; }

/* Watermark 4 kelopak ala logo clover, samar di pojok kanan-atas hero */
.petal{ width:120px; height:120px; border-radius:50%; background:var(--green-700); opacity:.35; }
.petal-a{ top:-52px; right:26px; }
.petal-b{ top:-52px; right:-30px; }
.petal-c{ top:4px; right:26px; }
.petal-d{ top:4px; right:-30px; }
.ring{ width:190px; height:190px; border-radius:50%; border:1px solid var(--green-100); opacity:.14; }
.ring-a{ top:-40px; right:-40px; }
.ring-b{ width:130px; height:130px; bottom:-56px; left:-46px; opacity:.12; }
.dot{ border-radius:50%; background:var(--orange-500); }
.dot-a{ width:9px; height:9px; opacity:.85; top:22px; left:30px; }
.dot-b{ width:5px; height:5px; opacity:.6; top:64px; left:54px; }
/* Dot tambahan tersebar di area tengah hero yang tadinya kosong,
   sekarang hero-nya lebih tinggi (65%) jadi butuh lebih banyak tekstur. */
.dot-c{ width:6px; height:6px; opacity:.5; top:42%; right:17%; background:var(--green-100); }
.dot-d{ width:10px; height:10px; opacity:.5; top:52%; left:13%; background:var(--orange-500); }
.dot-e{ width:4px; height:4px; opacity:.7; top:60%; right:28%; background:var(--surface); }
.diamond{ width:13px; height:13px; border-radius:3px; background:var(--orange-500); transform:rotate(45deg); }
.diamond-a{ top:34%; left:22%; opacity:.3; }
.diamond-b{ width:9px; height:9px; top:47%; right:10%; opacity:.35; background:var(--green-100); transform:rotate(45deg); }

/* Cabang daun mengalir dari hero turun ke tepi atas card - dijauhkan
   dikit dari sudut kiri/kanan supaya radius card tetap kelihatan jelas */
.leaf{ background:var(--green-700); border-radius:0 60% 0 60%; }
.leaf-1{ width:64px; height:33px; left:34px; bottom:-16px; transform:rotate(24deg); opacity:.9; }
.leaf-2{ width:48px; height:25px; left:66px; bottom:2px; transform:rotate(-12deg); opacity:.75; background:var(--green-100); }
.leaf-3{ width:40px; height:21px; left:100px; bottom:-10px; transform:rotate(46deg); opacity:.8; }
.leaf-4{ width:30px; height:16px; left:130px; bottom:8px; transform:rotate(-6deg); opacity:.6; background:var(--green-100); }
.leaf-5{ width:54px; height:28px; right:36px; bottom:-12px; transform:rotate(-30deg); opacity:.85; }
.leaf-6{ width:34px; height:18px; right:76px; bottom:4px; transform:rotate(18deg); opacity:.65; background:var(--green-100); }

.login-brand{ position:relative; z-index:1; display:flex; flex-direction:column; align-items:center; gap:6px; }
.login-logo{ width:64px; height:64px; border-radius:50%; background:var(--surface); border:2px solid var(--green-700); display:flex; align-items:center; justify-content:center; margin-bottom:8px; overflow:hidden; }
.login-logo img{ width:100%; height:100%; object-fit:cover; }
.login-brand-title{ font-family:var(--font-display); font-weight:800; font-size:1.3rem; color:#fff; letter-spacing:.02em; }
.login-brand-subtitle{ font-size:.82rem; color:var(--green-100); text-align:center; }

/* ---- Card putih, menimpa bagian bawah hero, full-bleed ----
   Sengaja TANPA border-radius sama sekali (garis lurus tegas antara
   hijau hero & putih card, bukan lengkung). */
.login-card{
  position:relative; z-index:1;
  flex:1; display:flex; flex-direction:column;
  background:var(--surface);
  border-radius:0;
  margin-top:-28px;
  padding:32px 26px max(20px, calc(env(safe-area-inset-bottom) + 14px));
  transition:transform .6s cubic-bezier(.61,.02,.28,1), opacity .5s ease;
}
.login-form{ display:flex; flex-direction:column; }
.login-password-wrap{ position:relative; border-radius:var(--radius-sm); }
.login-password-wrap .form-input{ padding-right:44px; }
@keyframes loginPassFlash{ 0%{ border-color:var(--green-700); } 100%{ border-color:var(--border); } }
.login-password-wrap.is-flashing .form-input{ animation:loginPassFlash .5s ease; }

.login-toggle-password{
  position:absolute; right:6px; top:50%; transform:translateY(-50%);
  width:32px; height:32px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  color:var(--ink-soft); background:transparent;
  transition:background .15s ease, color .15s ease;
}
.login-toggle-password:hover{ background:var(--surface-alt); color:var(--ink-muted); }
.login-toggle-password:active{ transform:translateY(-50%) scale(.88); }
.login-toggle-password.is-active{ color:var(--green-700); }
.eye-icon-stack{ position:relative; width:19px; height:19px; }
.eye-icon{ position:absolute; inset:0; width:19px; height:19px; transition:opacity .22s ease, transform .32s cubic-bezier(.34,1.56,.64,1); }
.eye-icon-open{ opacity:1; transform:scale(1) rotate(0deg); }
.eye-icon-closed{ opacity:0; transform:scale(.4) rotate(-30deg); }
.login-toggle-password.is-active .eye-icon-open{ opacity:0; transform:scale(.4) rotate(30deg); }
.login-toggle-password.is-active .eye-icon-closed{ opacity:1; transform:scale(1) rotate(0deg); }

.login-remember{ display:flex; align-items:center; gap:10px; font-size:.8rem; color:var(--ink-muted); margin:14px 0 22px; cursor:pointer; }
/* Checkbox custom: input asli disembunyikan visual tapi tetap ada di DOM
   (accessible, tetap kekirim di form POST), bentuknya dibikin bulat
   dan keisi hijau brand pas dicentang. */
.login-remember-input{ position:absolute; width:1px; height:1px; opacity:0; pointer-events:none; }
.login-remember-box{
  flex-shrink:0; width:21px; height:21px; border-radius:50%;
  border:2px solid var(--border); background:var(--surface);
  display:flex; align-items:center; justify-content:center;
  transition:background .18s ease, border-color .18s ease, transform .18s cubic-bezier(.34,1.56,.64,1);
}
.login-remember-check{ width:12px; height:12px; stroke:#fff; fill:none; stroke-width:3; stroke-linecap:round; stroke-linejoin:round; stroke-dasharray:22; stroke-dashoffset:22; transition:stroke-dashoffset .25s ease .05s; }
.login-remember-input:checked + .login-remember-box{ background:var(--green-700); border-color:var(--green-700); transform:scale(1.06); }
.login-remember-input:checked + .login-remember-box .login-remember-check{ stroke-dashoffset:0; }
.login-remember-input:focus-visible + .login-remember-box{ outline:2px solid var(--green-700); outline-offset:2px; }
.login-remember-text{ user-select:none; }
/* Tombol pakai hijau brand (BUKAN oren) - radius disamakan skalanya
   sama radius atas card (32px) supaya seirama, bukan pill var(--radius-full)
   kayak tombol lain di app. */
.login-submit.btn-primary{ background:var(--green-700); border-radius:16px; }
.login-submit.btn-primary:hover{ background:var(--green-900); }
.login-submit:disabled{ opacity:.75; }
.login-footer{ margin-top:auto; padding-top:24px; font-size:.74rem; color:var(--ink-soft); text-align:center; transition:opacity .25s ease; }

/* ---- Transisi keluar dari login menuju beranda ----
   Dipicu lewat class .is-leaving di .login-page-inner (lihat script di
   login.php): card putih meluncur turun keluar layar, hero hijau
   melebar sampai 100% tinggi layar, judul/subjudul & footer memudar
   supaya yang tersisa cuma logo di tengah layar penuh hijau, dibarengi
   loading bar kecil yang muncul di bawah layar. */
.login-page-inner.is-leaving .login-hero{ min-height:100vh; min-height:100dvh; }
.login-page-inner.is-leaving .login-card{ transform:translateY(115%); opacity:0; }
.login-page-inner.is-leaving .login-brand-title,
.login-page-inner.is-leaving .login-brand-subtitle{ opacity:0; transition:opacity .3s ease; }
.login-page-inner.is-leaving .login-logo{ transform:scale(1.12); transition:transform .6s cubic-bezier(.34,1.56,.64,1); }
.login-page-inner.is-leaving .login-shapes,
.login-page-inner.is-leaving .login-leaves{ opacity:.5; transition:opacity .6s ease; }

.login-transition-loading{
  position:fixed; left:0; right:0;
  bottom:max(28px, calc(env(safe-area-inset-bottom) + 22px));
  display:flex; justify-content:center;
  opacity:0; pointer-events:none; z-index:5;
  transition:opacity .3s ease .25s;
}
.login-page-inner.is-leaving ~ .login-transition-loading{ opacity:1; }
.login-transition-loading-track{
  width:96px; height:4px; border-radius:var(--radius-full);
  background:rgba(255,255,255,.25); overflow:hidden; position:relative;
}
.login-transition-loading-bar{
  position:absolute; top:0; bottom:0; width:40%; border-radius:var(--radius-full);
  background:var(--surface);
  animation:loginLoadingSlide 1s ease-in-out infinite;
}
@keyframes loginLoadingSlide{
  0%{ left:-40%; }
  100%{ left:100%; }
}

/* Details/summary toggle (saldo adjust, etc.) */
details.details-card{ margin-bottom:16px; }
details.details-card summary{ cursor:pointer; font-size:.82rem; font-weight:700; color:var(--green-700); padding:10px 14px; background:var(--green-100); border-radius:var(--radius-sm); list-style:none; }
details.details-card summary::-webkit-details-marker{ display:none; }
details.details-card summary::before{ content:'+ '; }
details.details-card[open] summary::before{ content:'− '; }

/* Checkbox chip grid (wilayah assignment) */
.checkbox-grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; margin:8px 0 16px; }
.checkbox-chip{ display:flex; align-items:center; gap:8px; padding:10px 12px; border-radius:var(--radius-sm); border:1px solid var(--border); font-size:.8rem; font-weight:500; color:var(--ink); cursor:pointer; }
.checkbox-chip input{ accent-color:var(--green-700); width:16px; height:16px; flex-shrink:0; }
.checkbox-chip:has(input:checked){ background:var(--green-100); border-color:var(--green-700); color:var(--green-900-fg); font-weight:700; }

/* Empty state */
.empty-state{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:10px; padding:44px 20px; background:var(--surface); border:1px dashed var(--border); border-radius:var(--radius-md); color:var(--ink-muted); }
.empty-state-icon{ width:52px; height:52px; border-radius:50%; background:var(--surface-alt); color:var(--ink-soft); display:flex; align-items:center; justify-content:center; }
.empty-state-title{ font-weight:700; color:var(--ink); font-size:.92rem; }
.empty-state-desc{ font-size:.82rem; max-width:320px; }

/* ============ BUTTONS ============ */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  padding:12px 20px; border-radius:var(--radius-full);
  font-weight:700; font-size:.87rem; font-family:var(--font-body);
  transition:background .15s ease, transform .1s ease, opacity .15s ease;
  border:1px solid transparent;
}
.btn:active{ transform:scale(.98); }
.btn-block{ width:100%; }
.btn-sm{ padding:8px 14px; font-size:.79rem; }
.btn-primary{ background:var(--orange-500); color:#fff; }
.btn-primary:hover{ background:var(--orange-600); }
.btn-secondary{ background:var(--green-900); color:#fff; }
.btn-secondary:hover{ background:#143324; }
.btn-outline{ background:var(--surface); color:var(--green-900-fg); border-color:var(--border); }
.btn-outline:hover{ background:var(--surface-alt); }
.btn-ghost{ background:transparent; color:var(--ink-muted); }
.btn-ghost:hover{ background:var(--surface-alt); color:var(--ink); }
.btn-danger{ background:var(--red-600); color:#fff; }
.btn-danger:hover{ background:#8f3117; }
.btn-danger-outline{ background:var(--surface); color:var(--red-600); border-color:#EBC7B9; }
.btn-danger-outline:hover{ background:var(--red-100); }
.btn:disabled{ opacity:.5; cursor:not-allowed; }
/* Versi "abu-abu" buat elemen BUKAN <button> (mis. <a>/<span>) yang perlu
   kelihatan nonaktif - native [disabled] cuma berlaku di <button>, jadi
   dipakai class ini + href/onclick-nya dihilangkan dari markup (bukan
   cuma div dikasih style, biar konsisten sama tampilan .btn:disabled di
   atas). Dipakai di shortcut "Ajukan Pencairan" dashboard kurir kalau
   saldo tersedia masih di bawah minimal pencairan Rp20.000. */
.btn.is-disabled{ opacity:.5; cursor:not-allowed; pointer-events:none; }
.btn-fab-wrap{ position:sticky; left:0; right:0; bottom:0; padding:0px 0px calc(14px + env(safe-area-inset-bottom)); background:linear-gradient(180deg, rgba(247,243,236,0), var(--bg) 30%); z-index:20; }


/* Tombol FAB bulat mengambang di pojok (dipakai "Tambah Pesanan" admin) */
.btn-fab-round{
  position:fixed; right:20px; bottom:calc(20px + env(safe-area-inset-bottom));
  width:56px; height:56px; border-radius:50%;
  background:var(--green-900); color:#fff;
  display:flex; align-items:center; justify-content:center;
  
  z-index:20; transition:transform .15s ease;
}
.btn-fab-round:hover{ transform:translateY(-2px); }
.btn-fab-round:active{ transform:scale(0.93); }
.btn-fab-round svg{ width:26px; height:26px; stroke-width:2.2; }

/* Tombol FAB pill (icon + label) mengambang di pojok kanan bawah - dipakai
   tombol "Peta" di kurir/perlu-dikirim.php buat buka halaman peta full-layar
   (kurir/peta-perlu-dikirim.php). Posisi bottom-nya SAMA seperti
   .btn-fab-round (mentok pojok, margin kecil) - halaman ini pakai
   $topbarMode='page', yang otomatis bikin bottom navbar dapat class
   "bottom-nav-hidden" (lihat includes/layout-bottom.php), jadi navbar-nya
   memang disembunyikan & tidak perlu jarak ekstra buat menghindarinya.*/
.btn-fab-label{
  position:fixed; right:20px; bottom:calc(20px + env(safe-area-inset-bottom));
  display:flex; align-items:center; gap:8px;
  padding:15px 24px 15px 20px; border-radius:var(--radius-full);
  background:var(--green-900); color:#fff;
  font-family:var(--font-display); font-weight:700; font-size:.98rem;
  box-shadow:0 2px 6px rgba(14,59,46,.14);
  z-index:20; transition:transform .15s ease;
}
.btn-fab-label:hover{ transform:translateY(-2px); }
.btn-fab-label:active{ transform:scale(.95); }
.btn-fab-label svg{ width:21px; height:21px; stroke-width:2.2; flex-shrink:0; }



/* Panel "swipe kanan-ke-kiri untuk buka Tambah Pesanan" di dashboard admin */
.swipe-tambah-overlay{
  position:fixed; inset:0; background:rgba(15,61,46,0);
  opacity:0; visibility:hidden; transition:opacity .25s ease, visibility .25s ease;
  z-index:58;
}
.swipe-tambah-overlay.is-visible{ opacity:1; visibility:visible; }
.swipe-tambah-panel{
  position:fixed; top:0; right:0; bottom:0; width:82%; max-width:340px;
  background:var(--green-900); color:#fff;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px;
  transform:translateX(100%); z-index:59;
  padding:24px;
}
.swipe-tambah-panel span{ font-size:1.05rem; font-weight:700; text-align:center; }
.swipe-tambah-hint{ font-size:.78rem !important; font-weight:500 !important; color:rgba(255,255,255,.65); }

/* ============ FORMS ============ */
.form-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); padding:18px; }
.form-group{ display:flex; flex-direction:column; gap:6px; margin-bottom:10px; }
.form-group:last-child{ margin-bottom:0; }
.form-row{ display:grid; grid-template-columns:1fr; gap:0px; }
.form-label{ font-size:.82rem; font-weight:700; color:var(--ink); }
.form-label .optional{ font-weight:500; color:var(--ink-soft); }
.form-input, .form-select, .form-textarea{
  width:100%; padding:12px 14px; border-radius:var(--radius-sm);
  border:1px solid var(--border); background:var(--surface);
  font-family:var(--font-body); font-size:.88rem; color:var(--ink);
  transition:border-color .15s ease;
}
.form-input:focus, .form-select:focus, .form-textarea:focus{
  border-color:var(--green-700); outline:none;
}
.form-textarea{ resize:vertical; min-height:100px; }
.form-hint{ font-size:.76rem; color:var(--ink-soft); }
.form-error{ font-size:.76rem; color:var(--red-600); font-weight:600; }
.input-prefix-wrap{ position:relative; display:flex; align-items:center; }
.input-prefix{ position:absolute; left:14px; font-size:.88rem; color:var(--ink-muted); font-weight:600; }
.input-prefix-wrap .form-input{ padding-left:34px; }
.upload-box{ display:flex; background-color: var(--surface); align-items:center; justify-content:center; gap:8px; padding:16px; border:1.5px solid var(--border); border-radius:var(--radius-full); font-size:.82rem; font-weight:600; color:var(--ink-muted); cursor:pointer; text-align:center; transition:border-color .15s ease, background .15s ease; }
.upload-box:hover{ border-color:var(--green-700); background-color:var(--green-100); }

.tab-group{ display:flex; gap:6px; background:var(--surface-alt); padding:4px; border-radius:var(--radius-full); }
.tab-btn{ flex:1; text-align:center; padding:9px 14px; border-radius:var(--radius-full); font-size:.82rem; font-weight:700; color:var(--ink-muted); }
.tab-btn.tab-active{ background:var(--surface); color:var(--green-900-fg); }

/* ============ TABLES ============ */
.table-wrap{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); overflow:hidden; }
.table-toolbar{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; padding:14px 16px; border-bottom:1px solid var(--border); }
.table-search{ position:relative; flex:1; min-width:180px; }
.table-search svg{ position:absolute; left:12px; top:50%; transform:translateY(-50%); color:var(--ink-soft); }
.table-search input{ width:100%; padding:9px 12px 9px 36px; border-radius:var(--radius-full); border:1px solid var(--border); font-size:.83rem; background:var(--bg); }
.table-scroll{ overflow-x:auto; }
.data-table{ width:100%; border-collapse:collapse; font-size:.84rem; }
.data-table th{ text-align:left; padding:11px 16px; background:var(--surface-alt); color:var(--ink-muted); font-weight:700; font-size:.74rem; text-transform:uppercase; letter-spacing:.03em; white-space:nowrap; }
.data-table td{ padding:13px 16px; border-top:1px solid var(--border); vertical-align:middle; }
.data-table tr:hover td{ background:var(--surface-alt); }
.data-table .cell-strong{ font-weight:700; color:var(--ink); }
.data-table .cell-muted{ color:var(--ink-muted); font-size:.8rem; }
.row-actions{ display:flex; gap:6px; justify-content:flex-end; }
.icon-action-btn{ width:32px; height:32px; display:flex; align-items:center; justify-content:center; border-radius:var(--radius-sm); color:var(--ink-muted); transition:background .15s ease, color .15s ease; }
.icon-action-btn:hover{ background:var(--surface-alt); color:var(--green-900-fg); }
.icon-action-btn.danger:hover{ background:var(--red-100); color:var(--red-600); }

/* Mobile "card" table (used under 720px for wide tables) */
@media (max-width:719px){
  .data-table.card-table thead{ display:none; }
  .data-table.card-table, .data-table.card-table tbody, .data-table.card-table tr, .data-table.card-table td{ display:block; width:100%; }
  .data-table.card-table tr{ padding:12px 16px; border-top:1px solid var(--border); }
  .data-table.card-table td{ padding:5px 0; border:none; display:flex; justify-content:space-between; gap:10px; text-align:right; }
  .data-table.card-table td::before{ content:attr(data-label); font-weight:700; color:var(--ink-muted); font-size:.74rem; text-align:left; }
}

.pagination{ display:flex; align-items:center; justify-content:center; gap:6px; padding:14px 16px; border-top:1px solid var(--border); }
.page-btn{ min-width:32px; height:32px; padding:0 8px; display:flex; align-items:center; justify-content:center; border-radius:var(--radius-sm); font-size:.82rem; font-weight:600; color:var(--ink-muted); }
.page-btn:hover{ background:var(--surface-alt); }
.page-btn.is-active{ background:var(--green-900); color:#fff; }
.page-btn.disabled{ opacity:.4; pointer-events:none; }

/* Filter chips */
.filter-bar{ display:flex; flex-wrap:wrap; gap:8px; }
.filter-chip{ padding:8px 14px; border-radius:var(--radius-full); background:var(--surface); border:1px solid var(--border); font-size:.8rem; font-weight:600; color:var(--ink-muted); }
.filter-chip.is-active{ background:var(--green-900); color:#fff; border-color:var(--green-900); }

/* Badges */
.badge{ display:inline-flex; align-items:center; padding:3px 9px; border-radius:var(--radius-full); font-size:.7rem; font-weight:700; }
.badge-green{ background:var(--green-100); color:var(--green-700); }
.badge-orange{ background:var(--orange-100); color:var(--orange-600); }
.badge-blue{ background:var(--blue-100); color:var(--blue-600); }
.badge-red{ background:var(--red-100); color:var(--red-600); }
.badge-gray{ background:var(--surface-alt); color:var(--ink-muted); }

/* Cards (settings / summary grids) */
.card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md); padding:18px; }
.card-grid{ display:grid; grid-template-columns:1fr; gap:14px; }

/* Pin kurir di peta "Pantau Kurir" (lihat admin/pantau-kurir.php) - lingkaran
   kecil berisi inisial nama kurir, warnanya beda antara online/offline. */
.kurir-pin{ width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-family:var(--font-display); font-weight:700; font-size:.78rem; color:#fff; border:2px solid #fff; box-shadow:0 1px 5px rgba(0,0,0,.35); }
.kurir-pin-online{ background:var(--green-700); }
.kurir-pin-offline{ background:var(--ink-soft); }

/* Pin di peta "Perlu Dikirim" milik kurir (lihat kurir/perlu-dikirim.php) -
   beda kategori beda warna: posisi kurir sendiri, pelanggan (pesanan biasa),
   pesanan urgent, dan pesanan yang sudah selesai dikirim hari ini (filter). */
.map-pin{ width:32px; height:32px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-family:var(--font-display); font-weight:700; font-size:.72rem; color:#fff; border:2px solid #fff; box-shadow:0 1px 5px rgba(0,0,0,.35); }
/* Kontrol Leaflet (tombol zoom dll) pakai z-index internal sampai 1000 -
   tanpa baris ini, itu bisa "nembus" ke atas sidebar/drawer (z-index:50)
   kalau sidebar sedang dibuka di mobile, karena position:relative Leaflet
   sendiri (tanpa z-index eksplisit) TIDAK membentuk stacking context baru.
   Kasih z-index eksplisit di sini supaya semua isi peta "terkurung" di
   dalam stacking context-nya sendiri, tidak bisa melebihi elemen lain di
   luar peta manapun z-index internalnya. */
.leaflet-container{ z-index:0; }

.map-pin svg{ width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.map-pin-kurir{ background:var(--blue-600); }
.map-pin-pelanggan{ background:var(--green-700); }
.map-pin-dikirim{ background:var(--blue-600); }
.map-pin-urgent{ background:var(--orange-600); }
.map-pin-bukti{ background:#8B5CF6; } /* ungu - beda dari pin tujuan (hijau) & kurir (biru), pin verifikasi lokasi foto bukti pengiriman */
.map-pin-selesai{ background:var(--ink-soft); } /* abu-abu - pesanan yang sudah kelar dikirim, ditampilkan lewat filter */
/* ============ FULLSCREEN MAP PAGE (rute.php) ============ */
/* Body class dipakai halaman yang mau petanya mengisi SELURUH layar ala
   aplikasi navigasi - topbar disembunyikan, tombol jadi lingkaran mengambang
   di atas peta, detail pesanan jadi bottom sheet yang bisa ditarik naik/turun. */
body.page-fullscreen-map{
  /* overflow:hidden di sini yang BENERAN mencegah scroll (bukan cuma
     menghindarinya) - beberapa browser mobile melaporkan 100vh sedikit
     lebih besar dari area yang benar-benar terlihat (address bar belum
     collapse saat render pertama), jadi tanpa overflow:hidden halaman
     masih bisa discroll beberapa px walau tinggi kontennya sudah pas-pasan
     dibuat 100vh. */
  overflow:hidden;
  /* Fallback berlapis: 100vh (browser lama) -> 100dvh (browser modern yg
     paham dynamic viewport) -> calc(var(--app-vh)*100) (nilai PALING akurat,
     dihitung sendiri lewat JS di includes/layout-top.php - dipakai di semua
     mesin termasuk WebView PWA lawas yg belum kenal dvh sama sekali). Baris
     paling bawah menang kalau didukung, browser yg tidak kenal properti
     tertentu otomatis skip baris itu & tetap pakai baris sebelumnya. */
  height:100vh;
  height:100dvh;
  height:calc(var(--app-vh, 1vh) * 100);
}
body.page-fullscreen-map .topbar{ display:none; }
body.page-fullscreen-map .content{ padding:0; max-width:none; gap:0; height:100%; }
body.page-fullscreen-map .app{ overflow:hidden; }
body.page-fullscreen-map .main{ height:100vh; overflow:hidden; }
body.page-fullscreen-map .main{ height:100dvh; }
body.page-fullscreen-map .main{ height:calc(var(--app-vh, 1vh) * 100); }

.fullscreen-map-wrap{ position:relative; width:100%; height:100%; overflow:hidden; }
.fullscreen-map-wrap .leaflet-container{ width:100%; height:100%; }
/* Tombol zoom +/- bawaan Leaflet sengaja disembunyikan di peta rute
   (rute.php pakai zoomControl:false saat init map, ini jaga-jaga saja
   kalau suatu saat opsi itu lupa di-set). */
.fullscreen-map-wrap .leaflet-control-zoom{ display:none; }

.map-float-btn{
  position:absolute; top:16px; left:16px; z-index:20;
  width:42px; height:42px; border-radius:50%;
  background:var(--surface); box-shadow:0 1px 4px rgba(0,0,0,.14);
  display:flex; align-items:center; justify-content:center;
  color:var(--green-900-fg);
}
.map-float-btn:active{ background:var(--surface-alt); }

/* Skeleton loading KHUSUS AREA PETA (dipakai rute.php &
   kurir/peta-perlu-dikirim.php) - sengaja dipisah dari .skeleton-overlay
   biasa (yang menutup SELURUH halaman) karena di sini tombol kembali,
   filter, dan bottom sheet harus TETAP kelihatan & bisa dipakai selagi
   petanya sendiri masih dimuat - lihat markup & catatan JS di kedua
   halaman itu soal kapan tepatnya skeleton ini disembunyikan.

   Bentuknya "blok kota" (bangunan/taman berbentuk kotak membulat, sedikit
   dirotasi biar organik) dihubungkan jalan-jalan LENGKUNG (bukan grid
   kotak-kotak lurus) + pin yang berdenyut dengan cincin menyebar ala
   "posisi kamu" di aplikasi peta navigasi sungguhan, plus sapuan shimmer
   diagonal yang lewat di atasnya biar kesannya "sedang memuat" bukan cuma
   ilustrasi statis. Disembunyikan cuma setelah data pesanan PERTAMA
   datang DAN tile peta yang lagi kelihatan di viewport selesai dirender
   (lihat cobaSembunyikanSkeleton() di JS kedua halaman itu). */
.map-skeleton{
  position:absolute; inset:0; z-index:15; /* di bawah tombol & bottom sheet (z-index:20), di atas peta asli (z-index:0) */
  background:linear-gradient(160deg, #EAF1EC 0%, #E2EBE4 55%, #E9EFEA 100%);
  overflow:hidden;
  opacity:1; visibility:visible;
  transition:opacity .35s ease, visibility .35s ease;
}
.map-skeleton.is-hidden{ opacity:0; visibility:hidden; pointer-events:none; }
.map-skeleton svg{ display:block; width:100%; height:100%; position:relative; z-index:1; }
.map-skeleton-block{ fill:#FFFFFF; opacity:.55; }
.map-skeleton-park{ fill:#D9E8DD; opacity:.75; }
.map-skeleton-road{ fill:none; stroke:#CBD9CF; stroke-linecap:round; }
.map-skeleton-road-main{
  stroke-width:5;
  stroke-dasharray:2 16;
  animation:map-skeleton-dash 2.2s linear infinite;
}
.map-skeleton-road-minor{
  stroke-width:2.5;
  stroke-dasharray:1.5 12;
  opacity:.7;
  animation:map-skeleton-dash 2.8s linear infinite reverse;
}
.map-skeleton-pin-group{ transform-box:fill-box; transform-origin:center; }
.map-skeleton-pin{ fill:var(--green-700); transform-box:fill-box; transform-origin:center; animation:map-skeleton-pulse 1.8s ease-in-out infinite; }
.map-skeleton-pin-ring{
  fill:none; stroke:var(--green-700); stroke-width:2.5;
  transform-box:fill-box; transform-origin:center;
  animation:map-skeleton-ring 1.8s cubic-bezier(.2,.7,.3,1) infinite;
}
/* Sapuan shimmer diagonal - elemen terpisah DI ATAS svg (bukan bagian dari
   svg itu sendiri) supaya gradiennya gampang dianimasikan lewat
   background-position tanpa perlu masking SVG yang ribet. */
.map-skeleton-shimmer{
  position:absolute; inset:0; z-index:2; pointer-events:none;
  background:linear-gradient(110deg, transparent 35%, rgba(255,255,255,.65) 50%, transparent 65%);
  background-size:250% 100%;
  animation:map-skeleton-shimmer 2s ease-in-out infinite;
}
@keyframes map-skeleton-dash{ to{ stroke-dashoffset:-72; } }
@keyframes map-skeleton-pulse{ 0%,100%{ opacity:.6; transform:scale(1); } 50%{ opacity:1; transform:scale(1.2); } }
@keyframes map-skeleton-ring{ 0%{ opacity:.55; transform:scale(.7); } 100%{ opacity:0; transform:scale(2.6); } }
@keyframes map-skeleton-shimmer{ 0%{ background-position:180% 0; } 100%{ background-position:-80% 0; } }

/* Dropdown filter "Perlu Dikirim / Sedang Dikirim / Urgent Saja / Sudah
   Dikirim Hari Ini" - mengambang di pojok kanan atas peta (lihat
   kurir/peta-perlu-dikirim.php), posisinya sejajar dengan .map-float-btn
   (tombol kembali) yang ada di pojok kiri atas. Custom (bukan <select>
   bawaan browser) supaya tampilan menu-nya konsisten dengan dropdown lain
   di app ini (lihat .dropdown milik profile chip topbar). */
.map-filter-dropdown{ position:absolute; top:16px; right:16px; z-index:20; }
.map-filter-btn{
  display:flex; align-items:center; gap:6px;
  height:42px; padding:0 14px; border:none; border-radius:var(--radius-full);
  background:var(--surface); box-shadow:0 1px 4px rgba(0,0,0,.14);
  font-family:var(--font-body); font-weight:600; font-size:.8rem; color:var(--green-900-fg);
}
.map-filter-btn svg{ width:15px; height:15px; color:var(--ink-muted); flex-shrink:0; transition:transform .18s ease; }
.map-filter-dropdown.is-open .map-filter-btn svg{ transform:rotate(180deg); }
.map-filter-menu{
  position:absolute; top:calc(100% + 8px); right:0; min-width:200px; margin:0;
  list-style:none; padding:6px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-md);
  box-shadow:0 2px 10px rgba(0,0,0,.12);
  opacity:0; visibility:hidden; transform:translateY(-6px);
  transition:opacity .16s ease, transform .16s ease, visibility .16s;
}
.map-filter-dropdown.is-open .map-filter-menu{ opacity:1; visibility:visible; transform:translateY(0); }
.map-filter-option{ padding:9px 10px; border-radius:var(--radius-sm); font-size:.83rem; font-weight:500; color:var(--ink); cursor:pointer; white-space:nowrap; }
.map-filter-option:hover{ background:var(--surface-alt); }
.map-filter-option.is-active{ color:var(--green-700); font-weight:700; background:var(--surface-alt); }

.bottom-sheet{
  position:absolute; left:0; right:0; bottom:0; z-index:20;
  background:var(--surface); border-radius:20px 20px 0 0;
  box-shadow:0 -2px 10px rgba(0,0,0,.1);
  display:flex; flex-direction:column;
  max-height:78vh;
  max-height:calc(var(--app-vh, 1vh) * 78);
}
/* Bug fix: kurva bouncy punya terapkanPosisi() (rute.php &
   kurir/peta-perlu-dikirim.php) pakai cubic-bezier(.34,1.56,.64,1) yg
   sengaja "mantul" - artinya translateY sempat MELEWATI 0 (jadi negatif)
   sesaat pas sheet-nya dibuka penuh, sebelum balik pas ke posisi akhir.
   Karena sheet posisinya absolute+bottom:0, translateY negatif itu
   ngangkat SELURUH kotak sheet ke atas termasuk tepi bawahnya, jadi ada
   celah/"bolong" sekilas di bawah sheet yang nampakin peta di baliknya.
   Fix: elemen ::after ini nambahin lapisan warna solid yg nongol 60px di
   BAWAH tepi sheet (posisinya absolute, sengaja TIDAK ikut ngaruh ke
   sheet.offsetHeight yg dipakai JS buat ngitung posisi "peek" - beda sama
   kalau nambah padding/height beneran ke sheet-nya) - jadi pas overshoot
   ngangkat sheet ke atas, yang kelihatan di balik celahnya tetap warna
   solid ini, bukan peta. */
.bottom-sheet::after{
  content:''; position:absolute; left:0; right:0; top:100%; height:60px;
  background:var(--surface);
}
.bottom-sheet-handle-row{ flex-shrink:0; padding:16px 0 18px; cursor:grab; touch-action:none; }
.bottom-sheet-handle{ width:44px; height:5px; border-radius:3px; background:var(--border); margin:0 auto; }
.bottom-sheet-peek{ padding:8px 20px 16px; flex-shrink:0; cursor:grab; touch-action:none; }
.bottom-sheet-body{ padding:0 20px 20px; overflow-y:auto; overscroll-behavior:contain; }
/* Fallback murni CSS (dipakai kurir/peta-perlu-dikirim.php) - sheet-nya
   belum ada pin aktif sama sekali sampai JS sempat mengukur & mengganti
   transform-nya secara presisi (lihat terapkanPosisi() di skrip halaman
   itu). Begitu class .is-active ditambahkan JS, aturan ini otomatis kalah
   sama inline style transform yang di-set JS. */
.bottom-sheet:not(.is-active){ transform:translateY(100%); }

/* 2 tombol sejajar di bottom sheet kurir/peta-perlu-dikirim.php (Detail
   Pesanan + Kirim Sekarang/Selesaikan Pesanan) - label "Selesaikan Pesanan"
   lumayan panjang, jadi padding kiri-kanan tombolnya sengaja dibikin ikut
   menyempit (clamp, bukan 20px tetap seperti .btn biasa) mengikuti lebar
   layar supaya dua tombol tetap muat satu baris tanpa teksnya "bertumpuk"
   (wrap ke 2 baris) di kebanyakan ukuran HP. white-space SENGAJA tidak
   dipaksa nowrap - begitu layar sangat sempit & padding sudah di titik
   paling minim (8px) pun tetap tidak cukup, teksnya baru boleh wrap ke 2
   baris (bukan overflow/kepotong). */
#sheetActions .btn{
  padding-left:clamp(8px, 3vw, 20px);
  padding-right:clamp(8px, 3vw, 20px);
  white-space:normal;
  line-height:1.15;
}

/* ============ MODAL / OVERLAY ============ */
.modal-overlay{
  position:fixed; inset:0; background:rgba(27,52,48,.55); z-index:80;
  display:flex; align-items:center; justify-content:center; padding:20px;
  opacity:0; pointer-events:none; transition:opacity .2s ease;
}
.modal-overlay.active{ opacity:1; pointer-events:auto; }
.modal-box{ background:var(--surface); border-radius:var(--radius-lg); max-width:420px; width:100%; padding:24px; max-height:86vh; overflow-y:auto; overscroll-behavior:contain; }
.modal-box img{ border-radius:var(--radius-sm); }
/* Baris 2 tombol aksi di modal konfirmasi (mis. "Tidak" / "Ya, Batalkan").
   min-width:0 WAJIB di sini - tanpa ini, flex:1 saja TIDAK cukup bikin dua
   tombol sama lebar kalau teksnya beda panjang ("Tidak" vs "Ya, Batalkan"):
   default min-width flex item adalah "auto" (= lebar min-content teksnya
   sendiri), jadi tombol dengan teks lebih panjang "memaksa" ambil jatah
   lebih lebar duluan sebelum sisanya dibagi rata, dan tombol yang teksnya
   lebih pendek jadi kepepet lebih sempit. min-width:0 mematikan itu supaya
   flex-basis:0 dari flex:1 beneran dipatuhi 50/50, siapa pun isi teksnya.
   Berlaku juga untuk anak berupa <form> (submit-nya dibungkus form biar
   POST langsung) - form ikut jadi flex item biasa, aturan sama berlaku. */
.modal-confirm-actions{ display:flex; gap:8px; width:100%; margin-top:4px; }
.modal-confirm-actions > *{ flex:1; min-width:0; }
/* Kunci scroll & klik ke konten di belakang modal/overlay selagi aktif -
   lihat kuncilOverlay()/bukaKuncilOverlay() di script.js yang menambah/
   menghapus class ini di <body> otomatis tiap ada .modal-overlay atau
   .image-modal-overlay berstatus .active. position:fixed (bukan cuma
   overflow:hidden) sengaja dipakai supaya background BENAR-BENAR tidak
   bisa di-scroll sekalipun di iOS Safari (overflow:hidden saja masih bisa
   "ditembus" scroll/rubber-band di sana) - posisi scroll semula disimpan &
   dikembalikan lewat JS pas overlay ditutup. */
body.body-scroll-locked{ position:fixed; left:0; right:0; width:100%; overflow:hidden; }
html.body-scroll-locked{ overflow:hidden; height:100%; }
.modal-close-x{ position:absolute; top:16px; right:16px; width:34px; height:34px; display:flex; align-items:center; justify-content:center; border-radius:50%; background:rgba(0,0,0,.3); color:#fff; }
.image-modal-overlay{ position:fixed; inset:0; background:rgba(15,25,22,.85); z-index:90; display:flex; align-items:center; justify-content:center; padding:24px; opacity:0; pointer-events:none; transition:opacity .2s ease; }
.image-modal-overlay.active{ opacity:1; pointer-events:auto; }
.image-modal-overlay img{ max-height:82vh; border-radius:var(--radius-md); }

/* Toast (success confirmations) */
/* Toast "tekan sekali lagi utk keluar" - posisinya SENGAJA nempel tepat
   di atas bottom navbar (bukan di atas layar), dan z-index-nya LEBIH
   RENDAH dari .bottom-nav (45) saat tersembunyi supaya dia "start" dari
   belakang navbar (ketutup), lalu begitu .active z-index-nya dinaikkan
   MELEBIHI navbar sehingga kesannya muncul menyembul dari belakang navbar
   ke depan/atasnya. Background semi-transparan polos (SENGAJA tanpa
   backdrop-filter - blur itu mahal buat GPU/baterai di HP low-end, dan
   tanpa box-shadow biar tetap ringan) - cukup rgba solid, ringan tapi
   tetap kerasa "mengambang". white-space:nowrap + width:max-content
   supaya teks selalu 1 baris rapi (sebelumnya suka "numpuk" 2 baris kalau
   lebar pill pas-pasan). */
.toast{
  position:fixed; left:50%;
  bottom:calc(68px + env(safe-area-inset-bottom) + 14px);
  transform:translateX(-50%) translateY(10px) scale(.94);
  width:max-content; max-width:calc(100% - 56px);
  background:rgba(15,40,28,.82);
  color:#fff; padding:13px 22px; border-radius:var(--radius-full);
  font-size:.82rem; font-weight:600; letter-spacing:.01em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  z-index:40; opacity:0; pointer-events:none;
  transition:opacity .32s cubic-bezier(.32,.8,.4,1), transform .38s cubic-bezier(.32,.8,.4,1), z-index 0s .32s;
}
.toast.active{
  opacity:1; transform:translateX(-50%) translateY(0) scale(1);
  z-index:46; /* melompat ke depan bottom-nav (45) begitu tampil */
  transition:opacity .3s cubic-bezier(.32,.8,.4,1), transform .38s cubic-bezier(.32,.8,.4,1);
  animation:toastShake .42s cubic-bezier(.36,.07,.19,.97) .38s;
}
/* Getar halus kecil, sekali saja, SETELAH toast selesai naik ke posisi
   (delay .38s -> nunggu transisi translateY di atas kelar dulu) - biar
   kesannya "nyentak dikit" begitu landed, bukan gerak barengan sama slide
   naiknya (kalau digabung malah keliatan norak/kasar). */
@keyframes toastShake{
  0%, 100% { transform:translateX(-50%) translateY(0) scale(1) rotate(0); }
  20%      { transform:translateX(calc(-50% - 3px)) translateY(0) scale(1) rotate(-1deg); }
  40%      { transform:translateX(calc(-50% + 3px)) translateY(0) scale(1) rotate(1deg); }
  60%      { transform:translateX(calc(-50% - 2px)) translateY(0) scale(1) rotate(-.6deg); }
  80%      { transform:translateX(calc(-50% + 1px)) translateY(0) scale(1) rotate(.3deg); }
}

/* Skeleton loading */
.skeleton{ background:linear-gradient(90deg,var(--surface-alt) 25%,#E3DDCD 37%,var(--surface-alt) 63%); background-size:400% 100%; animation:skeleton-loading 1.4s ease infinite; border-radius:var(--radius-sm); }
@keyframes skeleton-loading{ 0%{background-position:100% 50%;} 100%{background-position:0 50%;} }

/* Skeleton overlay - ditampilkan MENIMPA AREA KONTEN SAJA selagi halaman
   baru diambil lewat fetch (lihat script.js), selagi #page-content lama
   masih tertampil di belakangnya. Header (.topbar) & bottom navbar asli
   SENGAJA dibiarkan tetap kelihatan/tidak ketutup - makanya top/bottom
   overlay ini di-pas-in ke tinggi topbar & bottom navbar asli (BUKAN
   inset:0 penuh layar kayak versi lama yang dulu masih perlu bikin "topbar
   palsu" buat nutupin topbar asli - sekarang tidak perlu lagi).
   top:67px = tinggi .topbar (padding 14px atas+bawah + tombol/ikon 38px
   tertinggi di dalamnya, konsisten di kedua $topbarMode + border 1px) -
   kalau tinggi .topbar berubah, angka ini perlu disesuaikan juga supaya
   tetap pas. bottom-nya cuma diterapkan di halaman yang MEMANG punya
   bottom navbar (body.has-bottom-nav) - harus SAMA dengan tinggi
   .bottom-nav (lihat rule itu) supaya pas berhenti tepat di atasnya. */
.skeleton-overlay{
  position:fixed;
  top:67px; left:0; right:0; bottom:0; z-index:44;
  background:var(--bg);
  padding:14px 18px 24px;
  display:flex; flex-direction:column; gap:16px;
  overflow:hidden;
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .15s ease;
}
body.has-bottom-nav .skeleton-overlay{ bottom:calc(69px + env(safe-area-inset-bottom)); }
body.page-fullscreen-map .skeleton-overlay{ bottom:0; }
.skeleton-overlay.is-active{ opacity:1; visibility:visible; }
.skeleton-overlay-body{ display:flex; flex-direction:column; gap:14px; max-width:1180px; width:100%; margin:0 auto; }
@media (min-width:1024px){
  .skeleton-overlay{ padding:20px 32px 24px; }
}

/* Tombol "‹ Kembali" di bagian bawah halaman detail */
.btn-back-bottom{ margin-top:4px; }

/* =========================================================
   MODE GELAP — sisa penyesuaian
   Beberapa warna di atas ditulis HARDCODE (bukan var(--...)) - kebanyakan
   nuansa pastel/terang yang sengaja dirancang buat di atas latar PUTIH
   (hover state, skeleton loading, border "urgent"), jadi butuh versi
   sendiri di sini supaya tetap nyaman kalau latarnya jadi hitam pekat.
   Dikumpulkan jadi satu blok di sini (bukan disisip di sebelah tiap
   aturan aslinya) supaya gampang dicari & dibandingkan sekaligus. */
:root[data-theme="dark"] .detail-btn:hover{ background:rgba(63,169,128,.28); }
:root[data-theme="dark"] .rute-btn:hover{ background:rgba(111,168,201,.26); }
:root[data-theme="dark"] .batal-btn:hover{ background:rgba(217,127,108,.26); }
:root[data-theme="dark"] .order-card.is-urgent{ border-color:rgba(217,137,74,.4); }
:root[data-theme="dark"] .banner-link.banner-urgent{ border-color:rgba(217,137,74,.4); }

/* Skeleton loading (placeholder shimmer selagi data dimuat) - shimmer band
   tengahnya tadinya warna tan (#E3DDCD) yang kontras di atas --surface-alt
   TERANG, di dark mode dibikin abu-abu terang tipis supaya kerasa
   "shimmer" tanpa nyilaukan di atas latar gelap. */
:root[data-theme="dark"] .skeleton{ background:linear-gradient(90deg,var(--surface-alt) 25%,#333333 37%,var(--surface-alt) 63%); background-size:400% 100%; }

/* Skeleton peta (ilustrasi "blok kota" selagi tile peta asli dimuat) -
   gradasi hijau muda pastel diganti nuansa gelap senada (bukan hijau
   terang di atas hitam yang malah norak), blok bangunan/taman/jalan juga
   diredupkan supaya konsisten dengan tile MapTiler versi gelap yang
   dimuat setelahnya (lihat rute.php/kurir/peta-perlu-dikirim.php). */
:root[data-theme="dark"] .map-skeleton{ background:linear-gradient(160deg, #0B0F0D 0%, #090C0A 55%, #0B0F0C 100%); }
:root[data-theme="dark"] .map-skeleton-block{ fill:#1E2A24; opacity:.7; }
:root[data-theme="dark"] .map-skeleton-park{ fill:#152019; opacity:.85; }
:root[data-theme="dark"] .map-skeleton-road{ stroke:#2C3B33; }
:root[data-theme="dark"] .map-skeleton-shimmer{ background:linear-gradient(110deg, transparent 35%, rgba(255,255,255,.14) 50%, transparent 65%); }

/* ============ RESPONSIVE ============ */
@media (min-width:560px){
  .form-row-2{ grid-template-columns:1fr 1fr; }
  .card-grid-2{ grid-template-columns:1fr 1fr; }
}
@media (min-width:640px){
  .stats-grid{ grid-template-columns:repeat(4,1fr); }
  .stats-grid-3{ grid-template-columns:repeat(3,1fr); }
  .menu-grid{ grid-template-columns:repeat(5,1fr); }
}
@media (min-width:900px){
  .orders-list{ grid-template-columns:repeat(2,1fr); }
  .card-grid-3{ grid-template-columns:repeat(3,1fr); }
}
@media (min-width:1024px){
  .content{ --content-px:32px; --content-pt:26px; padding:var(--content-pt) var(--content-px) 56px; }
  .hero{ flex-direction:row; align-items:center; justify-content:space-between; padding:28px 32px; gap:32px; }
  .hero-target{ flex:1; max-width:320px; }
}



.spacer-15px{height: 15px;}

