/* ============================================================
   app.css — METEOR
   Mobile-first PWA: login, navigasi, laporan, manajemen user.

   Aturan main file ini:
   1. Warna SELALU lewat token theme.css. Tidak ada hex mentah kecuali
      untuk lapisan transparan (rgba) yang memang harus tembus pandang.
   2. Yang boleh dianimasikan hanya transform dan opacity. Keduanya
      dikerjakan compositor, jadi tetap 60fps di HP kelas bawah — top,
      left, dan height tidak pernah dianimasikan di file ini. Satu-
      satunya width yang ditransisikan adalah .chart-bar-fill dan
      .gallery-dot: keduanya elemen tunggal yang bergerak sekali,
      bukan sesuatu yang ikut bergerak selama halaman digulir.
   3. Rem daruratnya satu: blok prefers-reduced-motion di theme.css.
   ============================================================ */

/* ============================================================
   1. KERANGKA PWA
   ============================================================ */
html {
  height: 100%;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
}

body {
  height: 100%;
  overflow: hidden;
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  /* Inset atas/bawah TIDAK dipasang di sini. Dulu body dan .bottom-nav
     sama-sama memasangnya, jadi di HP bernotch ruang bawahnya terhitung
     dua kali. Sekarang tiap elemen melayang mengurus insetnya sendiri. */
}

.app-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

/* dvh mengikuti bilah alamat yang menyusut saat digulir; 100% saja bikin
   dock nav tersembunyi di balik bilah itu saat dibuka lewat browser. */
@supports (height: 100dvh) {
  .app-shell { height: 100dvh; }
}

/* ============================================================
   2. BILAH ATAS
   Tidak lagi sebidang warna brand. Yang dipakai lapisan kaca tipis:
   konten yang lewat di bawahnya tetap terbaca sebagai konteks, dan di
   ruangan gelap tidak ada bidang terang menyilaukan di pucuk layar.
   ============================================================ */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 10px var(--sp-4);
  padding-top: calc(10px + env(safe-area-inset-top));
  background: var(--kaca);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  border-bottom: 1px solid var(--border);
  color: var(--text);
  flex-shrink: 0;
  z-index: 100;
}

/* Browser tanpa backdrop-filter (WebView Android lawas) akan melihat
   bilah setengah tembus tanpa blur — teks di baliknya jadi ikut terbaca.
   Di sana permukaannya dibuat padat. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar { background: var(--surface); }
}

.topbar-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.topbar-logo img {
  border-radius: 50%;
  object-fit: cover;
  box-shadow: var(--shadow-xs);
  flex: none;
}

.topbar-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.1rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.1;
  background: linear-gradient(100deg, var(--brand-teks), var(--aksen-teks));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.topbar-subtitle {
  font-size: 0.68rem;
  color: var(--text-3);
  letter-spacing: 0.04em;
  line-height: 1.2;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: none;
}

/* Bilah atas sekarang memuat TIGA tombol (muat ulang, tampilan, notifikasi),
   bukan dua. Di layar 320px sisanya tinggal pas-pasan, jadi teks judul diberi
   izin memendek — yang boleh terpotong tulisannya, bukan tombolnya. */
.topbar-logo > div { min-width: 0; }
.topbar-title,
.topbar-subtitle {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-btn {
  position: relative;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  width: 38px;
  height: 38px;
  padding: 0;
  color: var(--text-2);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
  transition: background var(--dur-2) var(--ease-standar),
              color var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.topbar-btn:hover  { color: var(--text); }
.topbar-btn:active { transform: scale(.88); background: var(--border); }
.topbar-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.topbar-btn[hidden] { display: none; }

/* Tombol muat ulang: ikonnya berputar selama proses berjalan supaya ketukan
   terasa "diterima" — di PWA, jeda antara ketuk dan halaman benar-benar
   berganti bisa sedetik lebih, dan tanpa tanda apa pun orang akan mengetuk
   berkali-kali. */
#btn-muat-ulang[data-sibuk="1"] svg { animation: putar-penuh .8s linear infinite; }
#btn-muat-ulang[data-sibuk="1"]     { pointer-events: none; color: var(--brand); }

@keyframes putar-penuh {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  #btn-muat-ulang[data-sibuk="1"] svg { animation: none; opacity: .5; }
}

/* Ikon matahari/bulan berputar saat mode ditukar, bukan sekadar berganti
   bentuk — perpindahannya jadi terbaca sebagai satu aksi. */
#ikon-tampilan {
  transition: transform var(--dur-3) var(--ease-pegas);
}
.topbar-btn:active #ikon-tampilan { transform: rotate(45deg); }

/* Penanda TOKO TUTUP (10 Okt 2026). Bilah tipis di bawah bilah atas, tampil
   di semua halaman selama toko ditutup. Warnanya warna peringatan, bukan
   error: toko tutup itu keadaan yang disengaja, tapi tidak boleh terlupa. */
.banner-toko {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 9px var(--sp-4);
  background: var(--warn-soft);
  border-bottom: 1px solid var(--warn);
  color: var(--text);
  font-size: .82rem;
  line-height: 1.4;
  z-index: 99;
}
.banner-toko[hidden] { display: none; }
.banner-toko strong { color: var(--warn); }
.banner-toko-teks { min-width: 0; }
.banner-toko-tombol {
  flex: none;
  padding: 7px 14px;
  border: 1px solid var(--warn);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-body);
  font-size: .8rem;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.banner-toko-tombol:active { transform: scale(.95); }
.banner-toko-tombol[hidden] { display: none; }

/* Penanda Komeet dimatikan: bentuknya sama, nadanya info — bot yang sengaja
   dimatikan bukan keadaan genting, cuma tidak boleh terlupa. */
.banner-komeet {
  background: var(--info-soft);
  border-bottom-color: var(--info);
}
.banner-komeet strong { color: var(--info); }
.banner-komeet .banner-toko-tombol { border-color: var(--info); }

/* Keadaan toko di Setelan, tepat di atas tombol saklarnya. */
.toko-status {
  margin-bottom: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--ok-soft);
  border-left: 4px solid var(--ok);
  border-radius: var(--r-sm);
  font-size: .88rem;
  font-weight: 600;
  color: var(--text);
}
.toko-status[data-tutup="1"] {
  background: var(--warn-soft);
  border-left-color: var(--warn);
}

/* ============================================================
   3. WADAH HALAMAN & TRANSISI
   ============================================================ */
.page-container {
  flex: 1;
  overflow: hidden;
  position: relative;
}

.page {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  opacity: 0;
  transform: translate3d(24px, 0, 0) scale(.985);
  transition: opacity var(--dur-3) var(--ease-tegas),
              transform var(--dur-3) var(--ease-tegas);
  pointer-events: none;
  /* will-change sengaja TIDAK dipasang permanen. Tujuh halaman yang
     masing-masing minta lapisan compositor sendiri memakan memori
     grafis HP murah, dan browser sudah mempromosikan sendiri selama
     transisi transform berlangsung. */
}

.page.active {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
  pointer-events: auto;
  z-index: 1;
}

.page.exit-left {
  opacity: 0;
  transform: translate3d(-24px, 0, 0) scale(.985);
  pointer-events: none;
}

.page-content {
  padding: var(--sp-4);
  /* Ruang untuk dock nav mengambang + inset bawah + napas. */
  padding-bottom: calc(112px + env(safe-area-inset-bottom));
}

/* Isi halaman naik bertahap saat halaman dibuka. Jeda antar anak sengaja
   pendek (28ms): cukup untuk terbaca sebagai urutan, belum cukup lama
   untuk terasa menunggu. Dibatasi delapan anak pertama — sisanya muncul
   bersamaan, karena di bawah itu sudah di luar layar. */
@keyframes naik-masuk {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}

.page.active .page-content > * {
  animation: naik-masuk var(--dur-3) var(--ease-tegas) backwards;
}
.page.active .page-content > *:nth-child(1) { animation-delay: 0ms; }
.page.active .page-content > *:nth-child(2) { animation-delay: 28ms; }
.page.active .page-content > *:nth-child(3) { animation-delay: 56ms; }
.page.active .page-content > *:nth-child(4) { animation-delay: 84ms; }
.page.active .page-content > *:nth-child(5) { animation-delay: 112ms; }
.page.active .page-content > *:nth-child(6) { animation-delay: 140ms; }
.page.active .page-content > *:nth-child(7) { animation-delay: 168ms; }
.page.active .page-content > *:nth-child(n+8) { animation-delay: 196ms; }

/* Peta dikecualikan. transform pada leluhur Leaflet membuat ubin dihitung
   terhadap kotak yang sedang bergerak, dan hasilnya ubin miring sesaat
   setiap kali halaman dibuka. */
.page.active .page-content > .map-wrap { animation: none; }

/* ============================================================
   4. DOCK NAVIGASI
   Empat tujuan utama; Users, Log, dan Setelan pindah ke sheet
   "Lainnya". Batas empat itu yang bikin dock ini muat sebagai pil
   mengambang, dan yang bikin role operator — yang cuma berhak atas
   Home — tetap terlihat sebagai navigasi utuh, bukan satu tombol
   nyasar di tengah bilah kosong.
   ============================================================ */
.bottom-nav {
  position: fixed;
  left: 50%;
  bottom: calc(10px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: min(420px, calc(100vw - 24px));
  display: flex;
  align-items: stretch;
  gap: 2px;
  padding: 6px;
  background: var(--kaca);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--kaca-border);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  z-index: 100;
  /* Diisi oleh motion.js saat digulir ke bawah: dock menyingkir supaya
     daftar titik yang panjang bisa dibaca utuh, lalu kembali begitu
     jari digulir naik. */
  transition: transform var(--dur-3) var(--ease-tegas),
              opacity var(--dur-2) var(--ease-standar);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .bottom-nav { background: var(--surface); }
}

.bottom-nav.tersembunyi {
  transform: translateX(-50%) translateY(calc(100% + 24px));
  opacity: 0;
  pointer-events: none;
}

/* Pil yang menggeser ke tab aktif. Keempat tab lebarnya sama (flex: 1),
   jadi lebar pil ini cukup diukur sekali dan yang berpindah hanya
   translateX — satu-satunya properti yang dianimasikan, dan itu murni
   urusan compositor. Lebarnya sengaja TIDAK ditransisikan: satu-satunya
   saat nilainya berubah adalah ketika terapkanRole() menyembunyikan tab
   untuk operator, dan itu terjadi sekali sebelum layar terlihat.
   scaleX sempat jadi kandidat dan dibuang: meregangkan elemen sempit
   membuat border-radius-nya ikut meregang jadi lonjong. */
.nav-indikator {
  position: absolute;
  top: 6px;
  left: 0;
  height: calc(100% - 12px);
  width: var(--ind-w, 0px);
  border-radius: var(--r-lg);
  background: var(--brand-soft);
  border: 1px solid var(--brand-border);
  transform: translate3d(var(--ind-x, 0px), 0, 0);
  transition: transform var(--dur-3) var(--ease-pegas);
  pointer-events: none;
}
/* Sebelum JS sempat mengukur (tab pertama kali digambar), pil belum
   punya lebar — jangan tampilkan garis 1px nyasar di tepi kiri. */
.nav-indikator[data-siap="0"] { opacity: 0; }

.nav-item {
  flex: 1;
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 8px 4px 7px;
  gap: 3px;
  border: none;
  background: transparent;
  color: var(--text-3);
  font-family: var(--font-body);
  font-size: 0.66rem;
  font-weight: 650;
  letter-spacing: .01em;
  cursor: pointer;
  border-radius: var(--r-lg);
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--dur-2) var(--ease-standar);
}

.nav-item.active { color: var(--brand-teks); }
.nav-item[hidden] { display: none; }
.nav-item:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.nav-item svg {
  width: 22px;
  height: 22px;
  stroke-width: 1.8;
  fill: none;
  stroke: currentColor;
  transition: transform var(--dur-3) var(--ease-pegas),
              stroke-width var(--dur-2) var(--ease-standar);
}

.nav-item.active svg {
  transform: translateY(-1px) scale(1.12);
  stroke-width: 2.2;
}
.nav-item:active svg { transform: scale(.86); }

/* Label tab nonaktif dikecilkan, bukan dihilangkan: menghilangkannya
   bikin tinggi dock berubah tiap pindah tab. */
.nav-item span.nav-label {
  transition: opacity var(--dur-2) var(--ease-standar),
              transform var(--dur-2) var(--ease-standar);
  opacity: .75;
}
.nav-item.active span.nav-label { opacity: 1; transform: scale(1.02); }

.nav-ikon-wadah {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.nav-badge {
  position: absolute;
  top: -5px;
  right: -9px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: var(--r-full);
  background: var(--error);
  color: var(--on-status);
  font-size: .62rem;
  font-weight: 800;
  line-height: 17px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--surface);
}
.nav-badge[hidden] { display: none; }

/* ============================================================
   5. FAB
   ============================================================ */
.fab {
  position: fixed;
  right: 18px;
  bottom: calc(88px + env(safe-area-inset-bottom));
  width: 56px;
  height: 56px;
  background: var(--brand);
  color: var(--on-brand);
  border: none;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-brand);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 99;
  touch-action: manipulation;
  transition: transform var(--dur-3) var(--ease-pegas),
              box-shadow var(--dur-2) var(--ease-standar),
              background var(--dur-2) var(--ease-standar);
}

.fab:active {
  transform: scale(.88);
  box-shadow: var(--shadow-md);
}
.fab:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }

.fab svg {
  width: 26px;
  height: 26px;
  transition: transform var(--dur-3) var(--ease-pegas);
}
.fab:active svg { transform: rotate(90deg); }

/* Disembunyikan di halaman selain dashboard. Menyusut ke titik sambil
   berputar, jadi kepergiannya terbaca sebagai satu benda yang sama. */
.fab.hidden {
  transform: scale(0) rotate(-90deg);
  opacity: 0;
  pointer-events: none;
}

/* Ikut menyingkir bersama dock saat digulir ke bawah. */
.fab.tersembunyi {
  transform: translateY(calc(100% + 32px));
  opacity: 0;
  pointer-events: none;
}

/* ============================================================
   6. KARTU STATISTIK
   ============================================================ */
.stats-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}

.stat-card {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur-2) var(--ease-tegas),
              box-shadow var(--dur-2) var(--ease-standar);
}

.stat-card:active { transform: scale(.98); }

/* Sapuan warna tipis di sudut kanan atas — memberi kartu arah cahaya
   tanpa menambah elemen atau menurunkan kontras teks di atasnya. */
.stat-card::before {
  content: "";
  position: absolute;
  top: -40%;
  right: -30%;
  width: 120px;
  height: 120px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--brand-soft) 0%, transparent 70%);
  pointer-events: none;
}

.stat-card.wide { grid-column: span 2; }
/* Sapuan bara, bukan azur. Dulu menempel pada .wide karena satu-satunya
   kartu lebar memang kartu omzet; sekarang kartunya selebar yang lain, jadi
   penandanya pindah ke kelas warna sendiri. */
.stat-card.wide::before,
.stat-card.aksen::before {
  background: radial-gradient(circle, var(--aksen-soft) 0%, transparent 70%);
}

.stat-card .stat-label {
  position: relative;
  font-size: 0.72rem;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  margin-bottom: 6px;
}

.stat-card .stat-nilai {
  position: relative;
  font-family: var(--font-display);
  font-size: 1.55rem;
  font-weight: 800;
  letter-spacing: -0.025em;
  color: var(--text);
  line-height: 1.1;
}

/* Di layar sempit, dua kartu "hari ini" melebar penuh dan isinya berbaring
   mendatar: label di kiri, angka di kanan.

   Percobaan sebelumnya membiarkan keduanya berbagi baris seperti di monitor,
   lalu mengecilkan nominalnya supaya muat. Hasilnya angka terpenting di
   halaman ini justru jadi yang paling kecil — dan tanpa pengecilan itu,
   .stat-card yang ber-overflow:hidden memotongnya diam-diam ("Rp 3.225.3"
   terbaca seperti angka yang benar). Membaringkannya menyelesaikan keduanya
   sekaligus: lebarnya cukup untuk nominal berapa pun, dan satu kartu mendatar
   justru lebih pendek daripada kartu bertumpuk.

   Di layar PC (≥1024px) kartu statistik tinggal di kolom kiri dasbor yang
   cuma 400px (lihat 26.4) — sesempit layar HP — jadi alasannya sama persis. */
@media (max-width: 620px), (min-width: 1024px) {
  #kartu-terjual-hari,
  #kartu-omzet {
    grid-column: span 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding-block: 13px;
  }

  #kartu-terjual-hari .stat-label,
  #kartu-omzet .stat-label { margin-bottom: 0; }

  /* Label omzet berhenti meregang selebar kartu, supaya tombol matanya
     duduk tepat di sebelah tulisannya dan bukan terlempar ke seberang. */
  #kartu-omzet .stat-label.omzet-label { justify-content: flex-start; }

  /* Angka tidak boleh menyusut atau terpotong; kalau ruangnya kurang, label
     yang mengalah lebih dulu (min-width:0 + ellipsis di bawah). Omzet ratusan
     juta pada 1,55rem sudah mepet tepi kartu 360px, jadi di sini sedikit
     lebih kecil daripada dua kartu di atasnya — tetap angka terbesar yang
     berdiri sendiri di barisnya. */
  #kartu-terjual-hari .stat-nilai,
  #kartu-omzet .stat-nilai {
    flex: none;
    text-align: right;
    font-size: 1.35rem;
    white-space: nowrap;
  }

  #kartu-terjual-hari .stat-label,
  #kartu-omzet .stat-label { min-width: 0; }

  #kartu-omzet .stat-label.omzet-label > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* Kedua kartu "hari ini" digantung ke izin dasbor.omzet lewat atribut hidden
   (terapkanRole() di app.js). Blok di atas memberi keduanya display: flex, dan
   display dari CSS mengalahkan hidden bawaan browser — jadi akun tanpa izin
   itu tetap melihat kedua kartunya, berisi nol yang lahir dari 403. Ketahuan
   10 Okt 2026 waktu tema baru diuji dengan akun operator; ditegaskan di sini,
   sama seperti #setelan-admin-only di bagian Setelan. */
#kartu-terjual-hari[hidden],
#kartu-omzet[hidden] { display: none; }

.stat-card .stat-nilai.positif { color: var(--ok); }
.stat-card .stat-nilai.brand   { color: var(--brand-teks); }

/* Dipasang motion.js sesaat setelah angkanya berubah. */
.stat-nilai.berubah { animation: angka-naik var(--dur-4) var(--ease-pegas); }
@keyframes angka-naik {
  0%   { transform: translateY(6px) scale(.96); opacity: .4; }
  100% { transform: none; opacity: 1; }
}

/* ============================================================
   7. PETA
   ============================================================ */
.map-wrap {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  margin-bottom: var(--sp-5);
  box-shadow: var(--shadow-sm);
  /* Sudut membulat pada peta Leaflet gampang bocor di iOS Safari:
     ubinnya digambar di lapisan sendiri dan mengabaikan border-radius
     induknya. isolation memaksa lapisan baru sehingga radius berlaku. */
  isolation: isolate;
}

.map-wrap.admin-map  { height: 300px; }
.map-wrap.lokasi-map { height: 240px; }

.leaflet-container { font-family: var(--font-body) !important; }

.leaflet-popup-content-wrapper {
  border-radius: var(--r-sm) !important;
  box-shadow: var(--shadow-lg) !important;
}

/* ============================================================
   8. JUDUL BAGIAN
   ============================================================ */
.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

.section-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0;
}

.section-action {
  font-size: 0.82rem;
  font-weight: 650;
  color: var(--brand-teks);
  background: var(--brand-soft);
  border: 1px solid var(--brand-border);
  cursor: pointer;
  padding: 5px 12px;
  border-radius: var(--r-full);
  touch-action: manipulation;
  transition: background var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.section-action:active { transform: scale(.94); background: var(--brand-border); }

/* ============================================================
   9. DAFTAR TITIK
   ============================================================ */

/* Saringan status (3 Okt 2026). Chipnya menumpang .badge — lencana status yang
   sudah dipakai di tiap baris daftar — supaya warna per status tetap hidup di
   satu tempat saja (lihat catatannya di theme.css). Yang ditambahkan di sini
   cuma yang membuatnya bisa ditekan dan bisa terlihat sedang aktif. */
.filter-titik {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
}
/* Dilipat, bukan digulir: di layar sempit chip yang terdorong keluar layar
   adalah saringan yang tidak akan pernah ketemu. */
.filter-titik[hidden] { display: none; }

.chip-saring {
  font-family: inherit;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-1) var(--ease-tegas),
              border-color var(--dur-2) var(--ease-standar),
              box-shadow var(--dur-2) var(--ease-standar);
}
.chip-saring:active { transform: scale(.94); }

/* Cincin penanda aktif memakai currentColor, jadi chip yang sedang menyala
   ditandai dengan warna statusnya sendiri — satu warna seragam justru akan
   menutup satu-satunya hal yang membedakan chip ini dari chip sebelahnya. */
.chip-saring[aria-pressed="true"] {
  border-color: currentColor;
  box-shadow: inset 0 0 0 1px currentColor;
}

/* Chip "Semua" bukan status, jadi tidak ikut membawa titik berwarna. */
.chip-saring:not([data-status])::before { display: none; }

.chip-jml {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  opacity: .62;
}

.saring-info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--sp-3);
  font-size: .82rem;
  color: var(--text-2);
}
.saring-info[hidden] { display: none; }

.saring-reset {
  font-family: inherit;
  font-size: .8rem;
  font-weight: 650;
  color: var(--brand-teks);
  background: none;
  border: none;
  padding: 2px 0;
  cursor: pointer;
  touch-action: manipulation;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.saring-reset:active { opacity: .6; }

.titik-item {
  position: relative;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-3);
  box-shadow: var(--shadow-xs);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-2) var(--ease-tegas),
              box-shadow var(--dur-2) var(--ease-standar),
              border-color var(--dur-2) var(--ease-standar);
}

.titik-item:active {
  transform: scale(.985);
  box-shadow: var(--shadow-md);
  border-color: var(--border-kuat);
}

/* Titik warna status diberi lingkaran cahaya senada, jadi statusnya
   terbaca sekilas tanpa harus membaca lencananya. Warnanya diwarisi dari
   background inline yang dipasang renderTitikList() — lapisan 18% dengan
   warna yang persis sama tidak mengubah titik di tengahnya sama sekali,
   jadi yang bertambah cuma haloonya. */
.titik-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  position: relative;
}
.titik-dot::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 50%;
  background: inherit;
  opacity: .18;
}

.titik-info { flex: 1; min-width: 0; }

.titik-kode {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
}

.titik-area {
  font-size: 0.8rem;
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.titik-right { text-align: right; flex-shrink: 0; }

.titik-harga {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  font-size: 0.9rem;
  margin-bottom: 3px;
}

.titik-kosong {
  padding: var(--sp-8) var(--sp-4);
  text-align: center;
  color: var(--text-3);
  font-size: .9rem;
  background: var(--surface-alt);
  border: 1px dashed var(--border-kuat);
  border-radius: var(--r-lg);
}

/* ============================================================
   10. FORM
   ============================================================ */
.form-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-sm);
}

.form-group { margin-bottom: var(--sp-4); }
.form-group:last-child { margin-bottom: 0; }

.form-group label {
  display: block;
  font-weight: 700;
  font-size: 0.74rem;
  color: var(--text-3);
  margin-bottom: 7px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.form-hint {
  font-size: .78rem;
  color: var(--text-3);
  margin-top: 6px;
  line-height: 1.45;
}

/* Daftar centang mendatar — dipakai memilih status order / peristiwa titik
   yang dikabari ke pemilik. Membungkus sendiri supaya tetap terbaca di layar
   HP tanpa memaksa halaman menggeser mendatar. */
.pilihan-kotak {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
.pilihan-kotak label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 0;
  padding: 7px 11px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  font-size: .78rem;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text);
  cursor: pointer;
}
.pilihan-kotak input { width: 15px; height: 15px; flex-shrink: 0; }

.input-row { display: flex; gap: var(--sp-2); }
.input-row .input { flex: 1; min-width: 0; }

textarea.input { resize: vertical; line-height: 1.55; }

/* Kolom isian di iOS diperbesar otomatis kalau font-size < 16px, dan
   halaman ikut melompat. 16px mematikan perilaku itu. */
@supports (-webkit-touch-callout: none) {
  .input, textarea.input, select.input { font-size: 16px; }
}

.upload-area {
  border: 2px dashed var(--border-kuat);
  border-radius: var(--r-lg);
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--dur-2) var(--ease-standar),
              background var(--dur-2) var(--ease-standar);
  position: relative;
}
.upload-area:active { background: var(--brand-soft); border-color: var(--brand); }
.upload-area input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.upload-area svg { color: var(--text-3); margin-bottom: var(--sp-2); }
.upload-area span {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-2);
}

/* ============================================================
   11. HALAMAN PEMBELI (order & lokasi)
   ============================================================ */
.order-header { text-align: center; padding: var(--sp-3) 0 var(--sp-4); }
.order-header h2 { font-size: 1.1rem; margin-bottom: 4px; }
.order-area { font-size: 0.88rem; color: var(--text-2); }

.countdown-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-4);
}
.countdown-row .countdown { font-size: 1.1rem; padding: 9px var(--sp-4); }

.rekening-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-sm);
}

.rekening-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}

.rekening-bank {
  font-size: 0.78rem;
  color: var(--text-3);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.rekening-nomor {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  margin: 4px 0;
}

.rekening-nama { font-size: 0.85rem; color: var(--text-2); }

.btn-salin { padding: 9px 16px; font-size: 0.82rem; }

.watermark-bar {
  text-align: center;
  font-size: 0.72rem;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 2px;
  padding: var(--sp-3) 0;
}

.petunjuk-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-sm);
}
.petunjuk-card h3 { margin-bottom: var(--sp-3); font-size: 0.95rem; }
.petunjuk-text { font-size: 0.95rem; line-height: 1.65; color: var(--text); }

.foto-lokasi {
  width: 100%;
  border-radius: var(--r-lg);
  margin-top: var(--sp-3);
  box-shadow: var(--shadow-sm);
}

.action-btns { display: flex; flex-direction: column; gap: var(--sp-3); }

.btn-sukses {
  background: var(--ok);
  color: var(--on-status);
  border: none;
}
.btn-sukses:active { filter: brightness(.92); }

.btn-bahaya {
  background: var(--error-soft);
  color: var(--error);
  border: 1px solid var(--error);
}
.btn-bahaya:active { background: var(--error); color: var(--on-status); }

/* ============================================================
   12. ANTRIAN REVIEW
   ============================================================ */
.review-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-3);
  box-shadow: var(--shadow-xs);
  animation: naik-masuk var(--dur-3) var(--ease-tegas) backwards;
}

.review-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

.review-nominal {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.15rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

.review-detail {
  font-size: 0.85rem;
  color: var(--text-2);
  margin-bottom: var(--sp-3);
  line-height: 1.55;
}

.review-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.review-actions .btn { flex: 1; min-width: 110px; font-size: 0.85rem; padding: 9px; }

/* ============================================================
   13. KEADAAN KOSONG
   ============================================================ */
.empty-state {
  text-align: center;
  padding: var(--sp-8) var(--sp-4);
  color: var(--text-3);
}
.empty-state svg {
  width: 52px;
  height: 52px;
  margin-bottom: var(--sp-3);
  opacity: .35;
}
.empty-state p { font-size: 0.9rem; }

/* ============================================================
   14. TOAST
   ============================================================ */
.toast {
  position: fixed;
  bottom: calc(96px + env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%) translateY(24px) scale(.94);
  background: var(--sabak-900);
  color: #FFFFFF;
  padding: 12px 20px;
  border-radius: var(--r-full);
  font-size: 0.87rem;
  font-weight: 650;
  box-shadow: var(--shadow-xl);
  z-index: 1000;
  opacity: 0;
  transition: transform var(--dur-3) var(--ease-pegas),
              opacity var(--dur-2) var(--ease-standar);
  pointer-events: none;
  max-width: calc(100vw - 32px);
  text-align: center;
}

.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0) scale(1);
}

/* ============================================================
   15. SHEET
   ============================================================ */
.sheet-overlay {
  position: fixed;
  inset: 0;
  background: rgba(9, 18, 32, .45);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  z-index: 200;
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-standar);
  pointer-events: none;
}

.sheet-overlay.open { opacity: 1; pointer-events: auto; }

.sheet {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  margin: 0 auto;
  max-width: 520px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  z-index: 201;
  transform: translateY(100%);
  transition: transform var(--dur-4) var(--ease-tegas);
  max-height: 88vh;
  max-height: 88dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: var(--shadow-xl);
}

.sheet.open { transform: translateY(0); }

.sheet-handle {
  width: 40px;
  height: 4px;
  background: var(--border-kuat);
  border-radius: var(--r-full);
  margin: var(--sp-3) auto var(--sp-2);
  flex: none;
}

.sheet-content { padding: var(--sp-3) var(--sp-5) var(--sp-5); }

/* Isi sheet ikut naik bertahap, sama seperti isi halaman. */
.sheet.open .sheet-content > * {
  animation: naik-masuk var(--dur-3) var(--ease-tegas) backwards;
}
.sheet.open .sheet-content > *:nth-child(1) { animation-delay: 60ms; }
.sheet.open .sheet-content > *:nth-child(2) { animation-delay: 90ms; }
.sheet.open .sheet-content > *:nth-child(3) { animation-delay: 120ms; }
.sheet.open .sheet-content > *:nth-child(n+4) { animation-delay: 150ms; }

/* ---------- MENU "LAINNYA" ---------- */
.menu-daftar {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: 13px var(--sp-3);
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  font: inherit;
  font-weight: 600;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  transition: background var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.menu-item:active { background: var(--surface-alt); transform: scale(.98); }
.menu-item[hidden] { display: none; }
.menu-item:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.menu-ikon {
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--brand-soft);
  color: var(--brand-teks);
  display: flex;
  align-items: center;
  justify-content: center;
}
.menu-ikon svg { width: 19px; height: 19px; }

.menu-item.bahaya .menu-ikon { background: var(--error-soft); color: var(--error); }
.menu-item.bahaya { color: var(--error); }

.menu-teks { flex: 1; min-width: 0; }
.menu-teks small {
  display: block;
  font-weight: 500;
  font-size: .76rem;
  color: var(--text-3);
  line-height: 1.35;
}

.menu-panah { color: var(--text-3); flex: none; }

/* ============================================================
   16. TITIK DENYUT & SKELETON
   ============================================================ */
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .4; }
}

.dot-pulse {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  animation: pulse 1.5s var(--ease-standar) infinite;
}

@keyframes shimmer {
  0%   { background-position: -200% 0; }
  100% { background-position:  200% 0; }
}

.skeleton {
  background: linear-gradient(90deg,
              var(--surface-alt) 25%, var(--surface) 50%, var(--surface-alt) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
  border-radius: var(--r-sm);
}

/* ---------- RIAK SENTUH ---------- */
.ripple { position: relative; overflow: hidden; }

.ripple::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at var(--ripple-x, 50%) var(--ripple-y, 50%),
              color-mix(in srgb, var(--brand) 22%, transparent) 0%, transparent 55%);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease-standar);
  pointer-events: none;
}
.ripple:active::after { opacity: 1; transition-duration: var(--dur-1); }

/* color-mix belum ada di WebView Android lawas — di sana riaknya
   memakai lapisan netral. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .ripple::after {
    background: radial-gradient(circle at var(--ripple-x, 50%) var(--ripple-y, 50%),
                rgba(36, 114, 224, .18) 0%, transparent 55%);
  }
}

/* ---------- BATANG GULIR ---------- */
.page, .sheet, .notif-panel {
  scrollbar-width: thin;
  scrollbar-color: var(--border-kuat) transparent;
}
.page::-webkit-scrollbar,
.sheet::-webkit-scrollbar { width: 4px; }
.page::-webkit-scrollbar-track,
.sheet::-webkit-scrollbar-track { background: transparent; }
.page::-webkit-scrollbar-thumb,
.sheet::-webkit-scrollbar-thumb {
  background: var(--border-kuat);
  border-radius: var(--r-full);
}

.pull-indicator {
  text-align: center;
  padding: var(--sp-3);
  font-size: 0.82rem;
  color: var(--text-3);
  font-weight: 600;
  display: none;
}

/* ============================================================
   17. UNGGAH FOTO
   ============================================================ */
.foto-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-3);
}

.foto-add {
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 2px dashed var(--border-kuat);
  border-radius: var(--r-lg);
  cursor: pointer;
  transition: border-color var(--dur-2) var(--ease-standar),
              background var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
  position: relative;
  background: var(--surface-alt);
}

.foto-add:active {
  background: var(--brand-soft);
  border-color: var(--brand);
  transform: scale(.95);
}

.foto-add input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.foto-add svg { color: var(--text-3); }
.foto-add span {
  font-size: 0.7rem;
  font-weight: 650;
  color: var(--text-3);
  text-align: center;
  padding: 0 4px;
}
.foto-add.disabled { opacity: 0.35; pointer-events: none; }

.foto-thumb {
  aspect-ratio: 1;
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  animation: foto-masuk var(--dur-3) var(--ease-pegas);
}
@keyframes foto-masuk {
  from { opacity: 0; transform: scale(.8); }
  to   { opacity: 1; transform: none; }
}

.foto-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Formulir Ubah Titik (10 Okt 2026): tiap foto membawa dua tombolnya sendiri
   di tepi bawah, di atas fotonya. Latar tombolnya lapisan hitam tembus pandang
   — bukan token — karena ia duduk di atas foto, yang warnanya tidak ikut tema. */
.ubah-foto-aksi {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  gap: 1px;
}
.ubah-foto-aksi > * {
  flex: 1;
  margin: 0;
  padding: 9px 4px;
  border: 0;
  border-radius: 0;
  background: rgba(0, 0, 0, .64);
  color: #FFFFFF;
  font-family: var(--font-body);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  text-align: center;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.ubah-foto-aksi > *:active { background: rgba(0, 0, 0, .82); }

/* Akibat dari perubahan yang sedang dipilih — dibaca SEBELUM menekan Simpan. */
.ubah-akibat {
  margin-bottom: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--info-soft);
  border-left: 4px solid var(--info);
  border-radius: var(--r-sm);
  font-size: .85rem;
  line-height: 1.5;
  color: var(--text);
}
.ubah-akibat[hidden] { display: none; }
.ubah-akibat > div + div { margin-top: 6px; }

.foto-thumb-delete {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 26px;
  height: 26px;
  background: rgba(9, 18, 32, .65);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  color: #FFFFFF;
  border: none;
  border-radius: 50%;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
  padding: 0;
}
.foto-thumb-delete:active { background: var(--error); transform: scale(.85); }

.foto-thumb-badge {
  position: absolute;
  bottom: 6px;
  left: 6px;
  background: rgba(9, 18, 32, .62);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  color: #FFFFFF;
  font-size: 0.64rem;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: var(--r-full);
}

.foto-counter {
  margin-top: var(--sp-2);
  font-size: 0.8rem;
  font-weight: 650;
  color: var(--text-3);
  transition: color var(--dur-2) var(--ease-standar);
}
.foto-counter.warning { color: var(--warn); }
.foto-counter.ok      { color: var(--ok); }

/* ---------- GALERI FOTO (halaman lokasi) ---------- */
.foto-gallery {
  display: flex;
  gap: var(--sp-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  margin-top: var(--sp-3);
  padding-bottom: var(--sp-2);
  scrollbar-width: none;
}
.foto-gallery::-webkit-scrollbar { display: none; }

.foto-gallery-item {
  flex: 0 0 100%;
  scroll-snap-align: center;
  border-radius: var(--r-lg);
  object-fit: cover;
  width: 100%;
  height: auto;
  max-height: 280px;
  box-shadow: var(--shadow-sm);
  /* Thumbnail sengaja tetap dipotong (cover) supaya galerinya rata dan tidak
     melompat-lompat tingginya. Foto utuhnya sejauh satu ketukan — lightbox. */
  cursor: zoom-in;
}

/* ---------- LIGHTBOX FOTO ----------
   Foto petunjuk dipakai admin untuk mencocokkan lokasi kupon di lapangan,
   jadi versi terpotong 280px di galeri sering tidak cukup. Di sini fotonya
   tampil utuh (contain), dan sekali ketuk lagi jadi ukuran asli yang bisa
   digeser — untuk membaca detail kecil seperti nomor rumah atau papan nama. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 10000;                  /* di atas sheet (1200) dan toast (9999) */
  background: rgba(0, 0, 0, .93);
  visibility: hidden;
  opacity: 0;
  transition: opacity .18s ease, visibility 0s linear .18s;
}
.lightbox.open {
  visibility: visible;
  opacity: 1;
  transition: opacity .18s ease, visibility 0s linear 0s;
}

/* Panggung yang menggeser saat gambar diperbesar melebihi layar. */
.lightbox-panggung {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}

.lightbox-gambar {
  display: block;
  margin: auto;
  max-width: 100vw;
  max-height: 100vh;
  object-fit: contain;             /* utuh — ini inti perbaikannya */
  cursor: zoom-in;
}
.lightbox.zoom .lightbox-panggung { align-items: flex-start; justify-content: flex-start; }
.lightbox.zoom .lightbox-gambar {
  max-width: none;
  max-height: none;
  width: auto;
  height: auto;
  cursor: zoom-out;
}

.lightbox-tutup,
.lightbox-nav {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(0, 0, 0, .5);
  color: #fff;
  cursor: pointer;
  border-radius: 999px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.lightbox-tutup {
  top: calc(env(safe-area-inset-top, 0px) + 12px);
  right: 12px;
  width: 44px;
  height: 44px;
  font-size: 1.4rem;
  line-height: 1;
}
.lightbox-nav {
  top: 50%;
  transform: translateY(-50%);
  width: 48px;
  height: 48px;
  font-size: 2rem;
  line-height: 1;
  padding-bottom: 4px;
}
.lightbox-nav.kiri  { left: 10px; }
.lightbox-nav.kanan { right: 10px; }
.lightbox-tutup:active,
.lightbox-nav:active { background: rgba(0, 0, 0, .75); }

.lightbox-hitung {
  position: absolute;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
  left: 50%;
  transform: translateX(-50%);
  color: #fff;
  background: rgba(0, 0, 0, .5);
  padding: 6px 14px;
  border-radius: 999px;
  font-size: .8rem;
  font-weight: 600;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.foto-gallery-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: var(--sp-3) 0 var(--sp-1);
}

.gallery-dot {
  width: 7px;
  height: 7px;
  border-radius: var(--r-full);
  background: var(--border-kuat);
  transition: background var(--dur-3) var(--ease-standar),
              transform var(--dur-3) var(--ease-pegas),
              width var(--dur-3) var(--ease-pegas);
}
/* Titik aktif memanjang jadi garis pendek, bukan sekadar membesar —
   arah gulirnya jadi ikut terbaca. */
.gallery-dot.active {
  background: var(--brand);
  width: 20px;
}

/* ============================================================
   18. LAYAR MASUK
   ============================================================ */
.login-screen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sabak-900);
  transition: opacity var(--dur-4) var(--ease-standar),
              transform var(--dur-4) var(--ease-standar);
  overflow: hidden;
}

.login-screen.hidden {
  opacity: 0;
  transform: scale(1.06);
  pointer-events: none;
}

.login-bg { position: absolute; inset: 0; overflow: hidden; }

/* Dua cahaya yang bergerak pelan dengan tempo berbeda: satu biru dari
   ekor logo, satu bara dari intinya. Keduanya blur berat, jadi yang
   terlihat cuma warna yang perlahan bergeser — bukan bentuk. */
.login-glow {
  position: absolute;
  width: 340px;
  height: 340px;
  top: 6%;
  left: 50%;
  background: radial-gradient(circle,
              rgba(36, 114, 224, .55) 0%, transparent 70%);
  border-radius: 50%;
  filter: blur(70px);
  transform: translateX(-50%);
  animation: cahaya-utama 7s var(--ease-standar) infinite;
}

.login-glow::after {
  content: "";
  position: absolute;
  inset: 22% -18% -30% 30%;
  background: radial-gradient(circle,
              rgba(232, 115, 13, .42) 0%, transparent 70%);
  border-radius: 50%;
  animation: cahaya-bara 9s var(--ease-standar) infinite;
}

@keyframes cahaya-utama {
  0%, 100% { opacity: .55; transform: translateX(-50%) scale(1); }
  50%      { opacity: 1;   transform: translateX(-50%) scale(1.18); }
}
@keyframes cahaya-bara {
  0%, 100% { opacity: .5; transform: translate(0, 0) scale(1); }
  50%      { opacity: .9; transform: translate(-14px, 18px) scale(1.14); }
}

@keyframes shake {
  0%, 100%  { transform: translateX(0); }
  20%, 60%  { transform: translateX(-8px); }
  40%, 80%  { transform: translateX(8px); }
}

.login-container {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 380px;
  padding: var(--sp-5);
}

.login-brand { text-align: center; margin-bottom: var(--sp-6); }

.login-icon {
  width: 72px;
  height: 72px;
  margin: 0 auto var(--sp-4);
  animation: logo-mendarat var(--dur-4) var(--ease-pegas) backwards;
}
@keyframes logo-mendarat {
  from { opacity: 0; transform: translateY(-16px) scale(.86); }
  to   { opacity: 1; transform: none; }
}

.login-icon img { box-shadow: 0 8px 28px rgba(36, 114, 224, .45); }
.login-icon svg { width: 100%; height: 100%; }

.login-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 2.3rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  color: #FFFFFF;
  margin: 0;
  animation: naik-masuk var(--dur-4) var(--ease-tegas) 80ms backwards;
}

.login-tagline {
  font-size: 0.86rem;
  color: rgba(255, 255, 255, .55);
  margin: 6px 0 0;
  letter-spacing: 0.03em;
  animation: naik-masuk var(--dur-4) var(--ease-tegas) 140ms backwards;
}

.login-card {
  background: rgba(255, 255, 255, .07);
  border: 1px solid rgba(255, 255, 255, .12);
  -webkit-backdrop-filter: blur(24px) saturate(150%);
  backdrop-filter: blur(24px) saturate(150%);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .40);
  animation: naik-masuk var(--dur-4) var(--ease-tegas) 200ms backwards;
}

.login-card-header { margin-bottom: var(--sp-5); }
.login-card-header h2 { color: #FFFFFF; font-size: 1.15rem; margin: 0 0 4px; }
.login-card-header p { color: rgba(255, 255, 255, .5); font-size: 0.85rem; margin: 0; }

.login-card .form-group { margin-bottom: var(--sp-4); }
.login-card label {
  color: rgba(255, 255, 255, .62);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

.login-input-wrap { position: relative; display: flex; align-items: center; }

.login-input-wrap svg {
  position: absolute;
  left: 13px;
  color: rgba(255, 255, 255, .4);
  pointer-events: none;
  z-index: 1;
  transition: color var(--dur-2) var(--ease-standar);
}
.login-input-wrap:focus-within svg { color: var(--azur-300); }

.login-input-wrap .input {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .14);
  color: #FFFFFF;
  padding-left: 42px;
  font-size: 0.95rem;
  border-radius: var(--r-sm);
}
.login-input-wrap .input::placeholder { color: rgba(255, 255, 255, .3); }
.login-input-wrap .input:focus {
  border-color: var(--azur-400);
  background: rgba(255, 255, 255, .12);
  box-shadow: 0 0 0 3px rgba(36, 114, 224, .25);
}

.login-eye {
  position: absolute;
  right: 8px;
  background: none;
  border: none;
  color: rgba(255, 255, 255, .4);
  cursor: pointer;
  padding: 8px;
  z-index: 1;
  border-radius: var(--r-full);
  transition: color var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.login-eye:active { color: rgba(255, 255, 255, .85); transform: scale(.88); }

.login-error {
  font-size: 0.85rem;
  color: #FF8A80;
  font-weight: 650;
  min-height: 20px;
  margin-bottom: var(--sp-3);
  text-align: center;
}
.login-error:not(:empty) { animation: naik-masuk var(--dur-2) var(--ease-tegas); }

.login-btn {
  font-size: 1.02rem;
  padding: 15px;
  border-radius: var(--r-sm);
  background: var(--azur-500);
  color: #FFFFFF;
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow: 0 8px 24px rgba(36, 114, 224, .40);
}
.login-btn:hover  { background: var(--azur-400); }
.login-btn:active { transform: scale(.97); background: var(--azur-600); }

.login-footer {
  display: flex;
  justify-content: space-between;
  margin-top: var(--sp-4);
  font-size: 0.72rem;
  color: rgba(255, 255, 255, .3);
}

/* ============================================================
   19. LAPORAN
   ============================================================ */
.report-tabs {
  position: relative;
  display: flex;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  padding: 4px;
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-xs);
}

.report-tab {
  flex: 1;
  padding: 10px 8px;
  background: transparent;
  border: none;
  border-radius: var(--r-full);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 650;
  color: var(--text-2);
  cursor: pointer;
  touch-action: manipulation;
  transition: background var(--dur-3) var(--ease-tegas),
              color var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.report-tab:active { transform: scale(.96); }

.report-tab.active {
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--shadow-sm);
}

.report-panel { display: none; }
.report-panel.active { display: block; animation: fadeSlideIn var(--dur-3) var(--ease-tegas); }

@keyframes fadeSlideIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

.report-period-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--sp-3);
  font-weight: 700;
  font-size: 0.95rem;
}

.report-date { font-weight: 500; font-size: 0.82rem; color: var(--text-2); }

.report-summary-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.report-summary-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  box-shadow: var(--shadow-sm);
}

.report-summary-label {
  font-size: 0.72rem;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  margin-bottom: 6px;
}

.report-summary-value {
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -0.025em;
  color: var(--text);
  margin-bottom: 6px;
  font-variant-numeric: tabular-nums;
}

.report-compare {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.78rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: var(--r-full);
}
.report-compare.naik  { background: var(--ok-soft);    color: var(--ok); }
.report-compare.turun { background: var(--error-soft); color: var(--error); }

.report-vs-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-sm);
}

.report-vs-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 700;
  font-size: 0.9rem;
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--divider);
}

.report-vs-date { font-weight: 500; font-size: 0.8rem; color: var(--text-2); }

.report-vs-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  font-size: 0.9rem;
}
.report-vs-row span:first-child { color: var(--text-2); }
.report-vs-row span:last-child  { font-weight: 700; }

/* ---------- BATANG GRAFIK ---------- */
.report-chart-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-sm);
}

.report-chart-title { font-weight: 700; font-size: 0.9rem; margin-bottom: var(--sp-4); }

.chart-bars { display: flex; flex-direction: column; gap: var(--sp-4); }
.chart-bar-group { display: flex; flex-direction: column; gap: 6px; }
.chart-bar-label { font-size: 0.78rem; font-weight: 650; color: var(--text-2); }

.chart-bar-track {
  height: 28px;
  background: var(--surface-alt);
  border-radius: var(--r-full);
  overflow: hidden;
  border: 1px solid var(--border);
}

.chart-bar-fill {
  height: 100%;
  border-radius: var(--r-full);
  transition: width var(--dur-4) var(--ease-tegas);
}
.chart-bar-fill.current { background: linear-gradient(90deg, var(--brand), var(--azur-400)); }
.chart-bar-fill.prev    { background: var(--border-kuat); }

.chart-bar-value {
  font-size: 0.82rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* ---------- TABEL LAPORAN ---------- */
.report-table-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--sp-4);
}

.report-table-title { font-weight: 700; font-size: 0.9rem; margin-bottom: var(--sp-3); }

.report-table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

.report-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }

.report-table th {
  text-align: left;
  padding: 8px 10px;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-3);
  border-bottom: 2px solid var(--border);
  white-space: nowrap;
  font-weight: 700;
}

.report-table td {
  padding: 11px 10px;
  border-bottom: 1px solid var(--divider);
  white-space: nowrap;
}
.report-table tr:last-child td { border-bottom: none; }
.report-table .badge { font-size: .66rem; padding: 2px 8px; }

.report-table tr.baris-klik { cursor: pointer; transition: background var(--dur-2) var(--ease-standar); }
.report-table tr.baris-klik:hover td  { background: var(--surface-alt); }
.report-table tr.baris-klik:active td { background: var(--brand-soft); }
.report-table tr.baris-klik:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* ---------- FILTER & PEMBANDING ---------- */
.rpt-filter-bar {
  margin-bottom: var(--sp-4);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-2);
}
/* Layar sempit: dua saringan bertumpuk, bukan dua kotak yang sama-sama
   terpotong sampai isinya tidak terbaca. */
@media (max-width: 380px) {
  .rpt-filter-bar { grid-template-columns: 1fr; }
}

.rpt-compare-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.rpt-compare-card {
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.rpt-compare-card.main {
  border-color: var(--brand-border);
  background: linear-gradient(160deg, var(--brand-soft) 0%, var(--surface) 55%);
  box-shadow: var(--shadow-sm);
}

.rpt-compare-card.sub { background: var(--surface-alt); }

.rpt-compare-label {
  font-size: 0.7rem;
  font-weight: 800;
  color: var(--text-3);
  letter-spacing: 0.08em;
  margin-bottom: var(--sp-1);
}

.pct-badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: var(--r-full);
  font-size: 0.74rem;
  font-weight: 700;
}
.pct-badge.naik   { background: var(--ok-soft);    color: var(--ok); }
.pct-badge.turun  { background: var(--error-soft); color: var(--error); }
.pct-badge.netral { background: var(--surface-alt); color: var(--text-3); }

/* ============================================================
   20. MANAJEMEN USER
   ============================================================ */
.user-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-3);
  box-shadow: var(--shadow-xs);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  animation: naik-masuk var(--dur-3) var(--ease-tegas) backwards;
}

.user-avatar {
  width: 44px;
  height: 44px;
  border-radius: var(--r-sm);
  background: linear-gradient(140deg, var(--brand), var(--azur-700));
  color: #FFFFFF;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1rem;
  flex-shrink: 0;
  box-shadow: var(--shadow-xs);
}

.user-info { flex: 1; min-width: 0; }
.user-name { font-weight: 700; font-size: 0.95rem; }
.user-meta {
  font-size: 0.8rem;
  color: var(--text-3);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
}

.user-role {
  display: inline-block;
  padding: 2px 9px;
  border-radius: var(--r-full);
  font-size: 0.66rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.user-role.admin    { background: var(--brand-soft); color: var(--brand-teks); }
.user-role.operator { background: var(--aksen-soft); color: var(--aksen-teks); }

.user-delete {
  background: none;
  border: none;
  color: var(--text-3);
  cursor: pointer;
  padding: 9px;
  border-radius: var(--r-sm);
  touch-action: manipulation;
  transition: color var(--dur-2) var(--ease-standar),
              background var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.user-delete:active { color: var(--error); background: var(--error-soft); transform: scale(.88); }

/* ============================================================
   21. NOTIFIKASI
   ============================================================ */
.notif-badge {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--r-full);
  background: var(--error);
  color: var(--on-status);
  font-size: .66rem;
  font-weight: 800;
  line-height: 18px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--surface);
}
.notif-badge[hidden] { display: none; }

@keyframes notif-denyut {
  0%, 100% { transform: scale(1); }
  30%      { transform: scale(1.4); }
}
.notif-badge.baru { animation: notif-denyut .5s var(--ease-pegas) 3; }

.notif-panel {
  position: fixed;
  top: calc(58px + env(safe-area-inset-top));
  right: 12px;
  width: min(360px, calc(100vw - 24px));
  max-height: 70vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-xl);
  z-index: 1200;
  padding: 8px;
  transform-origin: top right;
  animation: panel-mekar var(--dur-3) var(--ease-pegas);
}
.notif-panel[hidden] { display: none; }

@keyframes panel-mekar {
  from { opacity: 0; transform: scale(.9) translateY(-8px); }
  to   { opacity: 1; transform: none; }
}

.notif-panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 10px 10px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.notif-panel-judul { font-weight: 700; font-size: .95rem; }
.notif-panel-aksi {
  background: none;
  border: none;
  color: var(--brand-teks);
  font-size: .8rem;
  font-weight: 650;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: var(--r-full);
  transition: background var(--dur-2) var(--ease-standar);
}
.notif-panel-aksi:active { background: var(--brand-soft); }

.notif-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: var(--r-sm);
  padding: 10px;
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition: background var(--dur-2) var(--ease-standar);
}
.notif-item:hover  { background: var(--surface-alt); }
.notif-item:active { background: var(--border); }
.notif-item + .notif-item { border-top: 1px solid var(--border); }
.notif-item.belum { background: var(--brand-soft); }

.notif-item-judul  { font-weight: 650; font-size: .88rem; margin-bottom: 2px; }
.notif-item-detail { font-size: .8rem; color: var(--text-2); line-height: 1.4; }
.notif-item-waktu  { font-size: .72rem; color: var(--text-3); margin-top: 3px; }

.notif-kosong {
  padding: 28px 12px;
  text-align: center;
  color: var(--text-3);
  font-size: .85rem;
}

/* Elemen yang disembunyikan terapkanRole() untuk role operator. Tanpa
   baris ini [hidden] kalah spesifisitas oleh display:flex di atasnya. */
#setelan-admin-only[hidden] { display: none; }

/* ============================================================
   22. SETELAN — FORMAT BALASAN WA
   ============================================================ */
.tpl-kartu {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin-bottom: var(--sp-3);
  box-shadow: var(--shadow-xs);
}

.tpl-judul {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: .95rem;
  margin-bottom: 4px;
}

.tpl-tanda {
  font-size: .64rem;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: var(--r-full);
  background: var(--aksen-soft);
  color: var(--aksen-teks);
  border: 1px solid var(--aksen-border);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.tpl-ket { font-size: .8rem; color: var(--text-2); margin-bottom: 10px; line-height: 1.45; }

.tpl-ph { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 10px; }
.tpl-ph code {
  font-size: .72rem;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: 3px 7px;
  cursor: pointer;
  transition: background var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.tpl-ph code:active { background: var(--brand-soft); transform: scale(.93); }

.tpl-teks {
  width: 100%;
  min-height: 140px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: .8rem;
  line-height: 1.55;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-alt);
  color: var(--text);
  resize: vertical;
}
.tpl-teks:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

.tpl-aksi { display: flex; gap: 8px; margin-top: 10px; }
.tpl-aksi .btn { flex: 1; padding: 9px; font-size: .82rem; }

.tpl-pratinjau {
  margin-top: 10px;
  padding: 12px;
  border-radius: var(--r-sm);
  border-top-left-radius: var(--r-xs);
  background: var(--wa-bubble);
  color: var(--wa-teks);
  font-size: .82rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  animation: naik-masuk var(--dur-3) var(--ease-tegas);
}
.tpl-pratinjau[hidden] { display: none; }

/* ============================================================
   23. SETELAN — PILIHAN TAMPILAN
   ============================================================ */
.segmen {
  display: flex;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  padding: 4px;
  margin-bottom: var(--sp-5);
  box-shadow: var(--shadow-xs);
}

.segmen-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 6px;
  background: transparent;
  border: none;
  border-radius: var(--r-full);
  font-family: var(--font-body);
  font-size: .8rem;
  font-weight: 650;
  color: var(--text-2);
  cursor: pointer;
  touch-action: manipulation;
  transition: background var(--dur-3) var(--ease-tegas),
              color var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
  -webkit-tap-highlight-color: transparent;
}
.segmen-btn:active { transform: scale(.95); }
.segmen-btn svg { width: 16px; height: 16px; flex-shrink: 0; }

.segmen-btn[aria-pressed="true"] {
  background: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--shadow-sm);
}

/* Saat pilihannya "ikut sistem", mode yang sedang berlaku tetap
   ditandai supaya terlihat HP-nya sekarang dibaca sebagai apa. */
.segmen-btn[data-ikut="1"] {
  box-shadow: inset 0 0 0 1.5px var(--brand-border);
  color: var(--brand-teks);
}

.setelan-ket {
  font-size: .85rem;
  color: var(--text-2);
  margin-bottom: var(--sp-4);
  line-height: 1.55;
}

/* Saklar per akun (11 Okt 2026, Setelan → Foto Titik): satu baris per akun,
   saklar di kanan. Kotak centang aslinya tetap ada, cuma dibuat tembus pandang
   di atas relnya — keyboard dan pembaca layar tetap memperlakukannya sebagai
   kotak centang biasa. */
.saklar-daftar {
  margin-bottom: var(--sp-6);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
}
.saklar-kosong { padding: var(--sp-3) var(--sp-4); }
.saklar-baris {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  margin: 0;
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
}
.saklar-baris + .saklar-baris { border-top: 1px solid var(--border); }
.saklar-baris[data-terkunci="1"] { cursor: default; }
.saklar-info { flex: 1; min-width: 0; }
.saklar-nama {
  display: block;
  font-size: .95rem;
  font-weight: 700;
  color: var(--text);
}
.saklar-ket {
  display: block;
  font-size: .8rem;
  font-weight: 400;
  color: var(--text-2);
}
.saklar {
  position: relative;
  flex: none;
  width: 46px;
  height: 26px;
}
.saklar input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: inherit;
}
.saklar-rel {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  /* Rel mati sengaja abu-abu netral dari warna teks, bukan --border-kuat:
     di Tema 3 garis tegasnya hitam, dan rel hitam terbaca seperti menyala.
     Baris pertama cadangan untuk browser tanpa color-mix(). */
  background: var(--border-kuat);
  background: color-mix(in srgb, var(--text) 28%, transparent);
  transition: background .15s;
  pointer-events: none;
}
.saklar-rel::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #FFFFFF;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
  transition: transform .15s;
}
.saklar input:checked + .saklar-rel { background: var(--ok); }
.saklar input:checked + .saklar-rel::after { transform: translateX(20px); }
.saklar input:focus-visible + .saklar-rel { outline: 2px solid var(--brand); outline-offset: 2px; }
.saklar input:disabled + .saklar-rel { opacity: .55; }

/* Pemilih tema (10 Okt 2026). Tiga tombol sejajar, masing-masing membawa
   contoh kecil rupa temanya. Warna contoh ditulis tetap — satu-satunya tempat
   di file ini selain lembar cetak — karena tugasnya justru menunjukkan tema
   LAIN dari yang sedang dipakai; lewat token ketiganya akan tampak kembar. */
.pilih-tema {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-2);
  margin-bottom: var(--sp-5);
}

.tema-opsi {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--font-body);
  color: var(--text);
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  box-shadow: var(--shadow-xs);
  transition: border-color var(--dur-2) var(--ease-standar),
              box-shadow var(--dur-2) var(--ease-standar),
              transform var(--dur-1) var(--ease-tegas);
}
.tema-opsi:active { transform: scale(.97); }
.tema-opsi:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.tema-opsi[aria-pressed="true"] {
  border-color: var(--brand);
  box-shadow: 0 0 0 2px var(--brand);
}

.tema-nama { font-size: .84rem; font-weight: 700; margin-top: 6px; }
.tema-ket  { font-size: .7rem; color: var(--text-3); line-height: 1.3; }

/* Contoh: satu blok besar (kartu omzet), satu blok kecil (kartu statistik),
   satu garis (baris daftar) — tiga benda yang paling beda antar tema. */
.tema-contoh {
  position: relative;
  display: block;
  height: 62px;
  border-radius: var(--r-sm);
  overflow: hidden;
}
.tema-contoh i { position: absolute; display: block; }
.tema-contoh i:nth-child(1) { left: 7px; right: 7px; top: 7px; height: 22px; }
.tema-contoh i:nth-child(2) { left: 7px; width: 38%; top: 35px; height: 20px; }
.tema-contoh i:nth-child(3) { right: 7px; width: 38%; top: 35px; height: 20px; }

.tema-opsi[data-tema="1"] .tema-contoh { background: #EDF1F6; }
.tema-opsi[data-tema="1"] .tema-contoh i { background: #FFFFFF; border: 1px solid #DDE3EC; border-radius: 7px; }
.tema-opsi[data-tema="1"] .tema-contoh i:nth-child(1) { background: linear-gradient(100deg, #2472E0 0 34%, #FFFFFF 34%); }
.tema-opsi[data-tema="1"] .tema-contoh i:nth-child(3) { background: linear-gradient(100deg, #FFFFFF 0 62%, #FFE8CE 62%); }

.tema-opsi[data-tema="2"] .tema-contoh { background: #F3F5F9; }
.tema-opsi[data-tema="2"] .tema-contoh i { background: #FFFFFF; border: 1px solid #E3E8F0; border-radius: 6px; }
.tema-opsi[data-tema="2"] .tema-contoh i:nth-child(1) { background: #1F4FD8; border-color: #1F4FD8; border-radius: 8px; }

.tema-opsi[data-tema="3"] .tema-contoh { background: #F2EFE6; }
.tema-opsi[data-tema="3"] .tema-contoh i { border: 2px solid #111111; border-radius: 6px; box-shadow: 2px 2px 0 #111111; }
.tema-opsi[data-tema="3"] .tema-contoh i:nth-child(1) { background: #FF5B14; }
.tema-opsi[data-tema="3"] .tema-contoh i:nth-child(2) { background: #2BD46C; }
.tema-opsi[data-tema="3"] .tema-contoh i:nth-child(3) { background: #FFD23F; }

/* ============================================================
   24. PENYESUAIAN MODE MALAM
   Sisa file sudah memakai token, jadi ikut berubah sendiri. Yang di
   bawah ini khusus: nilai yang memang ditulis sebagai warna mentah,
   atau komponen yang perlu bentuk berbeda di latar malam.
   ============================================================ */

/* Toast harus lebih TERANG dari permukaan di belakangnya, bukan lebih
   gelap — --sabak-900 nyaris lenyap di atas latar malam. */
[data-mode="gelap"] .toast {
  background: var(--malam-500);
  color: var(--malam-100);
  border: 1px solid rgba(255, 255, 255, .10);
}

[data-mode="gelap"] .sheet-overlay { background: rgba(0, 0, 0, .68); }

[data-mode="gelap"] .login-screen { background: var(--malam-900); }

/* Sapuan cahaya di kartu statistik jadi terlalu ramai di latar malam:
   permukaan sudah dibedakan lewat terang, bukan lewat warna. */
[data-mode="gelap"] .stat-card::before { opacity: .55; }

/* Peta. Ubin OpenStreetMap putih menyala — sebidang lampu sorot di
   tengah layar malam. invert + hue-rotate mengembalikan warna asli
   (biru tetap biru) di atas dasar gelap; hanya .leaflet-tile yang
   kena, jadi penanda titik, popup, dan tombol zoom tidak ikut
   terbalik. */
[data-mode="gelap"] .leaflet-tile {
  filter: invert(1) hue-rotate(180deg) brightness(.9) contrast(.92) saturate(.85);
}
[data-mode="gelap"] .leaflet-container { background: var(--surface-alt); }
[data-mode="gelap"] .leaflet-popup-content-wrapper,
[data-mode="gelap"] .leaflet-popup-tip {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-md);
}
[data-mode="gelap"] .leaflet-bar a {
  background: var(--surface);
  color: var(--text);
  border-bottom-color: var(--border);
}
[data-mode="gelap"] .leaflet-bar a:hover { background: var(--surface-alt); }

/* ============================================================
   25. LAYAR LEBAR
   ============================================================ */
@media (min-width: 768px) {
  .page-content { max-width: 560px; margin: 0 auto; }
  .map-wrap.admin-map { height: 380px; }
  .fab { right: calc(50% - 280px + 18px); }
}

@media (max-width: 360px) {
  .topbar { padding-left: var(--sp-3); padding-right: var(--sp-3); }
  .topbar-btn { width: 34px; height: 34px; }
  .nav-item { font-size: .6rem; }
  .nav-item svg { width: 20px; height: 20px; }
}

/* Layar pendek dalam mode lanskap: dock mengambang memakan terlalu
   banyak dari tinggi yang tersisa. */
@media (max-height: 480px) and (orientation: landscape) {
  .bottom-nav { bottom: 6px; padding: 3px; }
  .nav-item { padding: 5px 4px; font-size: .6rem; }
  .nav-item svg { width: 18px; height: 18px; }
  .page-content { padding-bottom: calc(84px + env(safe-area-inset-bottom)); }
  .fab { width: 46px; height: 46px; bottom: calc(70px + env(safe-area-inset-bottom)); }
}


/* ---------- DAFTAR PROJEK / KATA KUNCI OPERATOR ---------- */
/* Kata kunci ditampilkan tersamar ('c***'), jadi barisnya harus tetap
   terbaca sebagai daftar walau isinya cuma empat karakter. */
.kunci-daftar { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-2); }

.kunci-baris {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}

.kunci-samar {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 1rem;
  letter-spacing: .08em;
  color: var(--text);
}

.kunci-kosong { color: var(--text-3); font-size: .85rem; padding: var(--sp-2) 0; }

.kunci-hapus {
  background: none;
  border: none;
  color: var(--error);
  font-size: .8rem;
  cursor: pointer;
  padding: var(--sp-2);
  border-radius: var(--r-sm);
}
.kunci-hapus:hover { background: var(--surface); }

.kunci-tambah { display: flex; gap: var(--sp-2); }
.kunci-tambah .input { flex: 1; min-width: 0; }


/* ---------- sensor omzet (pola saldo mobile banking) ---------- */
.stat-card .stat-label.omzet-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.omzet-mata {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Kotak sentuh 28px: ikonnya 18px, sisanya ruang jari. Tombol seukuran
     ikon saja terlalu kecil untuk dipencet di HP. */
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--text-3);
  cursor: pointer;
  flex-shrink: 0;
  transition: color .15s ease, background .15s ease;
}
.omzet-mata:hover { color: var(--text); background: rgba(127,127,127,.14); }
.omzet-mata:active { transform: scale(.92); }
.omzet-mata:focus-visible { outline: 2px solid var(--brand, #2563eb); outline-offset: 2px; }
/* Ikon yang tampil ditentukan aria-pressed, jadi keadaan tombol dan gambarnya
   tidak mungkin berbeda. */
.omzet-mata .mata-buka { display: none; }
.omzet-mata[aria-pressed="true"] .mata-buka { display: block; }
.omzet-mata[aria-pressed="true"] .mata-tutup { display: none; }
/* Angka tersensor: sedikit direnggangkan supaya titiknya terbaca sebagai
   "disembunyikan", bukan sebagai angka yang gagal dimuat. */
.nilai-tersensor { letter-spacing: .08em; }

/* ── Inventory ─────────────────────────────────────────────── */

.inv-pengantar {
  font-size: .82rem;
  line-height: 1.55;
  color: var(--text-3);
  margin-bottom: var(--sp-4);
}

.inv-kartu-kaki {
  font-size: .68rem;
  line-height: 1.35;
  color: var(--text-3);
  margin-top: 2px;
}

.inv-aksi {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: var(--sp-4) 0 var(--sp-5);
}
.inv-aksi .btn { flex: 1 1 auto; min-width: 118px; }
.inv-aksi[hidden] { display: none; }

.inv-ringkas {
  font-size: .72rem;
  color: var(--text-3);
}

/* Kantong Saya di beranda: judulnya tombol buka-tutup (10 Okt 2026). Tombolnya
   melebar memenuhi baris judul, dan bantalan atas-bawahnya ditarik balik
   margin negatif: sasaran jarinya jadi 40px lebih tanpa menggeser jarak
   antarbagian beranda, yang dipatok .section-header. */
.kantong-judul { flex: 1; min-width: 0; }
.kantong-tombol {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 10px 0;
  margin: -10px 0;
  border: none;
  background: none;
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
.kantong-tombol:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.kantong-panah {
  flex-shrink: 0;
  color: var(--text-3);
  transition: transform var(--dur-2) var(--ease-standar);
}
.kantong-tombol[aria-expanded="true"] .kantong-panah { transform: rotate(180deg); }

.inv-tabel th.ka,
.inv-tabel td.ka { text-align: right; }
.inv-tabel td { vertical-align: top; }

.inv-sub {
  font-size: .7rem;
  line-height: 1.4;
  color: var(--text-3);
  margin-top: 2px;
  word-break: break-word;
}

/* Angka minus berarti barang keluar tanpa pernah dibukukan masuk. Diberi
   warna kesalahan supaya tertangkap mata sebelum dipakai mengambil keputusan. */
.inv-tabel td.negatif { color: var(--error); font-weight: 700; }

.inv-catatan-kaki {
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--border);
  font-size: .7rem;
  line-height: 1.5;
  color: var(--text-3);
}

.inv-alert {
  margin: var(--sp-3) 0;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--error-soft);
  color: var(--error);
  font-size: .74rem;
  line-height: 1.5;
}

/* Penanda kategori di tabel barang: kecil, tidak ikut menarik perhatian dari
   angkanya, tapi cukup untuk membedakan kupon dari banner. */
.inv-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--brand-soft);
  color: var(--brand-teks);
  font-size: .62rem;
  font-weight: 700;
  vertical-align: middle;
}
.inv-tag.mati { background: var(--error-soft); color: var(--error); }

/* Satu dokumen serah terima. */
.inv-dok {
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.inv-dok-kepala {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  font-size: .85rem;
}
.inv-dok-tgl { font-size: .7rem; color: var(--text-3); }
.inv-dok-aksi { margin-top: var(--sp-2); }

/* Satu baris kartu stok. */
.inv-mutasi {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: 6px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  font-size: .8rem;
}
.inv-mutasi-kiri { min-width: 0; flex: 1; }
.inv-mutasi-qty {
  font-family: var(--font-mono);
  font-size: .95rem;
  font-weight: 750;
  white-space: nowrap;
}
.inv-mutasi-qty.ok      { color: var(--ok); }
.inv-mutasi-qty.brand   { color: var(--brand-teks); }
.inv-mutasi-qty.bahaya  { color: var(--error); }
.inv-mutasi-qty.netral  { color: var(--text-2); }

.inv-bantuan {
  font-size: .72rem;
  line-height: 1.5;
  color: var(--text-3);
  margin: 6px 0 var(--sp-3);
}

/* Satu baris barang + kotak jumlah di sheet serah terima. */
.inv-baris-input {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}
.inv-baris-input > span { font-size: .85rem; font-weight: 600; }
.inv-baris-input .input { width: 100px; text-align: right; }

.kosong {
  padding: var(--sp-4);
  text-align: center;
  font-size: .78rem;
  color: var(--text-3);
}


/* ============================================================
   26. TATA LETAK DESKTOP
   Berlaku mulai 1024px ke atas. Di bawah ambang itu tidak ada satu
   properti pun di blok ini yang menyala, jadi tampilan HP persis
   seperti sebelum blok ini ditambahkan.

   Ambang 1024px yang sama dibaca dua tempat di JS:
     js/motion.js — arah pil penanda + gerbang dock-menyingkir
     js/app.js    — Tab memindahkan menu
   Kalau angka di sini diubah, ubah keduanya juga.

   Bentuknya: dock nav yang di HP mengambang di bawah, di sini
   diberdirikan jadi kolom melayang di kiri. Tidak ada markup baru —
   elemen yang sama, arahnya saja yang berganti — jadi terapkanRole()
   tetap menyembunyikan tab yang bukan hak akun tanpa tahu bedanya.
   ============================================================ */
@media (min-width: 1024px) {

  :root {
    /* Sidebar selebar ini muat menampung ikon dan labelnya berdampingan;
       di 108px keduanya harus bertumpuk dan separuh lebarnya jadi ruang
       kosong di kiri-kanan teks. */
    --sidebar-w:    184px;
    --sidebar-sela:  16px;
    /* Tinggi topbar dipatok, bukan dibiarkan mengikuti isi: sidebar
       bertambat di bawahnya (top: --topbar-h), dan angka yang menebak-nebak
       tinggi header akan menyisakan celah atau menumpuk begitu ukuran logo
       diubah sedikit saja. */
    --topbar-h:     100px;
    --isi-maks:    1240px;
    --isi-maks-dasbor: 1560px;
  }

  /* ---------- 26.1b Kepala aplikasi ----------
     Logo dan namanya dibesarkan di layar lebar. Ukuran HP-nya dibuat untuk
     bilah setinggi 56px yang harus berbagi tempat dengan tiga tombol; di
     monitor, blok sekecil itu tenggelam di sudut. */
  .topbar {
    min-height: var(--topbar-h);
    padding-top: 0;
    padding-bottom: 0;
  }

  .topbar-logo { gap: 16px; }

  /* Atribut width/height di <img> tetap 34 supaya tata letak HP tidak
     bergeser; yang menang di sini CSS. */
  .topbar-logo img { width: 60px; height: 60px; }

  .topbar-title { font-size: 1.9rem; letter-spacing: .1em; }
  .topbar-subtitle { font-size: .88rem; letter-spacing: .06em; }

  /* ---------- 26.1 Kerangka ----------
     Sidebar melayang (position: fixed) jadi dia tidak ikut terdorong
     padding ini. Yang terdorong topbar dan seluruh halaman sekaligus,
     karena keduanya sama-sama anak .app-shell. */
  .app-shell {
    padding-left: calc(var(--sidebar-w) + var(--sidebar-sela) * 2);
  }

  .topbar { padding-right: var(--sp-5); }

  /* ---------- 26.2 Sidebar ----------
     Dock yang sama, diputar jadi kolom dan ditambatkan ke tepi kiri
     setinggi tengah layar. Kacanya, bayangannya, dan radiusnya
     diwarisi apa adanya dari aturan .bottom-nav di atas. */
  /* Sidebar berdiri penuh dari bawah topbar sampai dasar layar, bukan pil
     mengambang setinggi isinya. Latarnya azur — biru yang sama dengan ekor
     logo — jadi kolom kiri terbaca sebagai satu bidang navigasi, bukan
     kotak kaca yang melayang di atas halaman putih. */
  .bottom-nav {
    top: calc(var(--topbar-h) + var(--sidebar-sela));
    bottom: var(--sidebar-sela);
    left: var(--sidebar-sela);
    transform: none;
    width: var(--sidebar-w);
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    padding: 14px 12px;
    background: linear-gradient(180deg, var(--azur-600) 0%, var(--azur-800) 100%);
    /* Kaca dimatikan: latar solid di atas backdrop-filter cuma membakar GPU
       untuk efek yang tidak kelihatan sama sekali. */
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-color: rgba(255, 255, 255, .14);
    box-shadow: var(--shadow-brand);
  }

  /* Di HP dock menyingkir saat halaman digulir ke bawah supaya daftar
     titik terbaca utuh. Sidebar tidak pernah menutupi apa pun, jadi dia
     tidak boleh ikut pergi. motion.js sudah tidak memasang kelas ini di
     lebar sini, dan aturan ini jaring pengamannya kalau JS gagal muat. */
  .bottom-nav.tersembunyi {
    transform: none;
    opacity: 1;
    pointer-events: auto;
  }

  /* Ikon dan label berdampingan — bentuk yang lazim untuk sidebar, dan yang
     membuat lebar 184px terpakai alih-alih jadi ruang kosong. */
  .nav-item {
    flex: 0 0 auto;
    flex-direction: row;
    justify-content: flex-start;
    padding: 13px 14px;
    gap: 12px;
    font-size: .84rem;
    color: rgba(255, 255, 255, .74);
  }

  .nav-item.active { color: #fff; }
  .nav-item:focus-visible { outline-color: rgba(255, 255, 255, .9); }
  /* Ikon tidak lagi naik-turun saat aktif: di baris mendatar, geseran tegak
     itu membuat label di sebelahnya terlihat miring. */
  .nav-item.active svg { transform: scale(1.08); }
  .nav-item svg { flex: none; }

  /* Pil penanda jadi sorotan terang di atas biru; --brand-soft yang dipakai
     di HP nyaris tak terlihat di latar segelap ini. */
  .nav-indikator {
    background: rgba(255, 255, 255, .18);
    border-color: rgba(255, 255, 255, .30);
  }

  /* Pil penanda ikut berdiri. Keempat variabelnya diisi motion.js:
     di arah tegak dia mengukur offsetTop/offsetHeight, bukan
     offsetLeft/offsetWidth seperti waktu jadi dock. */
  .nav-indikator {
    top: 0;
    left: 0;
    width: var(--ind-w, 0px);
    height: var(--ind-h, 0px);
    transform: translate3d(var(--ind-x, 0px), var(--ind-y, 0px), 0);
  }

  /* ---------- 26.3 Isi halaman ----------
     Menimpa blok 768px di atas, yang mengurung isi di 560px — lebar
     yang benar untuk tablet, terlalu sempit untuk layar PC. */
  .page-content {
    max-width: var(--isi-maks);
    margin: 0 auto;
    padding: var(--sp-5);
    padding-bottom: var(--sp-6);
  }

  /* ---------- 26.4 Dashboard dua kolom ----------
     Kolom kiri: kartu statistik, kantong, lalu daftar titik. Kolom kanan:
     peta, berdiri penuh dari atas sampai dasar layar. Urutan di HTML tetap
     urutan HP (statistik, peta, baru daftar); yang menata posisinya
     grid-template-areas, jadi markup tidak perlu disentuh dan urutan
     bacanya di HP tidak berubah.

     Dulu kartu statistik membentang selebar halaman di atas kedua kolom
     dan petanya cuma kebagian sisa di bawahnya: di monitor 1920×1080
     (viewport Chrome ±900px) jadi pita 1088×489, di laptop 1366×768
     malah 678×239 — terlalu gepeng untuk dibaca sebagai peta. Kartu
     statistik tidak butuh lebar halaman; peta butuh tinggi.

     Baris pertama dipatok setinggi judul peta, bukan auto. Kartu
     statistik membentang dua baris (1–2); kalau baris 1 auto, algoritme
     grid membagi tinggi kartu itu ke kedua baris dan petanya ikut turun
     sejauh separuh tinggi kartu. Track tetap tidak ikut dibagi.

     Tinggi dikunci ke tinggi layar supaya yang bergulir cuma daftar
     titiknya — peta dan kartu statistik tetap di tempat. */
  #page-dashboard { overflow: hidden; }

  #page-dashboard .page-content {
    --judul-peta-h: 2.75rem;
    max-width: var(--isi-maks-dasbor);
    height: 100%;
    display: grid;
    grid-template-columns: minmax(300px, 400px) minmax(0, 1fr);
    grid-template-rows: var(--judul-peta-h) auto auto auto auto minmax(0, 1fr);
    grid-template-areas:
      "statistik    judul-peta"
      "statistik    peta"
      "kantong      peta"
      "judul-daftar peta"
      "aksi         peta"
      "daftar       peta";
    column-gap: var(--sp-5);
  }

  #page-dashboard .stats-row    { grid-area: statistik; }
  #page-dashboard #kantong-saya { grid-area: kantong; }
  #page-dashboard #head-daftar  { grid-area: judul-daftar; }
  /* Dulu tanpa area dan ditaruh grid ke sel kosong pertama yang
     kebetulan ada di kolom kiri. Sekarang dipatok, supaya bar "hapus
     titik diambil" tidak pernah nyasar ke kolom peta. */
  #page-dashboard #aksi-diambil { grid-area: aksi; }

  #page-dashboard #head-peta {
    grid-area: judul-peta;
    align-self: start;
    margin-bottom: 0;
  }

  #page-dashboard .map-wrap.admin-map {
    grid-area: peta;
    height: 100%;
    min-height: 0;
    margin-bottom: 0;
  }

  /* min-height: 0 wajib. Tanpa itu anak grid menolak menyusut di bawah
     tinggi isinya, daftar 200 titik mendorong baris grid melewati layar,
     dan yang bergulir jadi seluruh halaman — bukan daftarnya saja. */
  #page-dashboard #list-titik {
    grid-area: daftar;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 6px;
  }

  #page-dashboard #list-titik::-webkit-scrollbar { width: 6px; }
  #page-dashboard #list-titik::-webkit-scrollbar-track { background: transparent; }
  #page-dashboard #list-titik::-webkit-scrollbar-thumb {
    background: var(--border-kuat);
    border-radius: var(--r-full);
  }

  /* ---------- 26.5 Tombol tambah ----------
     Dock sudah tidak ada di bawah, jadi FAB turun ke sudut layar. */
  .fab {
    right: var(--sp-5);
    bottom: var(--sp-5);
  }

  /* Pasangan .bottom-nav.tersembunyi di atas. :not(.hidden) menjaga
     supaya FAB tetap menghilang di halaman selain dashboard. */
  .fab.tersembunyi:not(.hidden) {
    transform: none;
    opacity: 1;
    pointer-events: auto;
  }

  /* ---------- 26.6 Bayangan sheet tertutup ----------
     Sheet parkir tepat di bawah tepi layar (translateY(100%)), tapi
     bayangannya menyebar ke ATAS dari tepi itu — segaris kabut kelabu
     di dasar layar padahal tidak ada apa-apa di sana. Di HP tidak
     kelihatan karena tertutup dock yang mengambang di depannya; begitu
     dock pindah ke kiri, kabutnya telanjang. Bayangan cuma berguna
     waktu sheet-nya terbuka. */
  .sheet:not(.open) { box-shadow: none; }
}

/* Tombol pembuka laporan cetak: selebar bilah saringan di atasnya, karena
   ini pintu ke satu-satunya bentuk laporan yang dibawa keluar aplikasi. */
.rpt-cetak { width: 100%; margin-bottom: var(--sp-4); }

/* ============================================================
   LAPORAN CETAK

   Lembar rekap satu rentang tanggal yang berakhir jadi PDF lewat dialog
   cetak browser. Warnanya sengaja ditulis tetap, bukan lewat token tema:
   kertas ini dibaca orang lain di luar panel dan dicetak di atas kertas
   putih, jadi tema gelap yang sedang dipakai admin tidak boleh ikut.
   ============================================================ */
.cetak-overlay {
  position: fixed;
  inset: 0;
  z-index: 4000;
  overflow-y: auto;
  background: #eef1f6;
  padding: 0 0 40px;
}

/* Bilah kendali. Tidak ikut tercetak — lihat blok @media print di bawah. */
.cetak-bar {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  background: var(--surface, #fff);
  border-bottom: 1px solid var(--border, #d9dee8);
  box-shadow: 0 2px 10px rgba(15, 23, 42, .08);
}

.cetak-bar-atas {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.cetak-preset { display: flex; flex-wrap: wrap; gap: 6px; }

.cetak-chip {
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--border, #d9dee8);
  background: transparent;
  color: inherit;
  font-size: .8rem;
  cursor: pointer;
}
.cetak-chip.aktif {
  background: #2f5fd0;
  border-color: #2f5fd0;
  color: #fff;
}

.cetak-baris { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }

.cetak-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: .72rem;
  color: var(--text-2, #64748b);
}

/* Kertas. Lebarnya kira-kira A4 dikurangi margin cetak, supaya yang terlihat
   di layar sudah sama susunannya dengan yang keluar dari printer. */
.cetak-kertas {
  max-width: 820px;
  margin: 18px auto;
  padding: 26px 28px 32px;
  background: #fff;
  color: #1f2430;
  border-radius: 6px;
  box-shadow: 0 6px 24px rgba(15, 23, 42, .12);
  font-size: 12px;
  line-height: 1.45;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

@media (max-width: 640px) {
  .cetak-kertas { margin: 12px 8px; padding: 16px 14px 22px; border-radius: 4px; }
}

.cetak-tunggu { padding: 40px 0; text-align: center; color: #6b7280; }

/* ── Kepala ── */
.cetak-kepala {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  border-bottom: 2px solid #2f5fd0;
  padding-bottom: 10px;
  margin-bottom: 14px;
}
.cetak-judul { font-size: 19px; font-weight: 700; margin: 0 0 3px; letter-spacing: -.2px; }
.cetak-subjudul { color: #6b7280; font-size: 11px; }
.cetak-meta { text-align: right; color: #6b7280; font-size: 10px; line-height: 1.6; white-space: nowrap; }
.cetak-tanda {
  display: inline-block;
  margin-top: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #eaf0fd;
  color: #2f5fd0;
  font-size: 10px;
  font-weight: 600;
}

/* ── Kartu ringkasan ── */
.cetak-kartu-baris {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin-bottom: 18px;
}
@media (max-width: 640px) {
  .cetak-kartu-baris { grid-template-columns: repeat(2, 1fr); }
}
.cetak-kartu {
  border: 1px solid #e3e8f0;
  border-radius: 6px;
  padding: 9px 11px;
}
.cetak-kartu-label { font-size: 10px; color: #6b7280; margin-bottom: 3px; }
.cetak-kartu-nilai { font-size: 17px; font-weight: 700; letter-spacing: -.3px; }
.cetak-kartu-sub { font-size: 9.5px; color: #9aa3b2; margin-top: 2px; }

.cetak-bagian { margin-bottom: 18px; }
.cetak-bagian-judul {
  font-size: 12.5px;
  font-weight: 700;
  border-bottom: 1px solid #e3e8f0;
  padding-bottom: 5px;
  margin-bottom: 10px;
}

/* ── Grafik batang ── */
.cetak-chart { padding-left: 34px; position: relative; }
.cetak-chart-plot { position: relative; height: 190px; }
.cetak-chart-grid { position: absolute; inset: 0; }
.cetak-chart-grid i {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px solid #eef1f6;
}
.cetak-chart-grid i b {
  position: absolute;
  left: -34px;
  top: -6px;
  width: 30px;
  text-align: right;
  font-weight: 400;
  font-size: 8.5px;
  color: #9aa3b2;
}
.cetak-chart-bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  gap: 2px;
}
.cetak-kolom { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; }
.cetak-kolom-nilai { font-size: 8px; color: #4b5563; text-align: center; margin-bottom: 2px; min-height: 10px; }
.cetak-bar-batang {
  background: #2f5fd0;
  border-radius: 2px 2px 0 0;
  width: 74%;
  max-width: 20px;
  margin: 0 auto;
  min-height: 1px;
}
/* Hari yang masih berjalan belum tutup buku — dipucatkan supaya tidak
   terbaca sebagai hari yang tiba-tiba anjlok. */
.cetak-bar-batang.berjalan { background: #b9caf0; }
.cetak-chart-x { display: flex; gap: 2px; margin-top: 4px; }
.cetak-x-sel { flex: 1 1 0; min-width: 0; text-align: center; font-size: 8px; color: #6b7280; }
.cetak-x-bulan { display: block; font-size: 7.5px; color: #9aa3b2; }
.cetak-chart-ket { margin-top: 8px; font-size: 9.5px; color: #9aa3b2; }

/* ── Tabel ── */
.cetak-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
@media (max-width: 640px) { .cetak-duo { grid-template-columns: 1fr; gap: 14px; } }

table.cetak-tabel { width: 100%; border-collapse: collapse; font-size: 11px; }
table.cetak-tabel th {
  text-align: left;
  font-weight: 500;
  color: #6b7280;
  font-size: 9.5px;
  padding: 5px 6px;
  border-bottom: 1px solid #e3e8f0;
}
table.cetak-tabel td { padding: 5px 6px; border-bottom: 1px solid #f1f4f9; }
table.cetak-tabel td.angka, table.cetak-tabel th.angka { text-align: right; white-space: nowrap; }
table.cetak-tabel td.tengah, table.cetak-tabel th.tengah { text-align: center; }
table.cetak-tabel tr.total td { font-weight: 700; border-top: 1.5px solid #cbd5e1; border-bottom: none; }
table.cetak-tabel td.samar { color: #9aa3b2; }
table.cetak-tabel tr.hari-kosong td { color: #b6bdc9; background: #fafbfd; }
table.cetak-tabel tr.hari-berjalan td { background: #f4f7fe; }
.cetak-minggu-merah { color: #d14343; }

.cetak-tabel-bungkus { overflow-x: auto; }

.cetak-catatan {
  margin-top: 16px;
  padding-top: 10px;
  border-top: 1px solid #e3e8f0;
  font-size: 9.5px;
  color: #6b7280;
  line-height: 1.6;
}
.cetak-catatan b { color: #4b5563; }

.cetak-kosong { padding: 26px 0; text-align: center; color: #9aa3b2; font-size: 11px; }

/* ============================================================
   Cetak. Seluruh panel disembunyikan dan hanya kertasnya yang tersisa;
   `body.cetak-aktif` yang menjaga aturan ini tidak ikut berlaku saat orang
   menekan Ctrl+P di halaman biasa.
   ============================================================ */
@media print {
  @page { size: A4 portrait; margin: 12mm; }

  body.cetak-aktif { background: #fff; }
  body.cetak-aktif > *:not(#cetak-overlay) { display: none !important; }

  body.cetak-aktif #cetak-overlay {
    position: static;
    overflow: visible;
    background: #fff;
    padding: 0;
  }
  body.cetak-aktif .cetak-bar { display: none !important; }
  body.cetak-aktif .cetak-kertas {
    max-width: none;
    margin: 0;
    padding: 0;
    border-radius: 0;
    box-shadow: none;
    font-size: 10.5px;
  }
  body.cetak-aktif .cetak-chart-plot { height: 150px; }
  /* Rincian per hari selalu mulai di halaman baru: tabelnya panjang, dan
     kalau dibiarkan menyambung, halaman pertama berakhir dengan potongan
     tabel yang tidak berkepala. */
  body.cetak-aktif .cetak-pisah { break-before: page; page-break-before: always; }
  /* Bagian pendek (per produk, per area, per projek, per minggu) pindah utuh
     ke halaman berikutnya kalau sisa halaman tidak cukup. Tabel yang kepalanya
     tertinggal di halaman sebelumnya jauh lebih membingungkan daripada satu
     halaman yang berakhir agak lebih cepat. */
  body.cetak-aktif .cetak-bagian:not(.cetak-pisah) { break-inside: avoid; }
  body.cetak-aktif table.cetak-tabel { break-inside: auto; }
  body.cetak-aktif table.cetak-tabel tr { break-inside: avoid; }
  body.cetak-aktif thead { display: table-header-group; }
  body.cetak-aktif .cetak-tabel-bungkus { overflow: visible; }
}


/* ===========================================================================
   LAYAR CHAT — percakapan WhatsApp nomor Meteor 1
   (lahir 11 Okt 2026; dirupakan seperti WhatsApp 12 Okt 2026)

   Dibuat untuk HP lebih dulu: ruang percakapan mengisi tinggi layar yang
   tersisa, gelembungnya yang bergulir, dan kotak tulis menempel di bawah di
   atas dock. Dock-nya sendiri tingginya ~64px + safe-area, jadi ruang tulis
   diberi jarak sebesar itu — tanpa itu tombol Kirim tertutup dock dan tidak
   bisa ditekan sama sekali di iPhone.

   TIGA HAL YANG MEMBEDAKANNYA DARI VERSI PERTAMA:

   1. Warnanya memakai token --chat-* (theme.css), bukan --surface/--brand.
      Dulu gelembung MASUK pakai --surface-2 dan KELUAR pakai --brand-soft:
      dua-duanya nyaris seputih kartu di belakangnya, jadi arah pembicaraan
      cuma bisa ditebak dari posisi kiri/kanan. Sekarang dindingnya gelap
      sedikit, masuk putih, keluar hijau WhatsApp — beda isian, bukan beda
      posisi saja.

   2. Teksnya BISA DISELEKSI. body { user-select: none } dipasang supaya
      aplikasi tidak terasa seperti halaman web saat diketuk-ketuk, tapi
      efek sampingnya isi pesan pembeli (nomor rekening, alamat, kode) tidak
      bisa disalin sama sekali. Di dalam gelembung aturan itu dibalik.

   3. Ukuran hurufnya naik di layar lebar. 0,85rem enak di HP, tapi di monitor
      27" gelembungnya jadi pita tipis yang harus didekati untuk dibaca.
   =========================================================================== */

/* ── Daftar percakapan ─────────────────────────────────────── */
/* Satu kartu berisi baris-baris rata, dipisah garis — seperti daftar chat
   WhatsApp, bukan tumpukan kartu terpisah. */
#chat-daftar {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
#chat-daftar .form-hint { padding: var(--sp-3); margin: 0; }

.chat-baris {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: 10px var(--sp-3);
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--divider);
  border-radius: 0;
  text-align: left;
  cursor: pointer;
  transition: background var(--dur-1) var(--ease-standar);
}
.chat-baris:last-child { border-bottom: none; }
.chat-baris:hover  { background: var(--surface-2); }
.chat-baris:active { background: var(--surface-alt); }
/* Yang belum dibaca ditebalkan + dilencanai, tidak diwarnai barisnya:
   latar berwarna di daftar panjang membuat mata mencari warna, padahal yang
   dicari jumlah pesannya. */
.chat-baris[data-belum="1"] .chat-baris-nama > span:first-child { font-weight: 700; }
.chat-baris[data-belum="1"] .chat-baris-waktu { color: var(--chat-kirim); font-weight: 600; }
.chat-baris[data-belum="1"] .chat-baris-cuplik { color: var(--text); }

.chat-avatar {
  position: relative;          /* panggung untuk foto profil di atas inisial */
  flex-shrink: 0;
  width: 48px; height: 48px;
  display: grid; place-items: center;
  overflow: hidden;
  border-radius: var(--r-full);
  background: var(--brand-soft);
  color: var(--brand-teks);
  font-weight: 700; font-size: 1rem;
}
/* Foto profil WhatsApp pemilik nomor, dipasang pasangFotoProfil(). Menumpuk
   DI ATAS inisial, tidak menggantinya: nomor tanpa foto (atau yang gambarnya
   gagal dimuat) tetap punya bulatan berinisial, bukan bulatan kosong.
   object-fit: cover supaya foto persegi panjang tidak gepeng. */
.chat-avatar-foto {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  border-radius: inherit;
  background: var(--surface-2);
}
.chat-baris-isi { flex: 1; min-width: 0; }
.chat-baris-nama {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2);
  font-weight: 600; font-size: .95rem; color: var(--text);
}
.chat-baris-nama > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-baris-waktu { font-size: .7rem; color: var(--text-3); font-weight: 400; white-space: nowrap; }
/* Satu baris saja, dipotong: pesan panjang yang dibiarkan membungkus membuat
   tinggi tiap baris daftar berbeda-beda dan daftarnya susah dibaca cepat. */
.chat-baris-cuplik {
  display: block; margin-top: 2px;
  font-size: .82rem; color: var(--text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-baris-lencana {
  flex-shrink: 0;
  min-width: 20px; height: 20px; padding: 0 6px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--chat-lencana); color: var(--chat-lencana-tek);
  font-size: .68rem; font-weight: 700;
}

/* ── Ruang percakapan ──────────────────────────────────────── */
/* Satu panel utuh: kepala menempel di atas, dinding percakapan di tengah,
   kotak tulis di bawah — tiga-tiganya satu bingkai seperti jendela WhatsApp.
   dvh dipakai sesudah vh: bilah alamat browser HP menyusut saat digulir, dan
   100vh membuat kotak tulis tersembunyi di baliknya. */
#chat-ruang {
  display: flex; flex-direction: column;
  height: calc(100vh - 210px);
  height: calc(100dvh - 210px);
  min-height: 320px;
}

.chat-kepala {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 8px 10px;
  background: var(--chat-bar);
  border: 1px solid var(--chat-bar-garis);
  border-bottom: none;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.chat-kembali {
  flex-shrink: 0; width: 34px; height: 34px;
  display: grid; place-items: center;
  background: transparent; border: none; border-radius: var(--r-full);
  color: var(--text); cursor: pointer;
}
.chat-kembali:hover { background: var(--surface-2); }
/* Avatar di kepala — penanda cepat percakapan siapa yang sedang dibuka. */
.chat-kepala-avatar { width: 38px; height: 38px; font-size: .88rem; }
.chat-kepala-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.chat-kepala-nama {
  font-weight: 600; font-size: .95rem; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-kepala-ket { font-size: .7rem; color: var(--text-2); }

.chat-gelembung-wadah {
  flex: 1; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--sp-3) var(--sp-2) var(--sp-3);
  /* Dinding bertitik halus menggantikan wallpaper WhatsApp. Digambar CSS,
     bukan berkas gambar: satu permintaan jaringan lagi untuk hiasan tidak
     sepadan, dan ini tetap hidup saat luring. */
  background-color: var(--chat-latar);
  background-image:
    radial-gradient(var(--chat-pola) 1px, transparent 1px),
    radial-gradient(var(--chat-pola) 1px, transparent 1px);
  background-size: 26px 26px, 26px 26px;
  background-position: 0 0, 13px 13px;
  border-left: 1px solid var(--chat-bar-garis);
  border-right: 1px solid var(--chat-bar-garis);
}

/* ── Gelembung ─────────────────────────────────────────────── */
.chat-pesan {
  --gel: var(--chat-masuk-bg);
  position: relative;
  max-width: 85%;
  padding: 6px 9px 7px;
  background: var(--gel);
  color: var(--chat-masuk-teks);
  border-radius: 9px;
  box-shadow: 0 1px .5px var(--chat-ekor-bayang);
  font-size: .9rem;
  line-height: 1.45;
}
/* Masuk di kiri, keluar di kanan — arah yang sama dengan app WhatsApp,
   supaya tidak perlu dipelajari lagi. Warnanya pun: putih vs hijau. */
.chat-pesan[data-arah="masuk"]  { align-self: flex-start; }
.chat-pesan[data-arah="keluar"] {
  align-self: flex-end;
  --gel: var(--chat-keluar-bg);
  color: var(--chat-keluar-teks);
}
/* Balasan robot dibedakan dari balasan manusia: tanpa ini Bos membuka chat
   dan tidak tahu kalimat yang sudah ada itu tulisannya sendiri atau bot.
   Dibedakan lewat ISIAN (krem), bukan garis putus-putus — garis tipis hilang
   begitu layarnya kena matahari. */
.chat-pesan[data-robot="1"] {
  --gel: var(--chat-bot-bg);
  color: var(--chat-bot-teks);
}

/* Ekor cuma di gelembung pertama satu rentetan, menyudut ke atas — bentuk
   WhatsApp sekarang. Pesan beruntun dari orang yang sama menempel rapat
   tanpa ekor, jadi satu rentetan terbaca sebagai satu giliran bicara. */
.chat-pesan[data-ekor="1"] { margin-top: 8px; }
.chat-pesan[data-ekor="1"][data-arah="masuk"]  { border-top-left-radius: 0; }
.chat-pesan[data-ekor="1"][data-arah="keluar"] { border-top-right-radius: 0; }
.chat-pesan[data-ekor="1"]::before {
  content: '';
  position: absolute; top: 0;
  width: 0; height: 0;
  border-top: 9px solid var(--gel);
}
.chat-pesan[data-ekor="1"][data-arah="masuk"]::before  { left: -8px;  border-left: 8px solid transparent; }
.chat-pesan[data-ekor="1"][data-arah="keluar"]::before { right: -8px; border-right: 8px solid transparent; }

/* Jam menempel di ujung baris terakhir selama masih muat — persis WhatsApp.
   Dibuat dengan flex-wrap, bukan float: teks pembeli bisa berisi apa saja
   (tempelan tabel, satu kata 200 huruf) dan float gampang jebol. */
.chat-pesan-baris {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: flex-end;
  column-gap: 10px; row-gap: 1px;
  min-width: 0;
}
.chat-pesan-teks {
  flex: 0 1 auto; min-width: 0;
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.chat-pesan-kaki {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .68rem; line-height: 1.7; white-space: nowrap;
  color: var(--chat-meta);
}
.chat-pesan-centang { flex-shrink: 0; color: var(--chat-centang); }

/* Teks pesan BISA DISELEKSI meski body { user-select: none }. Tanpa blok ini
   nomor rekening atau alamat yang dikirim pembeli harus diketik ulang dari
   layar — cara paling mudah salah. -webkit-touch-callout dihidupkan juga,
   kalau tidak tekan-lama di iOS tidak memunculkan menu Copy. */
.chat-pesan,
.chat-pesan-teks,
.chat-pesan-kaki,
.chat-pesan-suara-nama,
.chat-pesan-tautan,
.chat-hari {
  user-select: text;
  -webkit-user-select: text;
  -webkit-touch-callout: default;
}
.chat-pesan-teks { cursor: text; }
.chat-gelembung-wadah ::selection {
  background: var(--brand); color: var(--on-brand);
}
.chat-gelembung-wadah ::-moz-selection {
  background: var(--brand); color: var(--on-brand);
}

/* Tombol salin di kaki gelembung. Ada DUA jalan menyalin supaya tidak
   bergantung pada satu-satunya: seleksi manual (untuk sepotong) dan tombol
   ini (untuk seluruh isi pesan, sekali ketuk — di HP menyeret pegangan
   seleksi di dalam daftar yang bergulir itu menyiksa). */
.chat-salin-pesan {
  flex-shrink: 0;
  /* Tombolnya sendiri tidak ikut terseleksi: menyeret seleksi melewatinya
     tidak boleh menyelipkan karakter kosong ke papan klip. */
  user-select: none; -webkit-user-select: none;
  width: 22px; height: 22px; margin: -2px -3px -2px 0;
  display: grid; place-items: center;
  background: transparent; border: none; border-radius: var(--r-full);
  color: var(--chat-meta);
  opacity: .55;
  cursor: pointer;
  transition: opacity var(--dur-1) var(--ease-standar), background var(--dur-1) var(--ease-standar);
}
.chat-pesan:hover .chat-salin-pesan { opacity: 1; }
.chat-salin-pesan:hover { background: var(--chat-ekor-bayang); }
.chat-salin-pesan:focus-visible { opacity: 1; outline: 2px solid var(--brand); outline-offset: 1px; }
.chat-salin-pesan:active { transform: scale(.9); }
/* Di layar sentuh tidak ada hover, jadi ikonnya dibuat cukup terlihat sejak
   awal — dan sasaran ketuknya dilebarkan tanpa mengubah ukuran ikon. */
@media (hover: none) {
  .chat-salin-pesan { opacity: .8; width: 26px; height: 26px; }
}

/* Pembatas hari — pil di tengah, seperti "HARI INI" di WhatsApp. */
.chat-hari {
  align-self: center;
  margin: var(--sp-3) 0 var(--sp-1);
  padding: 5px 12px;
  background: var(--chat-pemisah-bg); color: var(--chat-pemisah-tek);
  border-radius: var(--r-full);
  box-shadow: 0 1px .5px var(--chat-ekor-bayang);
  font-size: .66rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase;
}
.chat-hari:first-child { margin-top: 0; }

.chat-pesan img, .chat-pesan video {
  max-width: 100%; max-height: 360px;
  border-radius: 6px; display: block;
  margin-bottom: 3px;
  cursor: zoom-in;
}
.chat-pesan audio { width: 100%; min-width: 180px; margin-bottom: 2px; }
.chat-pesan-tautan { font-size: .82rem; color: var(--brand-teks); }
.chat-pesan[data-arah="keluar"] .chat-pesan-tautan,
.chat-pesan[data-robot="1"] .chat-pesan-tautan { color: inherit; opacity: .85; }

/* Nama rekaman di bawah pemutar suara — keterangan, bukan isi pesan. */
.chat-pesan-suara-nama { margin-top: 2px; font-size: .72rem; opacity: .75; }

.chat-kosong {
  margin: auto; padding: 10px 16px;
  background: var(--chat-pemisah-bg); color: var(--chat-pemisah-tek);
  border-radius: var(--r-full);
  text-align: center; font-size: .78rem; font-weight: 600;
}

/* ── Kotak tulis ───────────────────────────────────────────── */
.chat-tulis {
  display: flex; align-items: flex-end; gap: var(--sp-2);
  padding: 8px 10px;
  background: var(--chat-bar);
  border: 1px solid var(--chat-bar-garis);
  border-top: none;
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  /* Ruang untuk dock melayang di bawah. */
  margin-bottom: calc(76px + env(safe-area-inset-bottom));
}
.chat-tulis textarea {
  flex: 1; resize: none; max-height: 120px;
  padding: 9px 14px;
  background: var(--chat-tulis-bg); color: var(--text);
  border: 1px solid transparent; border-radius: 20px;
  font: inherit; font-size: .9rem; line-height: 1.4;
}
.chat-tulis textarea::placeholder { color: var(--text-3); }
.chat-tulis textarea:focus { outline: none; border-color: var(--brand-border); }
.chat-tombol-lampiran, .chat-tombol-kirim {
  flex-shrink: 0; width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: var(--r-full); cursor: pointer;
  transition: opacity var(--dur-1) var(--ease-standar), background var(--dur-1) var(--ease-standar);
}
.chat-tombol-lampiran { background: transparent; border: none; color: var(--text-2); }
.chat-tombol-lampiran:hover { background: var(--surface-2); }
/* Hijau WhatsApp, bukan biru merek: di layar ini yang dikejar refleks tangan
   yang sudah terbentuk dari app aslinya. */
.chat-tombol-kirim { background: var(--chat-kirim); border: none; color: var(--chat-kirim-teks); }
.chat-tombol-lampiran:disabled, .chat-tombol-kirim:disabled { opacity: .5; cursor: default; }

/* Peringatan "robot sedang diam", di atas kotak tulis supaya terbaca justru
   saat Bos hendak mengetik balasan berikutnya. */
.chat-pegang {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  background: var(--warn-soft, var(--surface-2));
  border: 1px solid var(--chat-bar-garis); border-bottom: none;
  font-size: .74rem; color: var(--text-2);
}

/* ── Layar lebar ───────────────────────────────────────────── */
/* Keluhan di PC: gelembungnya kurus dan tulisannya kecil. Di sini hurufnya
   naik ke 1rem (16px) dan gelembungnya dibatasi lebarnya supaya satu baris
   tidak melintang selayar — mata kehilangan awal baris berikutnya kalau
   terlalu panjang. */
@media (min-width: 720px) {
  /* Di layar lebar dock tidak melayang menutupi apa pun. */
  #chat-ruang {
    height: calc(100vh - 240px);
    height: calc(100dvh - 240px);
  }
  .chat-tulis { margin-bottom: var(--sp-3); }
  .chat-gelembung-wadah { padding: var(--sp-3) var(--sp-4); gap: 3px; }
  .chat-pesan {
    max-width: min(72%, 620px);
    padding: 7px 11px 8px;
    font-size: 1rem; line-height: 1.5;
    border-radius: 10px;
  }
  .chat-pesan-kaki { font-size: .74rem; }
  .chat-salin-pesan { width: 24px; height: 24px; }
  .chat-hari { font-size: .7rem; }
  .chat-tulis textarea { font-size: .95rem; padding: 10px 16px; }
  .chat-kepala-nama { font-size: 1rem; }
  .chat-kepala-ket { font-size: .76rem; }
  .chat-baris-nama { font-size: 1rem; }
  .chat-baris-cuplik { font-size: .86rem; }
}
@media (min-width: 1200px) {
  .chat-pesan { font-size: 1.05rem; max-width: min(64%, 680px); }
  .chat-pesan-kaki { font-size: .76rem; }
}

/* Kepala percakapan: nomor apa adanya + tombol salin di sampingnya.
   Nomornya sengaja monospace — 13 digit berjejer lebih mudah dicocokkan mata
   dengan yang sudah tertempel di Jual Manual. */
.chat-kepala-ket { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.chat-nomor {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .01em;
  user-select: text; -webkit-user-select: text;
}
.chat-salin {
  padding: 1px 8px;
  background: var(--brand-soft);
  color: var(--brand-teks);
  border: 1px solid var(--brand-border);
  border-radius: var(--r-full);
  font-size: .66rem; font-weight: 600; line-height: 1.6;
  cursor: pointer;
}
.chat-salin:active { transform: scale(.96); }
.chat-status-bot { color: var(--text-3); }
