/* ==========================================================================
   Huruf, dilayani sendiri
   --------------------------------------------------------------------------
   Dulu dua berkas gaya diambil dari Google Fonts, dan font ikonnya adalah font
   variabel utuh berukuran 3,97 MB - satu berkas itu saja lebih berat daripada
   seluruh sisa aplikasi digabung. Di HP lama dengan sinyal seadanya, itulah
   yang membuat setiap halaman terasa tersendat.

   Sekarang keduanya dilayani dari server sendiri: hurufnya cuma subset latin,
   dan font ikonnya cuma memuat 60 ikon yang benar-benar dipakai - 8 KB, bukan
   3,97 MB. Sekalian hilang dua sambungan ke domain luar, dan berkasnya ikut
   disimpan pekerja layanan jadi kunjungan berikutnya tidak mengunduh apa pun.

   Kalau nanti ada ikon baru dipakai, font ikonnya harus dibuat ulang - lihat
   bagian "Huruf dan ikon" di README.
   ========================================================================== */
@font-face {
  font-family: "Plus Jakarta Sans";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../font/jakarta-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   Web Laundry - sistem tampilan v3.
   Diturunkan dari berkas desain: biru #E8402A, Plus Jakarta Sans, sudut
   membulat besar, kartu putih di atas latar biru pucat, ikon Material Symbols.
   ========================================================================== */

:root {
  /* --- warna inti --- */
  --primary: #E8402A;
  --on-primary: #ffffff;
  --primary-container: #C43521;
  --on-primary-container: #FFF1EE;
  --primary-fixed: #FDE3DE;
  --primary-fixed-dim: #F6BFB5;
  --on-primary-fixed: #55130A;
  --on-primary-fixed-variant: #A62716;

  --secondary: #576065;
  --on-secondary: #ffffff;
  --secondary-container: #dbe4ea;
  --on-secondary-container: #5d666b;

  --tertiary: #005a82;
  --on-tertiary: #ffffff;
  --tertiary-container: #0074a6;
  --on-tertiary-container: #e4f2ff;
  --tertiary-fixed: #c9e6ff;
  --tertiary-fixed-dim: #89ceff;
  --on-tertiary-fixed: #001e2f;
  --on-tertiary-fixed-variant: #004c6e;

  --error: #ba1a1a;
  --on-error: #ffffff;
  --error-container: #ffdad6;
  --on-error-container: #93000a;

  --background: #FFF8F7;
  --surface: #FFF8F7;
  --s-lowest: #ffffff;
  --s-low: #FDF2F0;
  --s-cont: #FBE9E5;
  --s-high: #F9DFDA;
  --s-highest: #F7D5CE;
  --s-dim: #EFC8C0;
  --on-surface: #221412;
  --on-surface-variant: #5C4B47;
  --outline: #86736E;
  --outline-variant: #D8C3BD;
  --inverse-surface: #40231D;
  --inverse-on-surface: #FBEDEA;
  --wa: #25d366;
  --wa-gelap: #128c7e;

  /* garis tipis, dipakai di mana-mana */
  --garis: rgba(216, 195, 189, .55);
  --garis-tegas: #D8C3BD;

  /* --- jarak --- */
  --base: 4px; --xs: 8px; --sm: 12px; --md: 16px;
  --pad: 20px; --lg: 24px; --xl: 32px;
  --touch: 44px; --touch-lg: 56px;

  /* --- sudut --- */
  --r: 4px; --r-lg: 8px; --r-xl: 12px; --r-2xl: 16px; --r-3xl: 24px; --r-full: 9999px;

  /* --- bayang --- */
  --sh-sm: 0 1px 2px rgba(34, 20, 18, .05), 0 1px 3px rgba(34, 20, 18, .05);
  --sh-md: 0 2px 4px rgba(34, 20, 18, .05), 0 6px 16px -6px rgba(34, 20, 18, .12);
  --sh-lg: 0 4px 8px rgba(34, 20, 18, .06), 0 16px 32px -12px rgba(34, 20, 18, .18);
  --sh-primary: 0 8px 16px rgba(232, 64, 42, .28);

  --font: "Plus Jakarta Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --sidebar-w: 256px;
  --topbar-h: 64px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--background);
  color: var(--on-surface);
  font-family: var(--font);
  font-size: 15px;
  line-height: 22px;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}

/* --- skala huruf, satu keluarga --------------------------------------- */
h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -.01em; }
h1 { font-size: 26px; line-height: 34px; }
h2 { font-size: 17px; line-height: 24px; }
h3 { font-size: 16px; line-height: 24px; }
p { margin: 0 0 var(--sm); }
p:last-child { margin-bottom: 0; }
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

.display { font-size: 40px; line-height: 48px; font-weight: 800; letter-spacing: -.02em; }
.harga-besar { font-size: 26px; line-height: 34px; font-weight: 700; letter-spacing: -.01em; }
.label-md { font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: .01em; }
.label-sm, .mini { font-size: 12px; line-height: 16px; font-weight: 500; }
.kecil { font-size: 14px; line-height: 20px; }
.body-lg { font-size: 16px; line-height: 24px; }
.label-atas {
  font-size: 12px; line-height: 16px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--on-surface-variant);
}

.num, td.num, th.num, .nominal, .kode, input[type="number"], .uang {
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}
.num, td.num, th.num { text-align: right; }
.minus { color: var(--error); }
.samar { color: var(--on-surface-variant); }
.pudar { color: var(--outline); }
.tebal { font-weight: 600; }
.tegas { font-weight: 700; }
.kanan { text-align: right; }
.tengah { text-align: center; }
.nowrap { white-space: nowrap; }
.tumbuh { flex: 1 1 auto; min-width: 0; }
.rapat { flex: 0 0 auto; }
.mb4 { margin-bottom: var(--md); }

/* --- ikon Material Symbols -------------------------------------------- */
/* Ikon adalah <svg> sebaris. Ukurannya sudah ditulis di atribut width/height
   oleh ikon.render(), jadi tidak ada pergeseran tata letak sebelum gaya termuat
   - dan tidak ada nama ligatur yang bisa bocor jadi tulisan kalau ada yang
   gagal. Warnanya ikut warna teks di sekitarnya lewat fill="currentColor". */
.ms {
  display: inline-block; vertical-align: middle;
  flex: 0 0 auto; user-select: none;
}
/* Kelas ukuran dipertahankan supaya aturan lama yang mengecilkan ikon di
   tempat tertentu (.ubin > .ms, .menu-petak .ms, dan seterusnya) tetap
   berlaku: yang diubah lebar dan tingginya, bukan font-size. */
.ms.sm { width: 18px; height: 18px; }
.ms.md { width: 20px; height: 20px; }
.ms.lg { width: 28px; height: 28px; }
.ms.xl { width: 36px; height: 36px; }

/* ==========================================================================
   Kerangka
   ========================================================================== */
.rangka { display: flex; min-height: 100vh; min-height: 100dvh; }

.sidebar {
  width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
  background: var(--s-lowest); border-right: 1px solid var(--garis);
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; height: 100dvh; overflow-y: auto;
  padding: var(--lg) var(--md);
}
.merek { display: block; padding: 0 var(--xs) var(--lg); text-decoration: none; }
.merek b {
  display: block; font-size: 22px; line-height: 28px; font-weight: 800;
  letter-spacing: -.02em; color: var(--primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.merek small { font-size: 14px; font-weight: 600; color: var(--on-surface-variant); }
.merek:hover { text-decoration: none; }

.nav { display: flex; flex-direction: column; gap: var(--base); }
.nav a {
  display: flex; align-items: center; gap: var(--md);
  padding: 12px var(--md); border-radius: var(--r-xl);
  color: var(--on-surface-variant); font-size: 14px; font-weight: 600;
  letter-spacing: .01em; text-decoration: none;
}
.nav a:hover { background: var(--s-low); color: var(--on-surface); text-decoration: none; }
.nav a.aktif {
  background: var(--primary-container); color: var(--on-primary-container);
  font-weight: 700; box-shadow: var(--sh-sm);
}
.nav a.aktif .ms { font-variation-settings: "FILL" 1; }
.nav a .hitung {
  margin-left: auto; font-size: 12px; font-weight: 700;
  background: var(--s-cont); color: var(--on-primary-fixed-variant);
  padding: 2px 8px; border-radius: var(--r-full); font-variant-numeric: tabular-nums;
}
.nav a.aktif .hitung { background: rgba(255,255,255,.22); color: #fff; }
.nav-grup { margin: var(--md) var(--md) var(--base); }

.sidebar-kaki { margin-top: auto; padding-top: var(--md); }
.siapa-kartu {
  display: flex; align-items: center; gap: var(--sm);
  padding: var(--sm); border-radius: var(--r-2xl); background: var(--s-low);
  margin-bottom: var(--xs);
}
.avatar {
  width: 40px; height: 40px; flex: 0 0 40px; border-radius: var(--r-full);
  background: var(--primary-fixed); color: var(--on-primary-fixed);
  display: grid; place-items: center; font-weight: 700; font-size: 16px;
}
.siapa-kartu .nm { font-size: 14px; font-weight: 700; }
.siapa-kartu .pr { font-size: 12px; color: var(--on-surface-variant); }

.panel-utama { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 30;
  min-height: var(--topbar-h);
  background: rgba(248, 249, 255, .88);
  backdrop-filter: saturate(180%) blur(10px);
  border-bottom: 1px solid var(--garis);
  display: flex; align-items: center; gap: var(--md);
  padding: var(--sm) var(--lg);
}
.topbar h1 { font-size: 20px; line-height: 28px; font-weight: 700; }
.topbar .sub { font-size: 14px; color: var(--on-surface-variant); margin-top: 2px; }
.topbar-aksi { display: flex; align-items: center; gap: var(--xs); flex: 0 0 auto; }

.cari-global { position: relative; flex: 0 1 320px; }
.cari-global input[type="search"] {
  height: 48px; padding: 0 var(--md) 0 48px; border-radius: var(--r-full);
  background: var(--s-lowest); box-shadow: var(--sh-sm);
}
.cari-global .ms {
  position: absolute; left: var(--md); top: 50%; transform: translateY(-50%);
  color: var(--outline); pointer-events: none;
}

.isi { padding: var(--lg); max-width: 1340px; width: 100%; }
.isi.lebar { max-width: none; }
.tabbar { display: none; }
.lembar-menu { display: none; }

/* ==========================================================================
   Kartu
   ========================================================================== */
.kartu {
  background: var(--s-lowest); border: 1px solid var(--garis);
  border-radius: var(--r-3xl); box-shadow: var(--sh-sm);
}
.kartu > .kepala {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--xs) var(--sm);
  padding: var(--lg) var(--lg) var(--sm);
}
.kartu > .kepala.bergaris { border-bottom: 1px solid var(--garis); padding-bottom: var(--md); }
.kartu > .badan { padding: var(--lg); }
.kartu > .kepala + .badan { padding-top: var(--xs); }
.kartu > .kaki {
  padding: var(--md) var(--lg); border-top: 1px solid var(--garis);
  background: var(--s-low); border-radius: 0 0 var(--r-3xl) var(--r-3xl);
}
.kartu + .kartu { margin-top: var(--md); }
.kartu-judul { display: flex; align-items: center; justify-content: space-between; gap: var(--sm); margin-bottom: var(--sm); }

/* Bagian yang isinya sudah terkunci karena notanya tersimpan: masih terbaca,
   tapi tidak bisa diubah lagi. pointer-events dipakai supaya tautan (pil
   metode bayar) ikut mati - atribut disabled tidak berlaku untuk <a>. */
.terkunci { pointer-events: none; opacity: .55; }

/* Dua tombol cetak berbagi satu baris sempit di HP. Padding jumbo bawaan
   (2x32px) membuat isi tombol lebih lebar daripada jatah kolomnya di layar
   360px, jadi labelnya terpotong - flex item boleh menyusut, isinya tidak.
   Tombolnya tetap selebar setengah baris; yang dikecilkan cuma paddingnya. */
.baris-simpan .tombol.jumbo { padding-left: var(--xs); padding-right: var(--xs); }

/* Jumlah kolom ditentukan lebar tempat yang benar-benar tersedia, bukan lebar
   jendela. Dulu kolomnya dipatok angka (repeat(4, 1fr)) lalu diturunkan lewat
   media query pada 900px - padahal di atas 900px menu samping muncul dan
   memakan 256px, jadi jendela 920px hanya menyisakan 616px untuk empat kolom.
   Isi ubin tidak muat, meluber keluar kartu, dan halamannya bisa digeser ke
   samping. auto-fit menghitungnya sendiri; min(100%, Npx) menjaga satu kolom
   pun tidak pernah lebih lebar dari wadahnya. */
.kisi { display: grid; gap: var(--md); }
.kisi-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.kisi-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }
.kisi-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
.kisi-sisi {
  grid-template-columns: minmax(0, 1.6fr) minmax(min(100%, 340px), .8fr);
  align-items: start;
}
.tumpuk { display: flex; flex-direction: column; gap: var(--md); }

/* --- angka ringkas ----------------------------------------------------- */
.ringkas {
  background: var(--s-lowest); border: 1px solid var(--garis);
  border-radius: var(--r-3xl); padding: var(--lg); box-shadow: var(--sh-sm);
  display: flex; flex-direction: column; gap: var(--base);
}
.ringkas .atas { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--xs); }
.ringkas .label {
  font-size: 14px; font-weight: 600; color: var(--on-surface-variant);
  min-width: 0; overflow-wrap: break-word;
}
.ringkas .lencet {
  width: 40px; height: 40px; border-radius: var(--r-full); flex: 0 0 40px;
  background: var(--s-cont); color: var(--primary); display: grid; place-items: center;
}
.ringkas .nilai {
  font-size: 25px; line-height: 32px; font-weight: 700; letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
}
.ringkas .catatan { font-size: 12px; color: var(--outline); }
.ringkas.utama { background: var(--primary); border-color: var(--primary); color: #fff; box-shadow: var(--sh-primary); }
.ringkas.utama .label { color: rgba(255,255,255,.85); }
.ringkas.utama .catatan { color: rgba(255,255,255,.7); }
.ringkas.utama .lencet { background: rgba(255,255,255,.18); color: #fff; }
.ringkas.awas { border-color: var(--error-container); }
.ringkas.awas .nilai { color: var(--error); }
.ringkas.awas .lencet { background: var(--error-container); color: var(--on-error-container); }

/* ==========================================================================
   Tombol
   ========================================================================== */
.tombol {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--xs);
  height: var(--touch); padding: 0 var(--lg); border-radius: var(--r-full);
  font-family: var(--font); font-size: 14px; font-weight: 700; letter-spacing: .01em;
  line-height: 1; border: 1px solid var(--garis-tegas);
  background: var(--s-lowest); color: var(--on-surface);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.tombol:hover { background: var(--s-low); border-color: var(--primary-fixed-dim); text-decoration: none; }
.tombol:active { transform: scale(.97); }
.tombol.utama {
  background: var(--primary); border-color: var(--primary); color: var(--on-primary);
  box-shadow: var(--sh-sm);
}
.tombol.utama:hover { background: var(--primary-container); border-color: var(--primary-container); }
/* "Tandai Siap" dibedakan dari tombol tahap lain (putih polos) DAN dari aksi
   terima-uang (merah brand). BIRU dipilih dari kosakata warna yang sudah ada:
   hijau sudah berarti lunas/siap (.lencana.lunas, .s-siap_ambil), merah aksi
   utama & merusak, kuning tertunggak - biru satu-satunya slot informasional
   yang belum dipakai status mana pun, dan tokennya sudah ada di palet.
   Labelnya TIDAK berubah: warna tidak pernah jadi satu-satunya sinyal.
   Kontras teks putih di atas #005a82 = 7,53:1 (AAA untuk teks normal). */
.tombol.siap {
  background: var(--tertiary); border-color: var(--tertiary); color: var(--on-tertiary);
}
.tombol.siap:hover { background: var(--tertiary-container); border-color: var(--tertiary-container); }
.tombol.wadah {
  background: var(--primary-container); border-color: transparent; color: var(--on-primary-container);
}
.tombol.wadah:hover { background: var(--primary); color: #fff; }
.tombol.lembut { background: var(--s-cont); border-color: transparent; color: var(--on-primary-fixed-variant); }
.tombol.lembut:hover { background: var(--s-high); }
.tombol.bahaya { color: var(--error); border-color: var(--error-container); background: var(--s-lowest); }
.tombol.bahaya:hover { background: var(--error-container); }
.tombol.hantu { background: transparent; border-color: transparent; }
.tombol.hantu:hover { background: var(--s-low); }
.tombol.kecil { height: 36px; padding: 0 var(--md); font-size: 13px; gap: 6px; }
.tombol.besar { height: var(--touch-lg); padding: 0 var(--xl); font-size: 16px; border-radius: var(--r-2xl); }
.tombol.jumbo {
  height: 64px; padding: 0 var(--xl); font-size: 20px; font-weight: 700;
  border-radius: var(--r-2xl); box-shadow: var(--sh-md);
}
.tombol.lebar { width: 100%; }
.tombol[disabled], .tombol.mati { opacity: .45; cursor: not-allowed; box-shadow: none; }
.tombol[disabled]:active { transform: none; }
.tombol-baris { display: flex; flex-wrap: wrap; gap: var(--xs); }

.tombol-bulat {
  width: 40px; height: 40px; border-radius: var(--r-full); flex: 0 0 40px;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--garis); background: var(--s-lowest); color: var(--on-surface-variant);
}
.tombol-bulat:hover { background: var(--s-cont); color: var(--on-surface); }
.tombol-bulat.bahaya:hover { background: var(--error-container); color: var(--on-error-container); }

/* ==========================================================================
   Formulir
   ========================================================================== */
label.bidang { display: block; margin-bottom: var(--md); }
label.bidang > span, .bidang-label {
  display: block; font-size: 14px; font-weight: 600; color: var(--on-surface-variant);
  margin-bottom: 6px;
}
input[type="text"], input[type="password"], input[type="number"], input[type="date"],
input[type="search"], input[type="tel"], select, textarea {
  width: 100%; height: 48px; padding: 0 var(--md);
  border: 1px solid var(--garis-tegas); border-radius: var(--r-xl);
  background: var(--s-lowest); color: var(--on-surface);
  font-family: var(--font); font-size: 16px;
}
select {
  appearance: none; padding-right: 40px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23737686' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}
textarea { height: auto; min-height: 96px; padding: var(--sm) var(--md); resize: vertical; border-radius: var(--r-2xl); }
input::placeholder, textarea::placeholder { color: var(--outline-variant); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(0, 74, 198, .18);
}
a:focus-visible, button:focus-visible, summary:focus-visible, .ubin:focus-visible {
  outline: 3px solid rgba(0, 74, 198, .45); outline-offset: 2px;
}

.petunjuk { font-size: 13px; line-height: 18px; color: var(--outline); margin-top: 6px; }
.baris { display: flex; flex-wrap: wrap; gap: var(--sm); align-items: flex-end; }
/* min-width bawaan item flex adalah auto, artinya ia menolak menyusut lebih
   sempit dari isinya. Isian tanggal punya lebar alami yang cukup besar, jadi
   tanpa min-width: 0 sepasang "Dari tanggal" dan "Sampai tanggal" mendorong
   dirinya keluar dari garis kartu. */
.baris > * { flex: 1 1 170px; min-width: 0; }
.baris > .rapat { flex: 0 0 auto; }
/* Isian yang wajib punya barisnya sendiri, tidak ikut berdesakan dengan
   tetangganya waktu lebarnya cukup untuk berbagi baris. */
.baris > .barisnya-sendiri { flex: 1 1 100%; }
.baris.rata { align-items: center; }
.centang { display: flex; align-items: center; gap: var(--sm); font-size: 16px; font-weight: 600; cursor: pointer; }
.centang input { width: 20px; height: 20px; flex: 0 0 20px; accent-color: var(--primary); margin: 0; }

fieldset { border: 1px solid var(--garis); border-radius: var(--r-2xl); padding: var(--md) var(--lg); margin: 0 0 var(--md); }
legend { font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--on-surface-variant); padding: 0 var(--xs); }

/* ==========================================================================
   Tabel
   ========================================================================== */
.tabel-geser { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Tabel di dalam wadah bergulir tidak boleh diperas sampai isinya terpotong.
   Kalau tidak muat, biar wadahnya yang digulir menyamping. */
.tabel-geser > table { min-width: max-content; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th {
  text-align: left; font-size: 12px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--on-surface-variant);
  padding: var(--sm) var(--md); border-bottom: 1px solid var(--garis-tegas);
  white-space: nowrap; background: var(--s-low);
}
td { padding: var(--sm) var(--md); border-bottom: 1px solid var(--garis); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--s-low); }
tfoot td { border-bottom: 0; border-top: 1px solid var(--garis-tegas); }
.kartu > .tabel-geser th:first-child, .kartu > .tabel-geser td:first-child { padding-left: var(--lg); }
.kartu > .tabel-geser th:last-child, .kartu > .tabel-geser td:last-child { padding-right: var(--lg); }
.kartu > .tabel-geser:last-child table tbody tr:last-child td { border-bottom: 0; }
.kode { font-weight: 700; font-variant-numeric: tabular-nums; }
a.kode { color: var(--primary); }

/* ==========================================================================
   Lencana tahap
   ========================================================================== */
.lencana {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 12px;
  border-radius: var(--r-full); font-size: 11px; font-weight: 700;
  white-space: nowrap; border: 1px solid transparent; line-height: 16px;
}
.s-terima      { background: var(--primary-fixed);  color: var(--on-primary-fixed-variant); }
.s-cuci        { background: #E3EDFA;                color: #1C5F9E; }
.s-pengering   { background: #DFF2F2;                color: #116B6B; }
.s-setrika     { background: #FAF0DC;                color: #8A6410; }
.s-packing     { background: #EFEAFA;                color: #543C9C; }
.s-siap_ambil  { background: #1D7A34;                color: #fff; }
.s-diambil     { background: var(--s-cont);          color: #6B5A55; }  /* 5.56:1; --outline lama cuma 3.81:1 */
.s-batal       { background: var(--error-container); color: var(--on-error-container); }
.lencana.lunas { background: #E5F4E8;                color: #1D5E28; }
/* Tanda ambil per layanan di detail order. Pembedanya BENTUK ikon (centang vs
   jam) + KATA, warna cuma penguat - buta warna merah-hijau butuh pembeda
   bentuk, dan "sudah" dapat isian penuh sedangkan "belum" bergaris putus.
   flex-wrap supaya nominalnya turun baris, bukan terpotong, di layar 360px;
   jaraknya margin, BUKAN gap flex (browser HP kasir belum mengenal gap). */
.tanda-ambil {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  margin: 0 0 8px; padding: 5px 10px; border-radius: var(--r-full);
  font-size: 13px; font-weight: 700; line-height: 18px;
}
.tanda-ambil .kp { display: flex; align-items: center; min-width: 0; }
.tanda-ambil .kp svg { flex: 0 0 auto; margin-right: 6px; }
/* margin-left, bukan gap: tanpa ini "Belum diambil" dan nominalnya menempel
   begitu barisnya penuh (justify-content saja tidak menyisakan jarak). */
.tanda-ambil .wk { font-weight: 400; min-width: 0; margin-left: 8px; }
.tanda-ambil.sudah { background: #E5F4E8; color: #1D5E28; }
.tanda-ambil.belum {
  background: var(--s-cont); color: var(--on-surface-variant);
  border: 1px dashed var(--outline); padding: 4px 9px;
}
/* Rincian sisa per layanan (kaki kartu Rincian layanan). */
.sisa-baris { display: flex; justify-content: space-between; padding: 2px 0; }
.sisa-baris .n { font-weight: 700; font-variant-numeric: tabular-nums; }
/* Baris kecil di bawah satu layanan: potongan & harga akhir layanan ITU.
   Sengaja lebih kecil & redup daripada barisnya sendiri supaya tetap terbaca
   sebagai keterangan, bukan layanan kedua. Jaraknya margin, BUKAN gap flex -
   browser HP kasir & pelanggan belum mengenal gap. */
.baris-rinci {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 0 0 6px; font-size: 12.5px; line-height: 17px;
  color: var(--on-surface-variant);
}
.baris-rinci .n { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 0; margin-left: 8px; }
.baris-rinci b { font-weight: 700; }
/* Satu layanan + baris rinciannya = satu grup, garisnya di bawah grup. */
.lacak-panel .grup-layanan { border-bottom: 1px solid var(--garis); }
.lacak-panel .grup-layanan .baris-nota { border-bottom: 0; }
.lencana.utang { background: var(--error-container); color: var(--on-error-container); }
.lencana.netral { background: var(--s-cont);         color: var(--on-surface-variant); }

/* ==========================================================================
   Pil filter dan subnavigasi
   ========================================================================== */
.pil { display: flex; flex-wrap: wrap; gap: var(--xs); }
.pil a, .pil button {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px var(--md); border-radius: var(--r-full);
  border: 1px solid var(--garis-tegas); background: var(--s-lowest);
  font-family: var(--font); font-size: 14px; font-weight: 600;
  color: var(--on-surface-variant); text-decoration: none; cursor: pointer;
}
.pil a:hover { background: var(--s-cont); text-decoration: none; }
.pil a.aktif { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 700; }
.pil a .n {
  font-size: 12px; background: var(--s-cont); color: var(--on-primary-fixed-variant);
  padding: 0 7px; border-radius: var(--r-full); font-variant-numeric: tabular-nums;
}
.pil a.aktif .n { background: rgba(255,255,255,.22); color: #fff; }

.subnav {
  display: inline-flex; gap: var(--base); max-width: 100%; overflow-x: auto;
  margin-bottom: var(--md); background: var(--s-lowest); border: 1px solid var(--garis);
  padding: var(--base); border-radius: var(--r-full);
}
.subnav-bungkus { display: block; }
.subnav a {
  padding: 10px var(--md); border-radius: var(--r-full); font-size: 14px; font-weight: 600;
  color: var(--on-surface-variant); text-decoration: none; white-space: nowrap;
}
.subnav a:hover { background: var(--s-low); color: var(--on-surface); text-decoration: none; }
.subnav a.aktif { background: var(--primary-container); color: var(--on-primary-container); font-weight: 700; }

/* Baris keterangan tipis di atas isi halaman. Menampung data yang dulu duduk
   di bawah judul halaman - nomor HP pelanggan, waktu buka shift - tanpa ikut
   memakan tinggi bilah atas yang menempel di layar. */
.kepala-mini {
  display: flex; align-items: center; gap: var(--xs);
  margin: 0 0 var(--sm); font-size: 13px; color: var(--on-surface-variant);
}
.kepala-mini .ms { flex: 0 0 auto; color: var(--outline); }

/* ==========================================================================
   Pita
   --------------------------------------------------------------------------
   Pemberitahuan satu baris untuk kondisi yang perlu ditindaklanjuti: antrean
   WhatsApp tertunda, shift kas belum dibuka. Dulu bentuknya kotak tiga baris
   berisi penjelasan lengkap, dan di layar HP itu memakan seluruh layar pertama
   sebelum isi halaman sempat kelihatan. Penjelasannya sekarang tinggal di
   halaman yang bisa menindaklanjutinya; di sini cuma keadaannya dan jalannya.
   ========================================================================== */
.pita {
  display: flex; align-items: center; gap: var(--xs);
  padding: var(--xs) var(--sm); margin-bottom: var(--sm);
  border-radius: var(--r-full);
  background: var(--tertiary-fixed); color: var(--on-tertiary-fixed-variant);
  font-size: 13px; font-weight: 600; line-height: 18px;
}
.pita .ms { flex: 0 0 auto; }
.pita .tumbuh { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pita a {
  flex: 0 0 auto; color: inherit; font-weight: 700; text-decoration: underline;
  padding: 2px var(--xs);
}

/* ==========================================================================
   Pesan sistem
   ========================================================================== */
.flash-tumpuk { display: flex; flex-direction: column; gap: var(--xs); margin-bottom: var(--md); }
.flash {
  display: flex; gap: var(--sm); align-items: flex-start;
  padding: var(--sm) var(--md); border-radius: var(--r-2xl); font-size: 15px;
  background: var(--primary-fixed); color: var(--on-primary-fixed);
  border: 1px solid transparent;
}
.flash .ms { margin-top: 1px; }
.flash.gagal { background: var(--error-container); color: var(--on-error-container); }
.flash.peringatan { background: var(--tertiary-fixed); color: var(--on-tertiary-fixed-variant); }
.flash.info { background: var(--s-cont); color: var(--on-surface-variant); }

.kosong { text-align: center; padding: var(--xl) var(--md); }
.kosong .gambar {
  width: 64px; height: 64px; margin: 0 auto var(--md); border-radius: var(--r-full);
  background: var(--s-cont); color: var(--primary); display: grid; place-items: center;
}
.kosong h3 { font-size: 16px; margin-bottom: 6px; }
.kosong p { color: var(--on-surface-variant); font-size: 14px; max-width: 440px; margin: 0 auto var(--md); }

/* ==========================================================================
   Layar kasir
   ========================================================================== */
.pos { display: grid; grid-template-columns: minmax(0, 1fr) 384px; gap: var(--md); align-items: start; }
/* Di layar lebar kartu Pelanggan duduk di kolom kanan, di atas keranjang, dan
   daftar layanan memakai kolom kiri sepenuhnya. Penempatannya ditulis eksplisit
   supaya urutan DOM bebas diatur untuk layar HP - di sana Pelanggan harus
   datang lebih dulu, karena nama pelanggan yang ditanyakan duluan di depan
   meja, baru cuciannya ditimbang. */
/* Kartu "Kasir" disisipkan di baris 2 kolom kanan, jadi urutan kolom kanan
   tetap Pelanggan -> Kasir -> Keranjang dan daftar layanan memanjang span 3.
   Penempatannya eksplisit: tanpa ini anak baru akan ditaruh otomatis dan
   merusak tata letak desktop. */
.pos > .kartu-pelanggan { grid-column: 2; grid-row: 1; }
.pos > .kartu-kasir     { grid-column: 2; grid-row: 2; }
.pos > .tumpuk          { grid-column: 1; grid-row: 1 / span 3; }
.pos > .keranjang       { grid-column: 2; grid-row: 3; }

.grid-layanan { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 180px), 1fr)); gap: var(--xs); }
/* Ubin layanan sengaja pendek: dengan hanya lima pilihan, semuanya harus muat
   satu layar HP tanpa digulir supaya kasir cukup sekali tekan. */
.ubin {
  min-height: 84px; padding: var(--sm) var(--md); cursor: pointer; text-align: left;
  background: var(--s-lowest); border: 1px solid var(--garis);
  border-radius: var(--r-2xl); box-shadow: var(--sh-sm);
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  font-family: var(--font); position: relative; overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
.ubin > .ms { width: 20px; height: 20px; margin-bottom: 2px; }
.grup-judul {
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--outline); margin: var(--md) 0 var(--xs);
}
.grup-judul:first-child { margin-top: 0; }
.ubin .ms { color: var(--on-surface-variant); }
.ubin .nm { font-size: 14px; font-weight: 600; line-height: 19px; color: var(--on-surface); }
.ubin .hg { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--primary); }
.ubin .hg .st { font-size: 12px; font-weight: 600; color: var(--on-surface-variant); }
.ubin:hover { border-color: var(--primary-fixed-dim); box-shadow: var(--sh-md); }
.ubin:hover .ms { color: var(--primary); }
.ubin.dipilih {
  border: 2px solid var(--primary); box-shadow: var(--sh-md); padding: calc(var(--lg) - 1px);
}
.ubin.dipilih .ms { color: var(--primary); }
.ubin.dipilih .hg { color: var(--primary); }
.ubin .dipakai {
  position: absolute; top: var(--md); right: var(--md);
  min-width: 28px; height: 28px; padding: 0 var(--xs); border-radius: var(--r-full);
  background: var(--primary); color: #fff; font-size: 13px; font-weight: 700;
  font-variant-numeric: tabular-nums; display: none; place-items: center;
  box-shadow: var(--sh-sm);
}
.ubin.dipilih .dipakai { display: grid; }

/* keranjang */
.keranjang { position: sticky; top: calc(var(--topbar-h) + var(--md)); }
.keranjang .kartu {
  box-shadow: var(--sh-md); overflow: hidden;
  /* Kepala dan tombol simpan harus tetap terlihat walau isinya panjang,
     jadi kartunya dibatasi setinggi layar dan bagian tengahnya yang digulir. */
  display: flex; flex-direction: column;
  max-height: calc(100dvh - var(--topbar-h) - var(--xl));
}
.keranjang .krj-gulir { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
/* .badan di keranjang bukan anak langsung .kartu, jadi paddingnya diatur di sini. */
.krj-gulir > .badan { padding: var(--lg); }
.krj-daftar { display: flex; flex-direction: column; gap: var(--sm); padding: 0 var(--lg); }
.krj-item {
  border: 1px solid var(--garis); border-radius: var(--r-2xl);
  padding: var(--md); background: var(--s-lowest); box-shadow: var(--sh-sm);
}
.krj-item .atas { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--sm); margin-bottom: var(--sm); }
.krj-item .nm { font-size: 14px; font-weight: 600; line-height: 20px; }
.krj-item .bawah { display: flex; align-items: center; justify-content: space-between; gap: var(--sm); }
.krj-item .jml { font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
/* Potongan & harga akhir layanan ITU, di bawah barisnya sendiri. Lebih kecil &
   redup supaya terbaca sebagai keterangan, bukan layanan kedua. Jaraknya
   margin, BUKAN gap flex - browser HP kasir belum mengenal gap. */
.krj-item .krj-rinci {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-top: 6px; font-size: 12.5px; line-height: 17px;
  color: var(--on-surface-variant);
}
.krj-item .krj-rinci .n { font-weight: 700; font-variant-numeric: tabular-nums; margin-left: 8px; min-width: 0; }

.stepper {
  display: flex; align-items: center; height: 44px;
  border: 1px solid var(--garis-tegas); border-radius: var(--r-xl);
  overflow: hidden; background: var(--surface);
}
.stepper button {
  width: 42px; height: 100%; border: 0; background: transparent; cursor: pointer;
  color: var(--on-surface); display: grid; place-items: center;
}
.stepper button:hover { background: var(--s-high); }
.stepper button:first-child { border-right: 1px solid var(--garis); }
.stepper input {
  width: 58px; height: 100%; text-align: center; border: 0; border-radius: 0;
  background: transparent; font-size: 16px; font-weight: 700; padding: 0;
  font-variant-numeric: tabular-nums;
}
.stepper input:focus { box-shadow: none; }
.stepper .satuan {
  padding: 0 var(--sm); height: 100%; display: flex; align-items: center;
  font-size: 13px; color: var(--on-surface-variant);
  border-left: 1px solid var(--garis); border-right: 1px solid var(--garis);
  background: var(--s-lowest);
}
.krj-kosong { padding: var(--xl) var(--lg); text-align: center; color: var(--on-surface-variant); font-size: 14px; }
.krj-baris { display: flex; justify-content: space-between; gap: var(--sm); padding: 6px 0; font-size: 15px; }
.krj-baris .n { font-weight: 600; font-variant-numeric: tabular-nums; }
.krj-total { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sm); }
.krj-total .lb { font-size: 16px; font-weight: 700; color: var(--on-surface-variant); }
.krj-total .n {
  font-size: 26px; line-height: 34px; font-weight: 700; letter-spacing: -.01em;
  color: var(--primary); font-variant-numeric: tabular-nums;
}
.putus { border-top: 1px solid var(--garis); margin: var(--md) 0; }

.cepat { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--xs); }
.cepat button {
  height: 48px; padding: 0 var(--base); cursor: pointer; white-space: nowrap;
  border: 1px solid var(--garis-tegas); border-radius: var(--r-xl);
  background: var(--surface); font-family: var(--font);
  font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--on-surface);
}
.cepat button:hover { border-color: var(--primary-fixed-dim); background: var(--s-high); }
.cepat button.pas { background: var(--primary-container); border-color: transparent; color: var(--on-primary-container); }
.cepat button.pas:hover { background: var(--primary); color: #fff; }

/* keranjang mengambang untuk layar HP */
.krj-apung { display: none; }
#tutup-keranjang { display: none; }

/* ==========================================================================
   Kotak isian jumlah
   --------------------------------------------------------------------------
   Muncul di tengah layar waktu ubin layanan diketuk. Lapisannya sengaja di atas
   lembar keranjang (60) dan tirainya, karena ia bisa dibuka dari mana saja -
   termasuk dari dalam keranjang waktu jumlah sebuah baris mau diubah.
   ========================================================================== */
.lembar-jumlah {
  position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 70;
  display: flex; align-items: center; justify-content: center;
  padding: var(--md);
  background: rgba(34, 20, 18, .5);
}
.lembar-jumlah > .kartu {
  width: 100%; max-width: 360px; background: var(--s-lowest);
  box-shadow: var(--sh-lg);
  animation: jumlah-masuk var(--gerak-sedang) var(--pegas) both;
}
@keyframes jumlah-masuk {
  from { opacity: 0; transform: translateY(12px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
body.hemat .lembar-jumlah > .kartu { animation: none; }
/* Angkanya besar: kasir membacanya sambil memegang timbangan, bukan sambil
   menunduk ke layar. */
.lembar-jumlah #jumlah-nilai {
  font-size: 28px; font-weight: 700; height: 60px; text-align: center;
  font-variant-numeric: tabular-nums;
}
.lembar-jumlah .pil { justify-content: center; margin-top: var(--sm); }
.lembar-jumlah .pil button { min-width: 64px; justify-content: center; }
.lembar-jumlah .petunjuk.gagal { color: var(--error); }
.lembar-jumlah > .kartu > .kaki { display: flex; gap: var(--xs); }
.lembar-jumlah #jumlah-simpan { flex: 1 1 auto; }

/* ==========================================================================
   Autocomplete
   ========================================================================== */
.saran-bungkus { position: relative; }
.saran {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 6px);
  background: var(--s-lowest); border: 1px solid var(--garis-tegas);
  border-radius: var(--r-2xl); box-shadow: var(--sh-lg); overflow: hidden;
  max-height: 300px; overflow-y: auto;
}
.saran button {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sm);
  width: 100%; text-align: left; padding: var(--sm) var(--md);
  background: none; border: 0; border-bottom: 1px solid var(--garis);
  font-family: var(--font); font-size: 15px; cursor: pointer; color: var(--on-surface);
}
.saran button:last-child { border-bottom: 0; }
.saran button:hover, .saran button.sorot { background: var(--s-cont); }
.saran .hp { color: var(--on-surface-variant); font-size: 13px; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Kartu daftar
   --------------------------------------------------------------------------
   Dipakai daftar Order dan Pemasukan. Tabel yang ditumpuk per kolom membuat
   satu baris setinggi hampir satu layar di HP, jadi cuma satu dua item yang
   kelihatan sekaligus. Bentuk ini tiga baris saja: penanda utama, siapa
   orangnya, lalu angka dan waktu - tanpa judul kolom, karena posisinya sudah
   menjelaskan isinya.
   ========================================================================== */
.daftar-kartu {
  display: grid; gap: var(--xs); padding: var(--sm) var(--md) var(--md);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  align-items: start;
}
.kartu-daftar {
  border: 1px solid var(--garis); border-radius: var(--r-2xl);
  background: var(--s-lowest); padding: var(--sm) var(--md);
  display: flex; flex-direction: column; gap: 2px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.kartu-daftar:hover { border-color: var(--primary-fixed-dim); box-shadow: var(--sh-sm); }
.kd-atas { display: flex; align-items: center; justify-content: space-between; gap: var(--xs); }
.kd-kode {
  font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--primary); text-decoration: none; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kd-orang { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--xs); min-width: 0; }
.kd-nama { font-size: 14px; font-weight: 600; color: var(--on-surface); }
.kd-sub { font-size: 12px; color: var(--on-surface-variant); font-variant-numeric: tabular-nums; }
.kd-bawah {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--xs); margin-top: 2px;
}
.kd-nilai {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--xs);
  font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.kd-nilai.minus { color: var(--error); }
.kd-kanan { font-size: 12px; color: var(--outline); font-variant-numeric: tabular-nums; }
.kd-aksi { display: flex; justify-content: flex-end; margin-top: var(--xs); }
.kd-aksi details.lipat { border: 0; background: none; width: 100%; }
.kd-aksi details.lipat > summary { padding: 0; font-size: 13px; justify-content: flex-end; gap: var(--xs); }
.kd-aksi details.lipat > div { padding: var(--xs) 0 0; }

/* ==========================================================================
   Papan cucian
   ========================================================================== */
.papan { display: grid; grid-template-columns: repeat(auto-fit, minmax(198px, 1fr)); gap: var(--sm); }
.kolom {
  background: var(--s-low); border: 1px solid var(--garis);
  border-radius: var(--r-3xl); display: flex; flex-direction: column; min-height: 240px;
}
.kolom > .kepala {
  display: flex; align-items: center; justify-content: space-between; gap: var(--xs);
  padding: var(--md) var(--md) var(--sm);
}
.kolom > .kepala .jd { display: flex; align-items: center; gap: var(--xs); font-size: 14px; font-weight: 700; }
.kolom > .kepala .n {
  font-size: 12px; font-weight: 700; background: var(--s-lowest); color: var(--on-surface-variant);
  padding: 2px var(--xs); border-radius: var(--r-full); font-variant-numeric: tabular-nums;
}
.kolom > .isi-kolom {
  padding: 0 var(--xs) var(--xs); display: flex; flex-direction: column; gap: var(--xs);
  overflow-y: auto; max-height: calc(100dvh - 260px); overscroll-behavior: contain;
}
.kolom.sorot { background: var(--primary-fixed); border-color: var(--primary-fixed-dim); }

.kartu-order {
  background: var(--s-lowest); border: 1px solid var(--garis);
  border-radius: var(--r-2xl); padding: var(--md); box-shadow: var(--sh-sm);
}
.kartu-order .atas { display: flex; align-items: center; justify-content: space-between; gap: var(--xs); }
.kartu-order .kode { font-size: 14px; font-weight: 700; }
.kartu-order .nm { font-size: 14px; font-weight: 600; line-height: 19px; margin-top: 4px; }
/* Nomor HP diberi barisnya sendiri. Dulu ia menempel di sebelah nama dan
   dipisah gap flexbox - yang tidak dikenal Chrome sebelum versi 84, sehingga
   di tablet kasir keduanya menyatu jadi "Andre085735801525". */
.kartu-order .hp {
  display: block; font-size: 12px; line-height: 16px; color: var(--outline);
  font-variant-numeric: tabular-nums;
}
.kartu-order .bawah {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; margin-top: var(--xs);
}
.kartu-order .bawah .uang { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.kartu-order .bawah .uang .lencana { margin-left: 4px; }
.kartu-order .bawah .uang.minus { color: var(--error); }
.kartu-order .bawah .waktu { font-size: 11px; color: var(--outline); }
.kartu-order form { margin-top: var(--xs); }
.kartu-order .mt {
  font-size: 12px; color: var(--on-surface-variant); display: flex; flex-direction: column;
  gap: 3px; margin-top: var(--xs);
}
.kartu-order .mt span { display: flex; align-items: center; gap: 5px; }
.kartu-order .uang-baris {
  display: flex; align-items: center; gap: 6px; margin-top: var(--sm);
  padding-top: var(--sm); border-top: 1px solid var(--garis);
  font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.kartu-order form { margin-top: var(--sm); }
.kartu-order .tombol {
  height: 40px; font-size: 13px; padding: 0 var(--sm);
  white-space: normal; line-height: 1.25; text-align: center;
}
.kartu-order.telat { border: 2px solid var(--error); padding: calc(var(--md) - 1px); }
.tanda-telat {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px var(--xs);
  border-radius: var(--r-full); background: var(--error); color: #fff;
  font-size: 11px; font-weight: 700;
}

/* ==========================================================================
   Tahap: rel mendatar dan garis waktu tegak
   ========================================================================== */
.rel { display: flex; gap: var(--base); flex-wrap: wrap; }
.rel .tahap {
  flex: 1 1 0; min-width: 104px; padding: var(--sm); border-radius: var(--r-xl);
  background: var(--s-low); text-align: center;
}
.rel .tahap .nm { display: block; font-size: 13px; font-weight: 700; color: var(--outline); }
.rel .tahap .kt { font-size: 11px; color: var(--outline); }
.rel .tahap.lewat { background: var(--primary-fixed); }
.rel .tahap.lewat .nm, .rel .tahap.lewat .kt { color: var(--on-primary-fixed-variant); }
.rel .tahap.sekarang { background: var(--primary); }
.rel .tahap.sekarang .nm, .rel .tahap.sekarang .kt { color: #fff; }

.jalur { list-style: none; margin: 0; padding: 0; position: relative; }
.jalur::before {
  content: ""; position: absolute; left: 19px; top: 20px; bottom: 20px;
  width: 4px; border-radius: var(--r-full); background: var(--s-high);
}
.jalur li { position: relative; display: flex; gap: var(--md); padding-bottom: var(--lg); }
.jalur li:last-child { padding-bottom: 0; }
.jalur .titik {
  width: 42px; height: 42px; flex: 0 0 42px; border-radius: var(--r-full);
  background: var(--s-high); color: var(--outline);
  display: grid; place-items: center; z-index: 1;
}
.jalur .isi-tahap { padding-top: 8px; }
.jalur .isi-tahap .nm { font-size: 14px; font-weight: 700; color: var(--on-surface-variant); }
.jalur .isi-tahap .wk { font-size: 13px; color: var(--outline); font-variant-numeric: tabular-nums; }
.jalur li.lewat .titik { background: var(--primary); color: #fff; box-shadow: 0 4px 10px rgba(232, 64, 42, .22); }
.jalur li.lewat .isi-tahap .nm { color: var(--on-surface); }
.jalur li.sekarang .titik {
  background: var(--primary); color: #fff;
  box-shadow: 0 0 0 4px var(--s-lowest), 0 6px 16px rgba(0, 74, 198, .3);
  animation: denyut 2.2s ease-in-out infinite;
}
.jalur li.sekarang .isi-tahap .nm { color: var(--primary); }
.jalur li.sekarang .isi-tahap .wk {
  color: var(--on-primary-fixed-variant); font-weight: 600;
  background: var(--primary-fixed); padding: 2px var(--xs); border-radius: var(--r);
  display: inline-block; margin-top: 2px;
}
.jalur li.belum { opacity: .5; }

@keyframes denyut {
  0% { box-shadow: 0 0 0 4px var(--s-lowest), 0 0 0 0 rgba(0, 74, 198, .35); }
  70% { box-shadow: 0 0 0 4px var(--s-lowest), 0 0 0 16px rgba(0, 74, 198, 0); }
  100% { box-shadow: 0 0 0 4px var(--s-lowest), 0 0 0 0 rgba(0, 74, 198, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .jalur li.sekarang .titik { animation: none; }
  .tombol:active { transform: none; }
  * { transition: none !important; }
}

/* ==========================================================================
   Riwayat dan panel lipat
   ========================================================================== */
.riwayat { list-style: none; margin: 0; padding: 0; }
.riwayat li {
  display: flex; gap: var(--sm); padding: var(--sm) 0;
  border-bottom: 1px solid var(--garis); font-size: 14px; align-items: center;
}
.riwayat li:last-child { border-bottom: 0; }
.riwayat .waktu { flex: 0 0 132px; color: var(--outline); font-variant-numeric: tabular-nums; font-size: 12px; }

details.lipat { border: 1px solid var(--garis); border-radius: var(--r-xl); background: var(--s-lowest); }
details.lipat > summary {
  cursor: pointer; padding: var(--sm) var(--md); font-weight: 600; font-size: 14px;
  list-style: none; display: flex; align-items: center; justify-content: space-between; gap: var(--xs);
}
details.lipat > summary::-webkit-details-marker { display: none; }
details.lipat > summary::after { content: "+"; color: var(--outline); font-size: 18px; }
details.lipat[open] > summary::after { content: "\2212"; }
details.lipat > div { padding: 0 var(--md) var(--md); }

/* ==========================================================================
   Halaman masuk
   ========================================================================== */
/* Halaman masuk dan daftar harus muat satu layar tanpa digeser. Tabletnya
   dipakai berdiri di meja kasir, dan kartu yang menggantung sedikit di bawah
   lipatan membuat tombolnya tidak terlihat. Tingginya dikunci ke tinggi
   viewport, dan setiap jarak diberi batas bawah lewat clamp() supaya ikut
   mengecil di layar pendek, bukan mendorong isi ke luar. Satuan vh dipakai
   sebagai cadangan untuk browser lama yang belum mengenal dvh. */
.masuk-layar {
  height: 100vh; height: 100dvh; padding: clamp(8px, 2.2vh, 24px) var(--md);
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(160deg, var(--primary-fixed), var(--background) 60%);
  /* Jaring pengaman untuk layar yang benar-benar sangat pendek: yang bergeser
     kotaknya, bukan seluruh halaman. */
  overflow-y: auto;
}
.masuk-kotak { width: 100%; max-width: 400px; margin: auto; }
.masuk-kartu {
  background: var(--s-lowest); border-radius: var(--r-3xl);
  padding: clamp(16px, 3.2vh, 32px) var(--lg); box-shadow: var(--sh-lg);
}
.masuk-merek { text-align: center; margin-bottom: clamp(12px, 2.4vh, 24px); }
.masuk-merek .lambang {
  width: clamp(44px, 8.5vh, 64px); height: clamp(44px, 8.5vh, 64px);
  margin: 0 auto clamp(6px, 1.2vh, 12px); border-radius: var(--r-full);
  background: var(--primary); color: #fff; display: grid; place-items: center;
  box-shadow: var(--sh-primary);
}
.masuk-merek b {
  display: block; font-size: clamp(21px, 4vh, 28px); line-height: 1.15;
  font-weight: 800; letter-spacing: -.02em; color: var(--primary);
}
.masuk-merek small { font-size: clamp(12px, 1.8vh, 14px); color: var(--on-surface-variant); }
.masuk-kartu label.bidang { margin-bottom: clamp(10px, 1.8vh, 16px); }
.masuk-kartu label.bidang > span { margin-bottom: clamp(3px, .6vh, 6px); }
.masuk-kartu input { height: clamp(42px, 6.2vh, 48px); }
.masuk-kartu .tombol.besar { height: clamp(46px, 6.8vh, 56px); }
.masuk-kartu .flash { margin-bottom: clamp(10px, 1.8vh, 16px) !important; }
.masuk-kaki {
  text-align: center; font-size: 13px; color: var(--on-surface-variant);
  margin-top: clamp(8px, 1.6vh, 16px);
}
/* Ruas jebakan di halaman daftar. Dipindah ke luar layar, bukan
   display:none, karena sebagian robot melewati ruas yang jelas-jelas
   disembunyikan. Tingginya nol supaya tidak menambah tinggi kartu. */
.jebakan {
  position: absolute; left: -9999px; width: 1px; height: 0;
  overflow: hidden; opacity: 0; pointer-events: none;
}

.pesan-layar { min-height: 100vh; min-height: 100dvh; display: grid; place-items: center; padding: var(--lg); text-align: center; }
.pesan-layar .kotak { max-width: 480px; }
.pesan-layar .kode-galat { font-size: 64px; line-height: 1; font-weight: 800; letter-spacing: -.03em; color: var(--primary); }

/* ==========================================================================
   Halaman pelanggan
   ========================================================================== */
.lacak { max-width: 430px; margin: 0 auto;
  padding-bottom: calc(104px + env(safe-area-inset-bottom, 0px));
  min-height: 100vh; min-height: 100dvh; background: var(--background); }

.lacak-kepala {
  background: var(--s-lowest); padding: var(--lg);
  border-radius: 0 0 var(--r-3xl) var(--r-3xl); box-shadow: var(--sh-sm);
}
.lacak-kepala .atas { display: flex; align-items: center; justify-content: space-between; gap: var(--sm); margin-bottom: var(--lg); }
.lacak-kepala .outlet { font-size: 32px; line-height: 38px; font-weight: 800; letter-spacing: -.02em; color: var(--primary); }
.lacak-kepala .ket { font-size: 14px; color: var(--on-surface-variant); }
/* .lacak-kepala .lambang DIHAPUS 19 Sep 2026 bersama span-nya di
   track/show.html - lencana bulat merah di pojok kanan atas tidak dipakai
   lagi, jadi aturannya tidak boleh ditinggalkan yatim. */
.lacak-nota { background: var(--surface); border: 1px solid var(--garis); border-radius: var(--r-2xl); padding: var(--md); }
.lacak-nota .baris {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sm);
  padding: 6px 0;
}
.lacak-nota .baris + .baris { border-top: 1px solid var(--garis); padding-top: var(--sm); margin-top: 6px; }
.lacak-nota .kn { font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--on-surface-variant); }
.lacak-nota .is { font-size: 15px; font-weight: 700; }
.lacak-nota .is.biru { color: var(--primary); font-variant-numeric: tabular-nums; }

.lacak-isi { padding: var(--lg) var(--lg) 0; display: flex; flex-direction: column; gap: var(--lg); }

.lacak-hero {
  text-align: center; padding: var(--xl) var(--lg);
  background: linear-gradient(150deg, var(--primary-fixed), var(--s-high));
  border-radius: var(--r-3xl); border: 1px solid rgba(0, 74, 198, .1); box-shadow: var(--sh-sm);
}
.lacak-hero .bulat {
  width: 80px; height: 80px; margin: 0 auto var(--lg); border-radius: var(--r-full);
  background: var(--primary); color: #fff; display: grid; place-items: center;
  box-shadow: 0 10px 20px rgba(0, 74, 198, .25);
  animation: denyut-hero 2.4s ease-in-out infinite;
}
@keyframes denyut-hero {
  0% { box-shadow: 0 10px 20px rgba(0,74,198,.25), 0 0 0 0 rgba(0, 74, 198, .3); }
  70% { box-shadow: 0 10px 20px rgba(0,74,198,.25), 0 0 0 18px rgba(0, 74, 198, 0); }
  100% { box-shadow: 0 10px 20px rgba(0,74,198,.25), 0 0 0 0 rgba(0, 74, 198, 0); }
}
@media (prefers-reduced-motion: reduce) { .lacak-hero .bulat { animation: none; } }
.lacak-hero h2 { font-size: 24px; line-height: 32px; font-weight: 700; }
.lacak-hero .estimasi {
  display: inline-block; margin-top: var(--sm); padding: 6px var(--md);
  background: rgba(255,255,255,.7); border: 1px solid #fff; border-radius: var(--r-full);
  font-size: 14px; color: var(--on-surface-variant);
}
.lacak-panel {
  background: var(--s-lowest); border: 1px solid var(--garis);
  border-radius: var(--r-3xl); padding: var(--lg); box-shadow: var(--sh-sm);
}
.lacak-panel h3 { font-size: 20px; font-weight: 700; margin-bottom: var(--md); }
.lacak-panel .baris-nota {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sm);
  padding: var(--xs) 0; border-bottom: 1px solid var(--garis); font-size: 15px;
}
.lacak-panel .baris-nota:last-child { border-bottom: 0; }
.lacak-panel .baris-nota .n { font-weight: 700; font-variant-numeric: tabular-nums; }
/* Jumlah layanan ditulis redup di kiri nominalnya, uangnya tetap tebal supaya
   yang dibaca duluan angkanya. Jaraknya margin-right, BUKAN gap flex -
   browser HP pelanggan belum mengenal gap. */
.lacak-panel .baris-nota .n .jml {
  font-weight: 400; font-size: 13px; color: var(--on-surface-variant);
  margin-right: 8px;
}
/* Baris potongan dibesarkan supaya terbaca sebagai penghematan sungguhan.
   Satu angka gabungan; kata "Potongan" tetap ikut, warna tidak pernah jadi
   satu-satunya sinyal. min-width:0 supaya angka panjang tidak melebar di 360px. */
.lacak-panel .baris-nota.potongan-besar { padding: var(--sm) 0; }
.lacak-panel .baris-nota.potongan-besar .lb { font-size: 15px; font-weight: 700; }
.lacak-panel .baris-nota.potongan-besar .n { font-size: 20px; font-weight: 800; min-width: 0; }
.lacak-bayar {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sm);
  background: var(--s-cont); padding: var(--md); border-radius: var(--r-2xl); margin-top: var(--md);
}
.lacak-bayar .lb { font-size: 15px; font-weight: 600; color: var(--primary); }
.lacak-bayar .nl {
  font-size: 18px; font-weight: 700; color: var(--primary); background: #fff;
  padding: 4px var(--sm); border-radius: var(--r-full); box-shadow: var(--sh-sm);
  font-variant-numeric: tabular-nums;
}
.lacak-bayar.utang .lb, .lacak-bayar.utang .nl { color: var(--on-error-container); }
.lacak-bayar.utang { background: var(--error-container); }
.lacak-rak {
  background: var(--primary); color: #fff; border-radius: var(--r-3xl);
  padding: var(--lg); text-align: center; box-shadow: var(--sh-primary);
}
.lacak-rak .kn { font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; opacity: .85; }
.lacak-rak .nl { font-size: 40px; line-height: 48px; font-weight: 800; letter-spacing: -.02em; }
.lacak-wa {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  padding: var(--md) var(--md) calc(var(--md) + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, var(--background) 60%, transparent);
  max-width: 430px; margin: 0 auto;
}
.lacak-wa a {
  display: flex; align-items: center; justify-content: center; gap: var(--sm);
  width: 100%; height: var(--touch-lg); border-radius: var(--r-2xl);
  background: var(--wa); color: #fff; font-size: 16px; font-weight: 700;
  text-decoration: none; box-shadow: 0 8px 20px rgba(37, 211, 102, .3);
}
.lacak-wa a:hover { background: var(--wa-gelap); text-decoration: none; }
.lacak-kaki { text-align: center; font-size: 12px; color: var(--outline); padding: var(--lg); }

/* ==========================================================================
   Nota thermal 58mm
   ========================================================================== */
/* Nota 58mm untuk printer bawaan HP kasir. Kepala cetak termal menghasilkan
   titik-titik terputus pada huruf tipis, jadi seluruh nota memakai bobot 500 ke
   atas dan ukuran sedikit lebih besar daripada di layar - hemat kertas kalah
   penting dibanding nota yang masih terbaca setelah seminggu di saku. */
.nota-cetak {
  width: 58mm; margin: 0 auto; padding: 4mm 3mm; background: #fff; color: #000;
  font-family: var(--font); font-size: 12px; line-height: 1.4; font-weight: 500;
}
.nota-cetak .tengah { text-align: center; }
.nota-cetak h1 { font-size: 16px; font-weight: 800; margin: 0 0 2px; letter-spacing: 0; }
.nota-cetak .pisah { border-top: 1px dashed #000; margin: 5px 0; }
.nota-cetak table { width: 100%; font-size: 12px; }
.nota-cetak th, .nota-cetak td { padding: 1px 0; border: 0; background: none; font-size: 12px; font-weight: 500; text-transform: none; letter-spacing: 0; color: #000; }
.nota-cetak .num { text-align: right; font-variant-numeric: tabular-nums; }
.nota-cetak .total { font-size: 14px; font-weight: 700; }
/* Potongan naik ke pangkat yang sama dengan TOTAL supaya kelihatan sebagai
   penghematan sungguhan, bukan catatan kaki (nama kelas SENGAJA bukan
   "hemat" - itu sudah dipakai body.hemat untuk Mode hemat baterai). Satu angka gabungan.
   Keduanya ditulis "<tr> td" karena aturan ".nota-cetak td" di atas menyasar
   sel-nya LANGSUNG: nilai warisan dari <tr> selalu kalah, jadi ".total" sendiri
   tidak pernah berefek. Potongan tidak boleh lebih besar daripada TOTAL. */
.nota-cetak .total td, .nota-cetak .potongan-besar td { font-size: 14px; font-weight: 700; }
/* Jarak antar layanan, meniru baris kosong pemisah di nota kertas supaya blok
   tiap layanan sama gampang dipindai di layar maupun di kertas. Ditulis
   "<tr> td" dengan alasan yang sama seperti di atas: aturan ".nota-cetak td"
   menyasar sel-nya LANGSUNG, jadi nilai warisan dari <tr> selalu kalah.
   Layanan PERTAMA tidak perlu jarak - di atasnya sudah ada garis pemisah. */
.nota-cetak .nota-layanan td { padding-top: 7px; }
.nota-cetak tr:first-child.nota-layanan td { padding-top: 1px; }
/* Nomor nota dicari duluan tiap kali cucian diambil, jadi dibesarkan. */
.nota-kode {
  font-size: 20px; font-weight: 800; text-align: center;
  letter-spacing: .02em; margin: 2px 0 4px;
}
.nota-cetak .kotak-rak {
  border: 2px solid #000; border-radius: 4px; padding: 4px;
  font-size: 26px; font-weight: 800; text-align: center; margin: 4px 0;
}
.nota-cetak img { display: block; margin: 5px auto 3px; width: 34mm; height: 34mm; }
.cetak-bar { max-width: 300px; margin: var(--md) auto; display: flex; gap: var(--xs); padding: 0 var(--sm); }
.cetak-bar + .cetak-bar { margin-top: calc(var(--xs) * -1); }
.cetak-pesan {
  max-width: 300px; margin: var(--xs) auto 0; padding: 0 var(--sm);
  text-align: center; font-size: 13px; color: var(--on-surface-variant); min-height: 18px;
}
.cetak-pesan.gagal { color: var(--error); }

@media print {
  @page { size: 58mm auto; margin: 0; }
  body { background: #fff; }
  .cetak-bar, .cetak-pesan, .sidebar, .tabbar, .topbar { display: none !important; }
  .nota-cetak { width: 58mm; box-shadow: none; }
}

/* ==========================================================================
   Layar sempit
   ========================================================================== */
@media (max-width: 1180px) {
  .kisi-sisi { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 1100px) {
  .papan { grid-auto-flow: column; grid-template-columns: none;
           grid-auto-columns: minmax(250px, 1fr); overflow-x: auto; padding-bottom: var(--sm); }
  .pos { grid-template-columns: minmax(0, 1fr); }
  .pos > .kartu-pelanggan,
  .pos > .kartu-kasir,
  .pos > .tumpuk,
  .pos > .keranjang { grid-column: auto; grid-row: auto; }

  /* keranjang jadi lembar bawah yang ditarik dari tombol mengambang */
  .keranjang {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 60;
    max-width: 520px; margin: 0 auto;
    transform: translateY(100%); transition: transform .3s cubic-bezier(.4, 0, .2, 1);
    max-height: 88vh; display: flex; flex-direction: column;
  }
  .keranjang.buka { transform: translateY(0); }
  .keranjang .kartu {
    border-radius: var(--r-3xl) var(--r-3xl) 0 0; border-bottom: 0;
    box-shadow: 0 -10px 40px rgba(34, 20, 18, .18);
    display: flex; flex-direction: column; max-height: 88vh;
  }
  .keranjang .krj-gulir { overflow-y: auto; flex: 1 1 auto; }
  .tirai {
    position: fixed; top: 0; right: 0; bottom: 0; left: 0;
    background: rgba(34, 20, 18, .5);
    -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
    z-index: 55;                     /* di bawah lembar (60), saudara sekandungnya */
    animation: tirai-masuk var(--gerak-sedang) ease both;
  }
  @keyframes tirai-masuk { from { opacity: 0; } to { opacity: 1; } }

  /* Lembar keranjang harus punya latar sendiri yang pekat. Kalau mengandalkan
     latar kartu di belakangnya, isinya ikut terlihat redup di atas kelam. */
  .keranjang > .kartu { background: var(--s-lowest); }

  /* Bilah menu bawah dan bilah total ikut menyingkir saat lembar terbuka.
     Keduanya ada di luar stacking context lembar, jadi tidak bisa ditutup
     kelam; dibiarkan tampil, mereka menutupi tombol simpan. */
  body.keranjang-terbuka .tabbar,
  body.keranjang-terbuka .krj-apung.ada-isi {
    transform: translateY(150%);
    opacity: 0;
    pointer-events: none;
    transition: transform .3s cubic-bezier(.4, 0, .2, 1), opacity .2s ease;
  }

  /* Bilah atas juga di luar jangkauan kelam. Dibiarkan terang dan bisa ditekan,
     satu ketukan pada "Papan cucian" meninggalkan pesanan yang belum disimpan. */
  body.keranjang-terbuka .topbar {
    opacity: .4;
    pointer-events: none;
    transition: opacity var(--gerak-sedang) ease;
  }
  #tutup-keranjang { display: grid; }
  /* Duduk persis di atas bilah tab. Tingginya diukur skrip ke --tabbar-ruang
     karena berubah mengikuti ukuran huruf sistem dan tinggi garis home;
     angka cadangan dipakai sebelum skrip sempat mengukur. Lapisannya di atas
     bilah tab, supaya kalau toh bersentuhan, yang tertutup bukan nominalnya. */
  .krj-apung.ada-isi {
    display: block; position: fixed; left: 0; right: 0; z-index: 52;
    bottom: calc(var(--tabbar-ruang, 94px) + var(--sm));
    padding: 0 var(--pad);
    max-width: 520px; margin: 0 auto;
  }
  .krj-apung button {
    width: 100%; display: flex; align-items: center; justify-content: space-between;
    gap: var(--sm); padding: var(--base); padding-right: var(--md);
    background: var(--primary); color: #fff; border: 0; cursor: pointer;
    border-radius: var(--r-full); box-shadow: var(--sh-primary); font-family: var(--font);
  }
  .krj-apung .kiri { display: flex; align-items: center; gap: var(--sm); }
  .krj-apung .lingkar {
    width: 48px; height: 48px; border-radius: var(--r-full); position: relative;
    background: rgba(255,255,255,.2); display: grid; place-items: center;
  }
  .krj-apung .badge {
    position: absolute; top: -2px; right: -2px; min-width: 22px; height: 22px;
    padding: 0 5px; border-radius: var(--r-full); background: var(--error); color: #fff;
    font-size: 12px; font-weight: 700; display: grid; place-items: center;
    border: 2px solid var(--primary);
  }
  .krj-apung .teks { text-align: left; }
  .krj-apung .teks .lb { display: block; font-size: 12px; font-weight: 600; opacity: .9; }
  .krj-apung .teks .nl { display: block; font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
  .krj-apung .kanan-pil {
    display: flex; align-items: center; gap: 4px; padding: var(--xs) var(--sm);
    background: rgba(255,255,255,.15); border-radius: var(--r-full);
    font-size: 14px; font-weight: 700;
  }
  /* KUNCI-KASIR = kasir belum dipilih. Nama kelasnya SENGAJA bukan "terkunci":
     `.terkunci` (app.css:284) sudah dipakai mengunci isian sesudah order
     tersimpan dan isinya `pointer-events:none` - memakainya di sini membuat
     ketukan TEMBUS ke ubin layanan di bawah bilah, sehingga menekan bilah yang
     terkunci malah menambah layanan. Terbukti saat diuji; jangan disatukan.
     Bilahnya TETAP DI TEMPATNYA dan nominalnya
     tetap terbaca - yang berubah warnanya (merah aksi jadi permukaan redup)
     dan kata aksinya jadi "Pilih kasir dulu". Bilah ini TIDAK PERNAH boleh
     disembunyikan atau digeser keluar layar: itu bug lama yang membuat kasir
     tidak bisa membuka keranjangnya sendiri di Android.
     Warna saja tidak pernah jadi satu-satunya sinyal - katanya selalu ikut. */
  .krj-apung.kunci-kasir button {
    background: var(--s-highest); color: var(--on-surface-variant);
    box-shadow: none; cursor: not-allowed;
  }
  .krj-apung.kunci-kasir .lingkar { background: rgba(0,0,0,.06); }
  .krj-apung.kunci-kasir .kanan-pil { background: rgba(0,0,0,.06); }
  /* Cincin badge mengikuti latar barunya, kalau tidak ia tampak seperti sisa
     potongan merah di atas permukaan redup. */
  .krj-apung.kunci-kasir .badge { border-color: var(--s-highest); }
  /* Panah "buka" tidak punya arti selama tidak ada yang bisa dibuka. */
  .krj-apung.kunci-kasir .kanan-pil .ms { display: none; }
}

@media (max-width: 900px) {
  .sidebar { display: none; }
  .topbar { padding: var(--sm) var(--pad); gap: var(--sm); min-height: 56px; }
  .topbar h1 { font-size: 18px; line-height: 26px; }
  .isi { padding: var(--pad) var(--pad) 104px; }
  .kisi.duo-hp { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sm); }
  .kisi.duo-hp .ringkas { padding: var(--md); border-radius: var(--r-2xl); }
  .kisi.duo-hp .ringkas .nilai { font-size: 20px; line-height: 26px; }
  .kisi.duo-hp .ringkas .lencet { width: 32px; height: 32px; flex-basis: 32px; }
  .kisi.duo-hp .ringkas .lencet .ms { width: 18px; height: 18px; }
  /* Ubin separuh layar menyisakan sekitar 82px di samping lencana bulatnya.
     Pada 14px kata "Pengeluaran" butuh 84px, jadi ia terpenggal di tengah kata.
     Satu piksel huruf lebih kecil membuatnya muat utuh. */
  .kisi.duo-hp .ringkas .label { font-size: 13px; line-height: 18px; }
  /* Alas kartu dirapatkan di HP: dengan huruf yang lebih kecil, jarak selebar
     layar lebar cuma membuat isi terdorong turun dan kasir harus menggulir. */
  .kartu > .kepala { padding: var(--md) var(--md) var(--xs); }
  .kartu > .badan { padding: var(--md); }
  .kartu > .kaki { padding: var(--sm) var(--md); }
  .kartu > .tabel-geser td:first-child,
  .kartu > .tabel-geser td:last-child { padding-left: var(--md); padding-right: var(--md); }
  .cari-global { display: none; }
  .grid-layanan { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--xs); }
  .ubin { min-height: 76px; padding: var(--sm) var(--md); border-radius: var(--r-2xl); }
  .ubin.dipilih { padding: calc(var(--md) - 1px); }
  .rel .tahap { min-width: 88px; }

  /* Papan ketik yang terbuka sudah memakan separuh layar. Bilah tab dan bilah
     total ikut turun selagi kasir mengetik supaya isian yang sedang diisi tidak
     terhimpit, lalu naik lagi begitu papan ketik ditutup. */
  body.mengetik .tabbar,
  body.mengetik .krj-apung.ada-isi {
    transform: translateY(150%); opacity: 0; pointer-events: none;
    transition: transform var(--gerak-cepat) var(--halus), opacity var(--gerak-cepat) ease;
  }

  /* --- lembar menu "Lainnya" ---------------------------------------------
     Menampung halaman yang tidak kebagian tab: Pelanggan, Laporan, Layanan,
     Pengaturan, papan cucian, dan tombol keluar. */
  .lembar-menu {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    max-width: 520px; margin: 0 auto; padding: 0 var(--sm);
    transform: translateY(115%);
    transition: transform var(--gerak-sedang) var(--pegas);
  }
  .lembar-menu.buka { transform: translateY(0); }
  .lembar-menu > .kartu {
    background: var(--s-lowest);
    border-radius: var(--r-3xl) var(--r-3xl) 0 0; border-bottom: 0;
    box-shadow: 0 -10px 40px rgba(34, 20, 18, .18);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .menu-petak {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 88px), 1fr));
    gap: var(--xs);
  }
  .menu-petak a {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 6px; min-height: 80px; padding: var(--sm) var(--base);
    border-radius: var(--r-2xl); background: var(--s-low); text-decoration: none;
    color: var(--on-surface-variant); font-size: 12px; font-weight: 600; text-align: center;
    line-height: 16px;
  }
  .menu-petak a.aktif { background: var(--primary-container); color: var(--on-primary-container); }
  .menu-petak .ms { width: 24px; height: 24px; }
  .sakelar-hemat {
    align-items: flex-start; gap: var(--sm); margin-top: var(--md);
    padding: var(--sm) var(--md); border-radius: var(--r-2xl); background: var(--s-low);
    font-size: 14px;
  }
  .sakelar-hemat input { margin-top: 2px; }

  .lembar-siapa {
    display: flex; align-items: center; gap: var(--sm);
    padding: var(--md) 0 var(--sm); margin-top: var(--sm);
    border-top: 1px solid var(--garis);
  }
  .lembar-siapa .avatar {
    width: 40px; height: 40px; border-radius: var(--r-full); flex: 0 0 40px;
    background: var(--primary-fixed); color: var(--on-primary-fixed);
    display: grid; place-items: center; font-weight: 700;
  }
  .lembar-siapa .nm { font-size: 14px; font-weight: 700; }
  .lembar-siapa .pr { font-size: 12px; color: var(--on-surface-variant); }

  /* Selagi lembar terbuka, bilah tab ikut turun supaya tidak menempel di atas
     lembarnya, sama seperti perlakuan lembar keranjang. */
  body.lembar-terbuka .tabbar,
  body.lembar-terbuka .krj-apung.ada-isi {
    transform: translateY(150%); opacity: 0; pointer-events: none;
    transition: transform var(--gerak-sedang) var(--halus), opacity var(--gerak-cepat) ease;
  }
  body.lembar-terbuka { overflow: hidden; }

  /* --- bilah menu bawah --------------------------------------------------
     Bilahnya mengambang sedikit dari tepi layar, dan tab aktif ditandai pil
     yang bergeser dari tab lama ke tab baru, bukan muncul-hilang di tempat.
     Geserannya diatur skrip karena posisinya harus diukur dari tab yang
     sedang aktif. */
  .tabbar {
    display: flex; position: fixed; z-index: 50;
    left: var(--sm); right: var(--sm);
    bottom: calc(var(--sm) + env(safe-area-inset-bottom, 0px));
    background: var(--s-lowest);
    background: color-mix(in srgb, var(--s-lowest) 88%, transparent);
    -webkit-backdrop-filter: saturate(180%) blur(16px);
    backdrop-filter: saturate(180%) blur(16px);
    border: 1px solid var(--garis);
    border-radius: var(--r-3xl);
    padding: 6px;
    box-shadow: 0 6px 24px rgba(34, 20, 18, .12), 0 2px 6px rgba(34, 20, 18, .06);
    justify-content: space-around; align-items: center;
  }

  .tabbar-penanda {
    position: absolute; top: 0; left: 0; z-index: 0;
    border-radius: var(--r-full);
    background: var(--primary-container);
    opacity: 0; pointer-events: none;
    transition: transform .34s var(--pegas), width .34s var(--pegas),
                height .2s ease, opacity .2s ease;
  }

  .tabbar a {
    position: relative; z-index: 1;
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 5px 4px; min-width: 62px; flex: 1 1 0;
    color: var(--on-surface-variant);
    font-size: 11px; font-weight: 600; text-decoration: none;
    transition: color .22s ease;
  }
  .tabbar a .lingkar {
    width: 58px; height: 30px; border-radius: var(--r-full);
    display: grid; place-items: center;
    transition: transform .3s var(--pegas);
  }
  .tabbar a .ms {
    transition: font-variation-settings .25s ease, transform .3s var(--pegas);
  }
  .tabbar a:active .lingkar { transform: scale(.9); }
  /* Label duduk di luar pil, jadi warnanya harus terbaca di atas latar bilah,
     bukan di atas pil. Yang memakai --on-primary-container hanya ikonnya. */
  .tabbar a.aktif { color: var(--primary); font-weight: 700; }
  .tabbar a.aktif .lingkar { color: var(--on-primary-container); }
  .tabbar a.aktif .ms { font-variation-settings: "FILL" 1; transform: translateY(-1px); }

  /* Label tab aktif sedikit lebih tegas, tanpa menggeser tab di sebelahnya. */
  .tabbar a::after {
    content: ""; position: absolute; bottom: 1px; left: 50%;
    width: 0; height: 2px; border-radius: var(--r-full);
    background: currentColor; transform: translateX(-50%);
    transition: width .28s var(--pegas);
  }
  .tabbar a.aktif::after { width: 16px; }

  @media (prefers-reduced-motion: reduce) {
    .tabbar-penanda, .tabbar a, .tabbar a .lingkar,
    .tabbar a .ms, .tabbar a::after { transition: none; }
  }
}

@media (max-width: 560px) {
  .riwayat .waktu { flex: 0 0 100px; }
  .krj-total .n { font-size: 22px; }
  .cepat { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- antrean di beranda -------------------------------------------------- */
.antrean { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: var(--xs); }
.antrean a {
  display: block; padding: var(--md); border-radius: var(--r-2xl);
  background: var(--s-low); border: 1px solid transparent; text-decoration: none;
  color: var(--outline);
}
.antrean a:hover { text-decoration: none; border-color: var(--primary-fixed-dim); }
.antrean .ms { color: inherit; }
.antrean .n {
  display: block; font-size: 24px; line-height: 30px; font-weight: 800;
  letter-spacing: -.02em; font-variant-numeric: tabular-nums; margin-top: 2px;
}
.antrean .lb { font-size: 13px; font-weight: 600; }
.antrean a.ada { background: var(--primary-fixed); color: var(--on-primary-fixed-variant); }
.antrean a.tunggu { background: var(--primary); color: #fff; box-shadow: var(--sh-primary); }

/* --- pilihan mode kirim dan panel bot WhatsApp --------------------------- */
.pilih-mode { display: grid; gap: var(--xs); }
.pilih-mode label {
  display: flex; gap: var(--sm); align-items: flex-start; cursor: pointer;
  border: 1px solid var(--garis-tegas); border-radius: var(--r-2xl);
  padding: var(--md); background: var(--s-lowest);
}
.pilih-mode label:hover { border-color: var(--primary-fixed-dim); background: var(--s-low); }
.pilih-mode input { margin-top: 3px; accent-color: var(--primary); width: 20px; height: 20px; flex: 0 0 20px; }
.pilih-mode label:has(input:checked) {
  border-color: var(--primary); background: var(--primary-fixed);
  box-shadow: inset 0 0 0 1px var(--primary);
}
.pilih-mode .jd { display: block; font-size: 15px; font-weight: 700; }
.pilih-mode .ket { display: block; font-size: 13px; line-height: 19px; color: var(--on-surface-variant); margin-top: 2px; }

.bot-kisi { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: var(--lg); align-items: start; }
.bot-panggung { display: flex; flex-direction: column; gap: var(--sm); }

/* Dua cara menyambung, dipilih lewat sepasang tab kecil. */
.bot-cara {
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
  background: var(--s-low); border-radius: var(--r-full); padding: 4px;
}
.bot-cara-tab {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 36px; border: 0; border-radius: var(--r-full); cursor: pointer;
  background: transparent; color: var(--on-surface-variant);
  font: 600 13px/1 var(--font);
  transition: background-color .18s ease, color .18s ease;
}
.bot-cara-tab:hover { color: var(--on-surface); }
.bot-cara-tab.aktif {
  background: var(--s-lowest); color: var(--primary);
  box-shadow: var(--sh-sm);
}

.bot-qr {
  position: relative;
  width: 280px; height: 280px; border-radius: var(--r-2xl);
  border: 1px solid var(--garis); background: var(--s-low);
  display: grid; place-items: center; overflow: hidden;
}
.bot-qr img { width: 100%; height: 100%; object-fit: contain; background: #fff; }
.bot-kosong { text-align: center; color: var(--outline); padding: var(--md); }
.bot-kosong p { margin-top: var(--xs); }

/* Kode QR berganti tiap 20 detik. Pudar-masuk supaya pergantiannya tidak mengagetkan. */
.bot-qr img.segar { animation: qr-masuk .35s ease both; }
@keyframes qr-masuk {
  from { opacity: 0; transform: scale(.97); }
  to   { opacity: 1; transform: scale(1); }
}

.bot-sukses {
  position: absolute; top: 0; right: 0; bottom: 0; left: 0;
  display: grid; place-items: center;
  background: var(--primary-fixed); color: var(--primary);
}
.bot-sukses .ms { width: 84px; height: 84px; }
.bot-sukses.muncul { animation: sukses-masuk .45s cubic-bezier(.34, 1.4, .64, 1) both; }
.bot-sukses.muncul .ms { animation: sukses-cap .5s cubic-bezier(.34, 1.5, .64, 1) .06s both; }
@keyframes sukses-masuk { from { opacity: 0; } to { opacity: 1; } }
@keyframes sukses-cap {
  from { opacity: 0; transform: scale(.4); }
  to   { opacity: 1; transform: scale(1); }
}

/* Sisa umur kode QR, supaya pemilik tahu kapan harus menunggu kode berikutnya. */
.bot-umur { height: 4px; border-radius: var(--r-full); background: var(--s-low); overflow: hidden; }
.bot-umur-isi {
  height: 100%; width: 100%; border-radius: inherit; background: var(--primary);
  transition: width 1s linear, background-color .3s ease;
}
.bot-umur-isi[style*="width: 1"], .bot-umur-isi[style*="width: 2"] { background: var(--error); }

/* --- kode pairing 8 huruf ------------------------------------------------- */
.bot-kode-kotak {
  border: 1px solid var(--garis); border-radius: var(--r-2xl);
  background: var(--s-low); padding: var(--lg) var(--md);
  min-height: 132px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--xs);
}
.bot-kode { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; justify-content: center; }
.bot-kode-huruf {
  display: grid; place-items: center;
  width: 34px; height: 44px; border-radius: var(--r-xl);
  background: var(--s-lowest); border: 1px solid var(--garis);
  font: 700 20px/1 var(--font); color: var(--on-surface);
  font-variant-numeric: tabular-nums; letter-spacing: .5px;
  animation: huruf-masuk .3s cubic-bezier(.34, 1.4, .64, 1) both;
}
.bot-kode-pisah { color: var(--outline); font-weight: 700; padding: 0 2px; }
@keyframes huruf-masuk {
  from { opacity: 0; transform: translateY(6px) scale(.9); }
  to   { opacity: 1; transform: none; }
}
.bot-pair-form { display: flex; flex-direction: column; gap: var(--sm); margin-top: var(--sm); }
.bot-pair-form .ladang { margin: 0; }

.langkah { margin: var(--md) 0 0; padding-left: var(--lg); font-size: 14px; line-height: 22px; }
.langkah li { margin-bottom: 6px; }
/* Langkah-langkah dimunculkan berurutan supaya kebaca sebagai urutan, bukan blok. */
.langkah:not([hidden]) li { animation: langkah-masuk .32s ease both; }
.langkah li:nth-child(1) { animation-delay: .02s; }
.langkah li:nth-child(2) { animation-delay: .07s; }
.langkah li:nth-child(3) { animation-delay: .12s; }
.langkah li:nth-child(4) { animation-delay: .17s; }
@keyframes langkah-masuk {
  from { opacity: 0; transform: translateX(-6px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 720px) {
  .bot-kisi { grid-template-columns: minmax(0, 1fr); }
  /* aspect-ratio baru ada di Chrome 88 (awal 2021). Di tablet Android murah
     yang WebView-nya lebih tua, baris itu dibuang browsernya dan tingginya
     jatuh ke auto - sementara gambarnya memakai height: 100% terhadap induk
     yang tingginya tidak tentu, jadi resolusinya nol. Hasilnya kotak QR
     mengerut habis dan kode QR-nya tidak pernah kelihatan, padahal datanya
     sudah sampai. Gambarnya sekarang menentukan tingginya sendiri, dan
     aspect-ratio cuma jadi perapi di browser yang mendukungnya. */
  .bot-qr {
    width: 100%; max-width: 320px; margin: 0 auto;
    height: auto; min-height: 240px;
    aspect-ratio: 1;
  }
  .bot-qr img { height: auto; }
  .bot-panggung { max-width: 360px; margin: 0 auto; width: 100%; }
}

/* --- status pembayaran ---------------------------------------------------- */
/* Lunas HIJAU - sama dengan .lencana.lunas di halaman lain, dan tidak lagi
   memakai warna brand yang membuatnya terbaca seperti tombol/chip tahap.
   Yang BELUM lunas diberi garis tepi: hijau vs merah pucat hampir sama
   terangnya (1,13:1), jadi buta warna merah-hijau butuh pembeda bentuk. */
.b-lunas { background: #E5F4E8; color: #1D5E28; }
.b-dp    { background: var(--tertiary-fixed); color: var(--on-tertiary-fixed-variant);
           border-color: #7FB2D4; }
.b-belum { background: var(--error-container); color: var(--on-error-container);
           border-color: #C86A66; }

/* ==========================================================================
   Mode hemat
   --------------------------------------------------------------------------
   Dinyalakan sendiri di HP kelas bawah, dan bisa dipaksa lewat sakelar di menu
   "Lainnya". Yang dimatikan adalah tiga hal yang paling mahal digambar ulang:

   1. backdrop-filter pada bilah atas dan bilah tab. Keduanya menempel di layar
      selagi halaman digulir, jadi kaburnya dihitung ulang tiap frame - di GPU
      lama inilah sumber tersendat yang paling terasa.
   2. Animasi masuk kartu dan baris tabel. Daftar dua ratus baris berarti dua
      ratus elemen dianimasikan sekaligus.
   3. Bayangan dan riak sentuh. Murah satu-satu, mahal kalau seluruh halaman.

   Warnanya tidak diubah sama sekali - yang hilang cuma gerak dan kilau.
   ========================================================================== */
body.hemat .topbar,
body.hemat .tabbar {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: var(--s-lowest);
}
body.hemat .tirai { -webkit-backdrop-filter: none; backdrop-filter: none; }
body.hemat .isi .kartu,
body.hemat .isi .papan > .kolom,
body.hemat .isi > .subnav-bungkus,
body.hemat .kartu tbody tr,
body.hemat .flash,
body.hemat .kartu-daftar { animation: none !important; }
body.hemat .kartu,
body.hemat .ubin,
body.hemat .tombol,
body.hemat .ringkas,
body.hemat .kartu-order,
body.hemat .kartu-daftar { box-shadow: none; }
body.hemat .riak { display: none; }
/* Peralihan warna dan bentuk dipersingkat, bukan dihapus: tanpa umpan balik
   sama sekali, ketukan terasa tidak terbaca. */
body.hemat * { transition-duration: 90ms !important; }

/* ==========================================================================
   Gerak
   --------------------------------------------------------------------------
   Animasi di sini semuanya kecil dan pendek: tugasnya menjelaskan apa yang
   baru berubah, bukan menghibur. Yang muncul dianimasikan sekali saat masuk,
   yang berubah nilainya dikedipkan sebentar, dan yang ditekan memberi balasan
   dalam sekitar seperlima detik. Semuanya dimatikan di blok
   prefers-reduced-motion paling bawah berkas ini.
   ========================================================================== */

:root {
  --gerak-cepat: .16s;
  --gerak-sedang: .28s;
  --gerak-lambat: .42s;
  --pegas: cubic-bezier(.34, 1.35, .64, 1);
  --halus: cubic-bezier(.4, 0, .2, 1);
}

/* --- masuk halaman -------------------------------------------------------- */
/* Kartu naik pelan berurutan supaya mata sempat menangkap urutannya. */
@keyframes naik-masuk {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@keyframes pudar-masuk {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Kartu naik berurutan supaya mata sempat menangkap urutannya. Susunan tiap
   halaman berbeda-beda, jadi kartunya dipilih di kedalaman mana pun lalu
   kartu bersarang dikecualikan supaya tidak dianimasikan dua kali. */
.isi .kartu,
.isi .papan > .kolom,
.isi > .subnav-bungkus {
  animation: naik-masuk var(--gerak-sedang) var(--halus) both;
}
.isi .kartu .kartu { animation: none; }

.isi .kartu:nth-child(2), .isi .papan > .kolom:nth-child(2) { animation-delay: .05s; }
.isi .kartu:nth-child(3), .isi .papan > .kolom:nth-child(3) { animation-delay: .1s; }
.isi .kartu:nth-child(4), .isi .papan > .kolom:nth-child(4) { animation-delay: .15s; }
.isi .kartu:nth-child(n+5), .isi .papan > .kolom:nth-child(n+5) { animation-delay: .2s; }

/* --- pesan kilat ---------------------------------------------------------- */
/* Hasil tindakan muncul dari atas, arah yang sama dengan datangnya halaman. */
.flash { animation: flash-masuk var(--gerak-sedang) var(--pegas) both; }
.flash:nth-child(2) { animation-delay: .06s; }
.flash:nth-child(3) { animation-delay: .12s; }
@keyframes flash-masuk {
  from { opacity: 0; transform: translateY(-8px) scale(.99); }
  to   { opacity: 1; transform: none; }
}

/* --- tombol --------------------------------------------------------------- */
.tombol {
  transition: background-color var(--gerak-cepat) ease,
              border-color var(--gerak-cepat) ease,
              color var(--gerak-cepat) ease,
              box-shadow var(--gerak-cepat) ease,
              transform var(--gerak-cepat) var(--halus);
}
.tombol:not(:disabled):active { transform: scale(.97); }
.tombol.utama:not(:disabled):hover { box-shadow: var(--sh-primary); }
.tombol:disabled { transition: none; }

/* Tombol yang menunggu jawaban server: teksnya diganti, ikonnya berputar. */
.tombol.sibuk { pointer-events: none; opacity: .75; }
.tombol.sibuk .ms { animation: putar 1s linear infinite; }
@keyframes putar { to { transform: rotate(360deg); } }

.tombol-bulat { transition: background-color var(--gerak-cepat) ease, transform var(--gerak-cepat) var(--halus); }
.tombol-bulat:active { transform: scale(.9); }

/* --- navigasi ------------------------------------------------------------- */
/* Menu aktif ditandai garis yang menyapu masuk, bukan muncul mendadak. */
.nav a { position: relative; }
.nav a.aktif::before {
  content: ""; position: absolute; left: 0; top: 50%;
  width: 3px; height: 0; border-radius: var(--r-full); background: currentColor;
  transform: translateY(-50%); opacity: .55;
  animation: garis-tumbuh var(--gerak-sedang) var(--halus) .05s both;
}
@keyframes garis-tumbuh { to { height: 60%; } }
.nav a .ms { transition: transform var(--gerak-cepat) var(--pegas); }
.nav a:hover .ms { transform: translateX(1px) scale(1.06); }

/* --- ubin layanan di layar kasir ------------------------------------------ */
.ubin { transition: border-color var(--gerak-cepat) ease, box-shadow var(--gerak-cepat) ease,
                    background-color var(--gerak-cepat) ease, transform var(--gerak-cepat) var(--halus); }
.ubin:active { transform: scale(.97); }
.ubin .ms { transition: color var(--gerak-cepat) ease, transform var(--gerak-sedang) var(--pegas); }
.ubin:hover .ms { transform: translateY(-2px); }
/* Angka jumlah melompat kecil tiap kali nilainya benar-benar berubah. */
.ubin .dipakai.berubah { animation: hitung-naik var(--gerak-sedang) var(--pegas) both; }
@keyframes hitung-naik {
  from { opacity: 0; transform: scale(.5); }
  to   { opacity: 1; transform: scale(1); }
}

/* --- keranjang ------------------------------------------------------------ */
/* Baris baru masuk dari kanan, arah datangnya dari grid layanan di kirinya. */
.krj-item.baru { animation: item-masuk var(--gerak-sedang) var(--pegas) both; }
@keyframes item-masuk {
  from { opacity: 0; transform: translateX(12px); }
  to   { opacity: 1; transform: none; }
}
.krj-item .stepper button { transition: background-color var(--gerak-cepat) ease, transform var(--gerak-cepat) var(--halus); }
.krj-item .stepper button:active { transform: scale(.88); }

/* Nilai uang yang berubah dikedipkan sekali supaya perubahannya terlihat. */
.nilai-berubah { animation: nilai-kedip var(--gerak-lambat) ease both; }
@keyframes nilai-kedip {
  0%   { color: var(--primary); transform: translateY(-2px); }
  100% { color: inherit; transform: none; }
}

/* --- lencana dan status --------------------------------------------------- */
.lencana { transition: background-color var(--gerak-sedang) ease, color var(--gerak-sedang) ease; }
.lencana.kedip { animation: lencana-kedip var(--gerak-lambat) var(--pegas) both; }
@keyframes lencana-kedip {
  0%   { transform: scale(.88); opacity: .4; }
  60%  { transform: scale(1.06); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

/* Ikon penanda "sedang menunggu", dipakai saat penyambung belum menjawab. */
.berdenyut { animation: denyut-samar 1.8s ease-in-out infinite; }
@keyframes denyut-samar {
  0%, 100% { opacity: .35; }
  50%      { opacity: .85; }
}

/* --- tabel dan daftar ----------------------------------------------------- */
tbody tr { transition: background-color var(--gerak-cepat) ease; }
.kartu-order {
  transition: box-shadow var(--gerak-cepat) ease, transform var(--gerak-cepat) var(--halus),
              border-color var(--gerak-cepat) ease;
}
.kartu-order:hover { transform: translateY(-2px); box-shadow: var(--sh-md); }
/* --- garis waktu pesanan -------------------------------------------------- */
/* Tahap yang sudah lewat digambar dari atas ke bawah, seperti urutan aslinya. */
.jalur li { animation: tahap-masuk var(--gerak-sedang) var(--halus) both; }
.jalur li:nth-child(1) { animation-delay: .04s; }
.jalur li:nth-child(2) { animation-delay: .1s; }
.jalur li:nth-child(3) { animation-delay: .16s; }
.jalur li:nth-child(4) { animation-delay: .22s; }
.jalur li:nth-child(5) { animation-delay: .28s; }
.jalur li:nth-child(6) { animation-delay: .34s; }
@keyframes tahap-masuk {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; }
}
.jalur li.belum { animation-name: tahap-masuk-samar; }
@keyframes tahap-masuk-samar {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: .5; }
}

/* --- isian ---------------------------------------------------------------- */
.ladang input, .ladang select, .ladang textarea {
  transition: border-color var(--gerak-cepat) ease, box-shadow var(--gerak-cepat) ease,
              background-color var(--gerak-cepat) ease;
}
/* Daftar saran nama pelanggan turun dari kotak isiannya. */
.saran { animation: saran-turun var(--gerak-cepat) var(--halus) both; }
@keyframes saran-turun {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* --- lembar bawah di layar kecil ------------------------------------------ */
.tirai { transition: opacity var(--gerak-sedang) ease; }
.krj-apung { transition: transform var(--gerak-sedang) var(--pegas), box-shadow var(--gerak-cepat) ease; }
.krj-apung:active { transform: scale(.98); }

/* --- matikan semua kalau pengguna minta gerak dikurangi -------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .jalur li.belum { opacity: .5; }
  .nav a.aktif::before { height: 60%; }
  .tombol:active, .ubin:active, .tombol-bulat:active { transform: none; }
}

/* --- ganti isi tanpa muat ulang ------------------------------------------- */
/* Waktu isi halaman ditukar, kartu yang tidak berubah tidak ikut masuk ulang.
   Kalau semuanya dianimasikan lagi, perubahan yang sebenarnya jadi tenggelam. */
.isi.tanpa-masuk .kartu,
.isi.tanpa-masuk .papan > .kolom,
.isi.tanpa-masuk .kartu-order,
.isi.tanpa-masuk .jalur li,
.isi.tanpa-masuk .flash { animation: none !important; }

/* Kartu yang barusan pindah kolom disorot sebentar. */
.kartu-order.baru-pindah { animation: pindah-sorot 1s var(--halus) both; }
@keyframes pindah-sorot {
  0%   { transform: scale(.96); box-shadow: 0 0 0 3px var(--primary); }
  35%  { transform: scale(1); box-shadow: 0 0 0 3px var(--primary); }
  100% { transform: scale(1); box-shadow: 0 0 0 3px rgba(0, 74, 198, 0); }
}

/* ==========================================================================
   Sentuhan layar
   --------------------------------------------------------------------------
   Android dan iOS punya kebiasaan bawaan yang mengganggu di aplikasi kasir:
   kotak abu-abu berkedip tiap ketukan, jeda 300 milidetik sebelum ketukan
   diproses, tarik-untuk-muat-ulang yang tidak sengaja aktif saat menggulir
   papan, dan pembesaran otomatis saat isian disentuh. Semuanya dimatikan di
   sini. Ukuran huruf isian sengaja 16px karena di bawah itu Safari iOS
   memperbesar halaman sendiri begitu isian disentuh.
   ========================================================================== */

html {
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Tarik-untuk-muat-ulang di tengah pekerjaan bikin isian yang belum disimpan
   hilang, jadi gerakan gulir ditahan di dalam halaman. */
body { overscroll-behavior-y: contain; }

a, button, .tombol, .tombol-bulat, .ubin, .kartu-order, label, summary,
input[type="checkbox"], input[type="radio"], select {
  touch-action: manipulation;
}

/* Kolom papan dan daftar panjang digulir sendiri tanpa menyeret halaman induk.
   Penahannya sengaja hanya di sumbu yang benar-benar digulir. Papan dan tabel
   cuma bergulir mendatar, tapi karena overflow-x yang bukan visible membuat
   overflow-y ikut jadi auto, keduanya tetap terhitung penggulir tegak. Kalau
   ditahan dua sumbu, gerakan jari ke bawah di atas tabel berhenti di situ dan
   halamannya diam - persis rasa "ketahan" saat menggulir daftar pelanggan. */
.papan, .tabel-geser { overscroll-behavior-x: contain; }
.krj-gulir, .sidebar { overscroll-behavior: contain; }

/* Di layar sentuh sebaiknya cuma ada satu penggulir tegak, yaitu halamannya.
   Daftar yang menggulir sendiri di tengah halaman menyerap gerakan jari, jadi
   maksimum tingginya dilepas di sini dan isinya dibiarkan memanjang ke bawah.
   Lembar keranjang tidak ikut karena memang panel terpisah di atas halaman. */
@media (pointer: coarse) {
  .saran { max-height: none; overflow: visible; }
  .kolom > .isi-kolom { max-height: none; overflow: visible; overscroll-behavior: auto; }
}

/* Sasaran sentuh minimal 44px, ukuran yang dipakai pedoman Apple dan Android.
   Di layar sentuh tombol yang lebih kecil dari ini sering meleset. */
@media (pointer: coarse) {
  .tombol, .tombol-bulat, .tabbar a, .nav a { min-height: 44px; }
  .tombol.kecil { min-height: 40px; }
  /* Efek sorot mouse tidak berlaku di layar sentuh dan malah nyangkut
     setelah ketukan, jadi ditiadakan. */
  .ubin:hover { border-color: var(--garis); box-shadow: none; }
  .ubin:hover .ms { transform: none; color: var(--on-surface-variant); }
  .kartu-order:hover { transform: none; box-shadow: var(--sh-sm); }
  .ubin.dipilih:hover .ms { color: var(--primary); }
}

/* Bilah tab bawah menyisakan ruang untuk garis home iPhone. Halaman ikut
   diberi alas setinggi bilah supaya isi terakhir tidak tertutup. */
@media (max-width: 900px) {
  /* main.isi, bukan .ms.isi yang dipakai ikon. Alasnya seluas bilah tab plus
     satu jarak, supaya kartu terakhir tidak tertutup. */
  main.isi { padding-bottom: calc(var(--tabbar-ruang, 94px) + var(--lg)); }

  /* Kalau bilah total kasir sedang tampil, alasnya ditambah setinggi bilah itu
     juga. Tanpa ini ubin layanan terakhir tertutup dua bilah sekaligus. */
  main.isi:has(.krj-apung.ada-isi) {
    padding-bottom: calc(var(--tabbar-ruang, 94px) + 56px + var(--xl));
  }
}

/* Layar dengan poni: sisi kiri-kanan ikut dihindari saat HP dimiringkan. */
@media (orientation: landscape) {
  .rangka { padding-left: env(safe-area-inset-left, 0px); padding-right: env(safe-area-inset-right, 0px); }
  .lacak { padding-left: env(safe-area-inset-left, 0px); padding-right: env(safe-area-inset-right, 0px); }
}

/* --- garis kemajuan ------------------------------------------------------- */
/* Muncul di puncak layar selagi permintaan berjalan. Melaju cepat di awal lalu
   melambat mendekati ujung, karena lamanya jawaban server tidak diketahui. */
.kemajuan {
  position: fixed; top: 0; left: 0; height: 3px; width: 0;
  background: var(--primary); z-index: 90; opacity: 0;
  border-radius: 0 var(--r-full) var(--r-full) 0;
  box-shadow: 0 0 8px rgba(0, 74, 198, .5);
}
.kemajuan.jalan { opacity: 1; animation: kemajuan-maju 8s cubic-bezier(.1, .8, .3, 1) forwards; }
.kemajuan.selesai { opacity: 0; width: 100%; transition: width .18s ease, opacity .3s ease .1s; }
@keyframes kemajuan-maju {
  0%   { width: 0; }
  25%  { width: 55%; }
  60%  { width: 78%; }
  100% { width: 92%; }
}

/* --- ubin antrean di beranda ---------------------------------------------- */
.antrean a {
  transition: transform var(--gerak-cepat) var(--halus),
              box-shadow var(--gerak-cepat) ease, background-color var(--gerak-cepat) ease;
}
.antrean a:hover { transform: translateY(-2px); box-shadow: var(--sh-md); }
.antrean a:active { transform: translateY(0) scale(.98); }
.antrean a .n { transition: transform var(--gerak-sedang) var(--pegas); }
.antrean a:hover .n { transform: scale(1.06); }

/* --- baris tabel ---------------------------------------------------------- */
/* Baris pertama muncul lebih dulu, jadi tabel terbaca sebagai daftar yang
   terisi dari atas, bukan blok yang muncul sekaligus. */
.kartu tbody tr { animation: baris-masuk var(--gerak-cepat) var(--halus) both; }
.kartu tbody tr:nth-child(1) { animation-delay: .02s; }
.kartu tbody tr:nth-child(2) { animation-delay: .05s; }
.kartu tbody tr:nth-child(3) { animation-delay: .08s; }
.kartu tbody tr:nth-child(4) { animation-delay: .11s; }
.kartu tbody tr:nth-child(5) { animation-delay: .14s; }
.kartu tbody tr:nth-child(n+6) { animation-delay: .16s; }
@keyframes baris-masuk {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
.isi.tanpa-masuk .kartu tbody tr { animation: none !important; }

@media (prefers-reduced-motion: reduce) {
  .kemajuan.jalan { animation: none; width: 40%; }
}

/* --- topbar di layar sempit ------------------------------------------------ */
/* Judul dan tombol berbagi satu baris di layar lebar. Di HP tombolnya memakan
   hampir seluruh lebar dan menyisakan kolom selebar beberapa huruf untuk judul,
   sehingga keterangannya pecah jadi satu kata per baris. Di bawah 700px
   keduanya dipisah jadi dua baris. */
@media (max-width: 700px) {
  .topbar {
    flex-wrap: wrap; align-items: center; gap: var(--xs);
    padding: var(--xs) var(--md); min-height: 0;
  }
  /* Judulnya dipotong titik-titik, bukan memaksa tombol turun ke baris sendiri:
     satu baris penuh di bilah yang menempel di layar itu mahal di HP. */
  /* Judul mengambil sisa ruang saja, bukan menuntut jatah sendiri: kalau ia
     memaksa 120px, tombol aksi terdorong turun dan bilahnya jadi dua baris
     padahal sebenarnya muat. */
  .topbar .tumbuh { flex: 1 1 0; min-width: 3.5em; }
  .topbar h1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .topbar-aksi { flex: 0 1 auto; justify-content: flex-end; flex-wrap: wrap; }
}

/* --- tabel di layar HP ------------------------------------------------------
   Tabel tujuh kolom tidak akan pernah muat di layar 360px. Selama ini wadahnya
   digulir menyamping, jadi kasir harus menggeser bolak-balik cuma untuk membaca
   satu baris - dan kolom paling kanan (tombol aksi) nyaris tidak pernah
   ketemu. Di bawah 700px tiap baris disusun ulang jadi satu kartu bertumpuk:
   nama kolom di atas, isinya di bawah. Nama kolomnya diambil dari <thead> lewat
   atribut data-label pada tiap sel, jadi tidak ada teks yang ditulis dua kali.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .tabel-geser { overflow-x: visible; }
  .tabel-geser > table { min-width: 0; }
  .tabel-geser thead { display: none; }
  .tabel-geser table, .tabel-geser tbody, .tabel-geser tfoot,
  .tabel-geser tr, .tabel-geser td { display: block; width: auto; }

  /* Tiap baris jadi kartu kecil supaya batas antar baris jelas; tanpa itu
     tumpukan judul-dan-isi menyatu jadi satu blok panjang yang sulit dibaca. */
  .tabel-geser tbody tr {
    margin: 0 var(--md) var(--xs);
    padding: var(--sm) 0;
    background: var(--s-low);
    border: 1px solid var(--garis);
    border-radius: var(--r-2xl);
  }
  .tabel-geser tbody tr:hover { background: var(--s-low); }
  .tabel-geser tfoot td { border-top: 1px solid var(--garis-tegas); padding-top: var(--sm); }

  .tabel-geser td {
    padding: 3px var(--md); border: 0; text-align: left;
    /* Sel bertumpuk punya seluruh lebar layar, jadi tidak ada lagi alasan
       menahan pembungkusan baris. Tautan lacak yang panjang dipatahkan supaya
       tidak mendorong halaman jadi bisa digeser. */
    white-space: normal; overflow-wrap: anywhere;
  }
  .tabel-geser td.nowrap { white-space: normal; }
  /* Sel yang kepalanya kosong - biasanya kolom tombol - tidak diberi judul. */
  .tabel-geser td[data-label]::before {
    content: attr(data-label);
    display: block; margin-bottom: 1px;
    font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--outline);
  }
  .tabel-geser td.num, .tabel-geser td.kanan { text-align: left; }
  .kartu > .tabel-geser td:first-child,
  .kartu > .tabel-geser td:last-child { padding-left: var(--md); padding-right: var(--md); }
  /* Sel kosong cuma menyisakan judul yang menggantung. */
  .tabel-geser td:empty { display: none; }
}

/* --- subnavigasi uang di layar sempit -------------------------------------- */
/* Di layar lebar kelima tab uang muat dalam satu baris. Di HP baris itu jadi
   lebih panjang dari layarnya, dan tab paling kanan (Laporan) tersembunyi di
   balik tepi sampai digeser. Kasir tidak tahu ada yang tersembunyi di sana.
   Di bawah 700px barisnya dibiarkan membungkus jadi dua baris: tiap tab
   melebar mengisi sisa baris, jadi semuanya kelihatan sekaligus dan tidak ada
   yang perlu digeser. */
@media (max-width: 700px) {
  .subnav {
    display: flex; flex-wrap: wrap; width: 100%; overflow: visible;
    gap: 6px; border-radius: var(--r-3xl); padding: 6px;
  }
  .subnav a {
    flex: 1 1 auto; text-align: center; justify-content: center;
    display: flex; align-items: center; min-height: 42px;
    padding: 8px var(--sm); font-size: 13.5px;
  }
}

/* Ubin angka berdiri dua-dua di HP. Di bawah 360px separuh layar hanya
   menyisakan sekitar 62px di samping lencana bulatnya, dan kata seperti
   "Pengeluaran" terpenggal di tengah. Di lebar itu ubinnya ditumpuk satu per
   baris saja. */
@media (max-width: 359px) {
  .kisi.duo-hp { grid-template-columns: minmax(0, 1fr); }
}

/* --- baris isian di layar sempit ------------------------------------------- */
/* .rapat dipakai untuk tombol yang lebarnya harus mengikuti isinya, dan itu
   benar di layar lebar. Di HP dua tombol ekspor yang berdampingan jadi lebih
   panjang dari layar, dan karena flex-nya 0 0 auto ia tidak mau menyusut:
   tombolnya menembus garis kartu dan halamannya jadi bisa digeser ke samping.
   Di sini tiap kelompok tombol diberi barisnya sendiri lalu isinya dibagi rata. */
@media (max-width: 700px) {
  .baris > .rapat { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: var(--xs); }
  .baris > .rapat > .tombol { flex: 1 1 auto; justify-content: center; }
  .tombol-baris > .tombol { min-width: 0; }
  .tombol { max-width: 100%; }

}

/* Layar 320px masih dipakai HP lama. Di lebar itu nama tombol seperti "Simpan
   pengaturan WhatsApp" lebih panjang dari kartunya, dan karena tombol memakai
   white-space: nowrap dengan tinggi tetap, tulisannya menembus garis. Di sini
   tingginya dibiarkan mengikuti isi supaya turun ke baris kedua. */
@media (max-width: 380px) {
  .tombol {
    white-space: normal; text-align: center; line-height: 18px;
    height: auto; min-height: var(--touch); padding: 10px var(--md);
  }
  .tombol.kecil { height: auto; min-height: 36px; padding: 8px var(--sm); }
}

/* Di 901-1100px menu samping masih tampil, sementara lembar keranjang dan
   bilah total sudah memakai position: fixed yang dihitung dari tepi jendela.
   Keduanya jadi bergeser ke kiri sampai menindih menu samping. Di rentang ini
   juga tidak ada bilah tab bawah, jadi alas halaman yang menghindari bilah
   total ikut hilang dan kartu terakhir tertutup. */
@media (min-width: 901px) and (max-width: 1100px) {
  .keranjang, .krj-apung.ada-isi { left: var(--sidebar-w); }
  main.isi:has(.krj-apung.ada-isi) { padding-bottom: calc(56px + var(--xl)); }
}

/* Kolom papan menyesuaikan isinya, tidak ikut setinggi kolom terpanjang.
   Di HP kolom kosong setinggi layar cuma bikin gulir jadi panjang. */
@media (max-width: 1100px) {
  .papan { align-items: start; }
  .kolom { min-height: 0; }
  .kolom:empty, .kolom > .isi-kolom:empty { min-height: 0; }
}

/* ==========================================================================
   Pindah halaman
   --------------------------------------------------------------------------
   Menu diambil alih skrip supaya isinya ditukar, bukan halaman dimuat ulang.
   Browser yang mendukung View Transitions menggarap silang-pudarnya sendiri,
   dan hasilnya lebih halus daripada animasi buatan tangan karena ia memotret
   keadaan lama dan baru lalu menyilangkannya di lapisan tersendiri.
   ========================================================================== */

main.isi { view-transition-name: isi-halaman; }
.sidebar { view-transition-name: bilah-sisi; }
.tabbar  { view-transition-name: bilah-tab; }
.topbar  { view-transition-name: bilah-atas; }

/* Isi halaman bergeser naik sedikit sambil berganti, arah yang sama dengan
   datangnya halaman baru. */
::view-transition-old(isi-halaman) {
  animation: isi-keluar .18s var(--halus) both;
}
::view-transition-new(isi-halaman) {
  animation: isi-masuk .28s var(--halus) both;
}
@keyframes isi-keluar {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-6px); }
}
@keyframes isi-masuk {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* Menu tetap diam. Ia tidak berpindah, jadi memudarkannya cuma bikin kedip. */
::view-transition-old(bilah-sisi), ::view-transition-new(bilah-sisi),
::view-transition-old(bilah-tab),  ::view-transition-new(bilah-tab) {
  animation: none; mix-blend-mode: normal;
}
::view-transition-old(bilah-atas) { animation: isi-keluar .14s var(--halus) both; }
::view-transition-new(bilah-atas) { animation: isi-masuk .22s var(--halus) both; }

/* Cadangan untuk browser tanpa View Transitions. */
main.isi.masuk { animation: isi-masuk .3s var(--halus) both; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(isi-halaman), ::view-transition-new(isi-halaman),
  ::view-transition-old(bilah-atas), ::view-transition-new(bilah-atas) {
    animation: none;
  }
  main.isi.masuk { animation: none; }
}

/* ==========================================================================
   Riak dan sentuhan kecil
   ========================================================================== */

/* Riak dibuat skrip di titik yang disentuh. Elemen yang bisa beriak dipotong
   di tepinya supaya lingkarannya tidak melimpah keluar bentuk tombol. */
.tombol, .tombol-bulat, .ubin, .tabbar a, .nav a, .antrean a,
.bot-cara-tab, .pilih-mode, .cepat button, .stepper button { overflow: hidden; }

.riak {
  position: absolute; border-radius: 50%; pointer-events: none;
  background: currentColor; opacity: .18; transform: scale(0);
  animation: riak-lebar .55s cubic-bezier(.2, .6, .3, 1) forwards;
}
.tombol.utama .riak, .tabbar a.aktif .lingkar .riak { background: #fff; opacity: .28; }
@keyframes riak-lebar {
  to { transform: scale(1); opacity: 0; }
}

/* --- isian ---------------------------------------------------------------- */
/* Kotak isian yang sedang dipakai diberi cincin tipis, bukan garis tebal, biar
   tata letaknya tidak bergeser satu piksel pun saat disentuh. */
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-fixed);
}
.ladang span, label > span:first-child {
  transition: color var(--gerak-cepat) ease;
}
.ladang:focus-within > span:first-child { color: var(--primary); }

/* Panah pada daftar pilihan ikut berputar saat daftarnya dibuka. */
select { transition: background-image 0s, border-color var(--gerak-cepat) ease,
                     box-shadow var(--gerak-cepat) ease; }

/* Kotak centang dan pilihan bulat memberi balasan saat ditekan. */
input[type="checkbox"], input[type="radio"] {
  transition: transform var(--gerak-cepat) var(--pegas),
              background-color var(--gerak-cepat) ease,
              border-color var(--gerak-cepat) ease;
}
input[type="checkbox"]:active, input[type="radio"]:active { transform: scale(.85); }
input[type="checkbox"]:checked, input[type="radio"]:checked {
  animation: centang-pop var(--gerak-sedang) var(--pegas);
}
@keyframes centang-pop {
  0%   { transform: scale(.7); }
  60%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}

/* Kartu pilihan mode ikut naik saat dipilih. */
.pilih-mode { transition: background-color var(--gerak-cepat) ease,
                          border-color var(--gerak-cepat) ease,
                          transform var(--gerak-cepat) var(--halus); }
.pilih-mode:active { transform: scale(.995); }

/* --- tautan --------------------------------------------------------------- */
/* Garis bawah tumbuh dari kiri, bukan muncul sekaligus. */
.isi a:not(.tombol):not(.kode):not(.merek):not(.pil a):not(.lencana) {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px; background-position: 0 100%;
  background-repeat: no-repeat;
  transition: background-size var(--gerak-sedang) var(--halus);
  text-decoration: none;
}
.isi a:not(.tombol):not(.kode):not(.merek):not(.pil a):not(.lencana):hover {
  background-size: 100% 1px;
}
/* Chip pemilih kategori berbentuk pil, garis bawah di situ salah tempat. */
.pil a { text-decoration: none; background-image: none; }

/* --- kartu dan tabel ------------------------------------------------------ */
.kartu { transition: box-shadow var(--gerak-sedang) ease; }
.ringkas { transition: transform var(--gerak-cepat) var(--halus), box-shadow var(--gerak-cepat) ease; }
.ringkas:hover { transform: translateY(-2px); box-shadow: var(--sh-md); }
tbody tr:hover { background: var(--s-low); }

/* Nomor nota melebar sedikit saat disorot, penanda bahwa ia bisa diklik. */
.kode { transition: color var(--gerak-cepat) ease, letter-spacing var(--gerak-cepat) ease; }
a.kode:hover { letter-spacing: .01em; }

/* --- keadaan kosong ------------------------------------------------------- */
/* Ikon melayang pelan supaya halaman kosong tidak terasa mati. */
.kosong .gambar { animation: melayang 4s ease-in-out infinite; }
@keyframes melayang {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-5px); }
}

/* --- fokus papan tik ------------------------------------------------------ */
/* Penanda fokus untuk yang berpindah dengan Tab, tanpa mengganggu pemakai tetikus. */
:focus-visible {
  outline: 2px solid var(--primary); outline-offset: 2px;
  border-radius: var(--r-xl);
}

@media (prefers-reduced-motion: reduce) {
  .riak { display: none; }
  .kosong .gambar { animation: none; }
  .isi a:not(.tombol):not(.kode):not(.merek):not(.pil a):not(.lencana) { transition: none; }
  .ringkas:hover { transform: none; }
}


/* ==========================================================================
   Pusat Laundry v2: logo dan papan angka (numpad)
   ========================================================================== */
.merek { display: flex; align-items: center; gap: 10px; }
.merek .logo { flex: 0 0 auto; border-radius: 6px; box-shadow: var(--sh-sm); }
.merek-teks { min-width: 0; }
.merek-teks b { display: block; font-size: 18px; line-height: 24px; font-weight: 800;
  letter-spacing: -.02em; color: var(--primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.merek-teks small { font-size: 13px; font-weight: 600; color: var(--on-surface-variant); }
.masuk-merek .logo { margin: 0 auto 10px; display: block; border-radius: 10px;
  box-shadow: var(--sh-md); }

.hp-jebakan { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

.pad-layar { width: 100%; height: 64px; padding: 0 16px; margin-bottom: 12px;
  border: 1px solid var(--garis-tegas); border-radius: var(--r-2xl);
  background: var(--s-low); color: var(--on-surface); font-family: var(--font);
  font-size: 30px; font-weight: 800; text-align: right;
  font-variant-numeric: tabular-nums; }
.pad-layar:focus { outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(232, 64, 42, .18); }
.pad-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
  margin: 12px 0 4px; }
.pad-grid button { min-height: 58px; font-family: var(--font); font-size: 22px;
  font-weight: 700; cursor: pointer; border: 1px solid var(--garis);
  border-radius: var(--r-2xl); background: var(--s-lowest); color: var(--on-surface);
  box-shadow: var(--sh-sm); }
.pad-grid button:active { transform: scale(.96); }
.pad-grid button:disabled { opacity: .3; cursor: default; }
.pad-grid .pad-hapus { color: var(--primary); font-weight: 800; }

/* ==========================================================================
   Papan cucian di HP: halaman penuh, geser menyamping per kolom
   --------------------------------------------------------------------------
   Sebelumnya papan cuma strip pendek dengan scrollbar sendiri dan halaman
   putih kosong di bawahnya. Sekarang papan MENGISI layar: tiap kolom hampir
   selebar layar (kolom berikutnya mengintip di tepi sebagai isyarat geser),
   gerakan menyamping mengunci per kolom (scroll-snap), dan isi kolom yang
   panjang digulir ke bawah di dalam kolomnya sendiri. Aturan "satu penggulir
   tegak per layar" tetap terjaga: halamannya sendiri tidak bergulir, kolomlah
   penggulirnya.
   180px = topbar 56 + jarak atas isi 20 + ruang tabbar 104.
   ========================================================================== */
@media (max-width: 900px) {
  .papan {
    margin-inline: calc(var(--pad) * -1);        /* rentang penuh dari tepi ke tepi */
    padding: 0 var(--pad);
    height: calc(100dvh - 180px);
    min-height: 340px;
    grid-auto-columns: min(86vw, 340px);
    align-items: stretch;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--pad);
    scrollbar-width: none;                       /* isyaratnya kolom yang mengintip */
  }
  .papan::-webkit-scrollbar { display: none; }
  .kolom {
    scroll-snap-align: start;
    scroll-snap-stop: always;                    /* satu gerakan = satu kolom */
    height: 100%;
    min-height: 0;
  }
  .kolom > .isi-kolom {
    flex: 1;
    max-height: none;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding-bottom: var(--sm);
  }
}

/* Halaman Order di HP, penyempurnaan: TIDAK ADA area mati.
   Tinggi papan tidak lagi ditebak dengan calc (yang meleset di sebagian HP,
   menyisakan ruang kosong yang tidak merespons geseran). Sekarang kerangka
   halaman dikunci setinggi layar dan papan mengisi SEMUA sisa ruang sampai
   bilah tab, jadi geseran ke mana pun - kanan-kiri di area kosong sekalipun -
   selalu mendarat di papan dan menggerakkannya. */
@media (max-width: 900px) {
  .rangka:has(.papan) { height: 100vh; height: 100dvh; overflow: hidden; }
  main.isi:has(> .papan) {
    flex: 1 1 auto; min-height: 0; overflow: hidden;
    display: flex; flex-direction: column;
  }
  main.isi:has(> .papan) > .papan { flex: 1 1 auto; height: auto; min-height: 0; }
}

/* Perbaikan geser mendatar di papan HP.
   overflow-y:auto pada .isi-kolom diam-diam menghitung overflow-x jadi auto
   juga, sehingga isi kolom terhitung "penggulir sumbu-x tanpa isi" - dan
   overscroll-behavior contain membuat SETIAP geseran mendatar yang dimulai
   di dalam kolom ditelan di situ, tidak pernah sampai ke papan. Sumbu-x
   dibuka supaya geseran merambat ke papan; sumbu-y tetap ditahan supaya
   gulir kartu tidak menyeret halaman. */
@media (max-width: 900px) {
  .kolom > .isi-kolom {
    overscroll-behavior-x: auto;
    overscroll-behavior-y: contain;
    touch-action: pan-x pan-y;
  }
}

/* Papan HP tanpa fitur CSS modern.
   Perbaikan sebelumnya bergantung pada :has() dan satuan dvh - dua fitur
   yang belum ada di sebagian browser HP, sehingga di perangkat itu papan
   kembali pendek dan area di bawahnya mati untuk geseran. Halaman papan kini
   menandai <body> dengan kelas biasa (halaman-papan) dari templatenya, dan
   rantai tinggi diturunkan dengan flexbox polos yang dimengerti semua
   browser. vh ditulis lebih dulu sebagai cadangan; dvh menimpanya hanya di
   browser yang mengenalnya. */
@media (max-width: 900px) {
  body.halaman-papan { height: 100vh; height: 100dvh; overflow: hidden; }
  body.halaman-papan .rangka { height: 100%; min-height: 0; overflow: hidden; }
  body.halaman-papan .panel-utama { height: 100%; min-height: 0; }
  body.halaman-papan main.isi {
    flex: 1 1 auto; min-height: 0; overflow: hidden;
    display: flex; flex-direction: column;
  }
  body.halaman-papan .papan { flex: 1 1 auto; height: auto; min-height: 0; }

  /* Di papan, tinggi layar dikunci jadi kolomnya HARUS bergulir sendiri -
     aturan sentuh umum (satu penggulir saja) mematikannya, membuat kartu
     terakhir tenggelam di balik bilah tab dan tombolnya sulit dipencet.
     Alas bawah setinggi bilah tab menaikkan kartu terakhir supaya lega. */
  body.halaman-papan .kolom > .isi-kolom {
    overflow-y: auto; min-height: 0;
    padding-bottom: calc(var(--tabbar-ruang, 94px) + var(--md));
  }
}

/* Alas halaman kasir saat bilah "Total pesanan" melayang tampil - versi tanpa
   :has(). Kelas ada-apung dipasang-lepas skrip pada main.isi mengikuti isi
   keranjang; nilainya sama dengan aturan :has di atas, tapi jalan juga di
   browser HP yang belum mengenal :has(). */
@media (max-width: 900px) {
  main.isi.ada-apung { padding-bottom: calc(var(--tabbar-ruang, 94px) + 56px + var(--xl)); }
}
@media (min-width: 901px) and (max-width: 1100px) {
  main.isi.ada-apung { padding-bottom: calc(56px + var(--xl)); }
}

/* Papan angka nominal uang: menumpang kelas pad yang sudah ada. */
#pad-uang .tombol-bulat { font-size: 24px; line-height: 1; }
#pad-uang .pu-ribu { font-size: 16px; letter-spacing: 1px; }

/* ==========================================================================
   Dialog jumlah & nominal uang di layar pendek.
   Di HP pendek dialognya bisa lebih tinggi dari layar: tombol Masukkan
   terdorong keluar dan kasir harus menggulir-gulir. Dua lapis perbaikan:
   (1) kartunya dibatasi setinggi layar; kepala dan kaki (tombol Masukkan)
   selalu menempel terlihat, hanya bagian tengah yang bergulir kalau
   benar-benar sempit; (2) di layar pendek ukuran tombol dan angka dipadatkan
   supaya umumnya muat utuh tanpa gulir sama sekali.
   ========================================================================== */
.lembar-jumlah > .kartu {
  max-height: calc(100% - 16px);
  display: flex; flex-direction: column;
}
.lembar-jumlah > .kartu > .kepala,
.lembar-jumlah > .kartu > .kaki { flex: 0 0 auto; }
.lembar-jumlah > .kartu > .badan {
  overflow-y: auto; min-height: 0;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
}

@media (max-height: 720px) {
  .lembar-jumlah > .kartu > .kepala { padding: 10px 16px 2px; }
  .lembar-jumlah > .kartu > .badan { padding: 6px 16px 10px; }
  .lembar-jumlah > .kartu > .kaki { padding: 10px 16px; }
  .lembar-jumlah .pad-layar { height: 46px; font-size: 22px; margin-bottom: 6px; }
  .lembar-jumlah .pad-grid { gap: 6px; margin: 8px 0 0; }
  .lembar-jumlah .pad-grid button { min-height: 44px; font-size: 18px; }
}


/* Lembar keranjang dipindah ke level <body> saat dibuka (lihat app.js), jadi
   position:fixed-nya tidak bisa dijebak elemen leluhur ber-transform. Di level
   body, tirai (z-index 55) dan lembar (60) otomatis menutup tabbar (50) dan
   bilah total (52) - tanpa trik menyembunyikan mereka satu per satu. */

/* Keranjang kosong: ajakan yang enak dilihat, bukan deretan Rp0. */
.krj-kosong {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 10px; padding: 40px 24px; color: var(--on-surface-variant);
}
.krj-kosong .ms { color: var(--outline-variant); width: 40px; height: 40px; }
.krj-kosong p { margin: 0; font-size: 14px; line-height: 20px; }
.krj-kosong b { color: var(--on-surface); font-size: 15px; }

/* --- ambil di papan: uang & kembalian ------------------------------------- */
/* Kartu papan sempit (198px), jadi barisnya pakai margin, bukan gap flex -
   browser HP kasir belum mengenal gap dan jaraknya jadi nol. */
.ambil-tagih {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-bottom: 6px; font-size: 12px;
}
.ambil-tagih .n { font-weight: 700; font-size: 14px; color: var(--on-surface); }
/* Tingginya dikunci supaya kartu tidak melompat waktu kembalian muncul. */
.ambil-kembali {
  margin: 0 0 6px; min-height: 16px; font-size: 12px; line-height: 16px;
  font-weight: 700; text-align: center; color: var(--primary);
}
.ambil-kembali.kurang { color: var(--error); }

/* Keterangan singkat di bawah kotak sisa tagihan halaman lacak. */
.lacak-untuk {
  margin: 8px 0 0; font-size: 13px; line-height: 19px;
  color: var(--on-surface-variant);
}
/* Rincian sisa: barisnya dirapatkan supaya blok ini terbaca sebagai lampiran
   pil tagihan di atasnya, bukan sebagai daftar layanan kedua. */
.lacak-untuk .jdl { margin-bottom: 2px; }
.lacak-panel .lacak-untuk .baris-nota { font-size: 14px; padding: 4px 0; }
