/* ==========================================================================
   Arunika-POS (BrewPOS) - Minimalist Design System

   Netral sebagai fondasi, satu aksen. Hierarki dibangun dari tipografi, jarak,
   dan perataan -- bukan dari border tebal, blok warna, atau bayangan berlapis.
   Halaman ini padat data (POS, laporan, HPP); tiap elemen dekoratif yang
   dibiarkan tinggal akan bersaing dengan angkanya sendiri.

   Nama token TIDAK diubah, hanya nilainya. --color-secondary dipakai 19 kali dan
   --font-heading 38 kali di seluruh stylesheet dan templat; mengganti namanya
   berarti menyunting semuanya tanpa satu pun perbedaan yang terlihat.
   ========================================================================== */

/* Satu keluarga huruf untuk seluruh antarmuka, plus mono khusus struk. 'Outfit'
   dilepas: dua sans yang mirip tidak menghasilkan hierarki, hanya satu unduhan
   font tambahan -- dan lewat Tailscale itu terasa. */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap');

:root {
  /* Typography */
  --font-heading: 'Plus Jakarta Sans', sans-serif;
  --font-body: 'Plus Jakarta Sans', sans-serif;
  --font-mono: 'Space Mono', monospace;

  /* Netral. Tangga abu yang rapat: bedanya cukup untuk memisahkan lapisan,
     tidak cukup untuk menarik perhatian. */
  --bg-main: #FFFFFF;
  --bg-sidebar: #FFFFFF;
  --bg-card: #FFFFFF;
  --bg-surface: #F7F7F7;       /* panel/kolom pembanding */
  --bg-input: #FFFFFF;
  --bg-hover: #F2F2F2;

  /* Garis rambut. Inilah pengganti bayangan sebagai pemisah antar bidang. */
  --border-color: #E5E5E5;
  --border-light: #EFEFEF;
  --border-focus: #5A3A28;

  /* Aksen tunggal. Cokelat espresso dipertahankan -- ini identitas kafenya,
     dan "aksen yang sangat terbatas" berarti satu, bukan nol. Dipekatkan dari
     #6F4E37 supaya teks putih di atasnya lolos kontras AA. */
  --color-primary: #5A3A28;
  --color-primary-hover: #422A1C;
  --color-primary-light: #F4F1EF; /* jejak aksen paling tipis, nyaris netral */
  /* Bukan warna kedua, melainkan abu gelap: dipakai untuk penekanan sekunder
     yang dulu memakai caramel. */
  --color-secondary: #6B6B6B;
  --color-secondary-light: #F7F7F7;
  --color-accent: #5A3A28;

  /* Status TETAP berwarna: ini informasi, bukan hiasan. Kasir harus bisa
     membedakan stok habis dari stok aman tanpa membaca teksnya. Yang diredam
     adalah latarnya -- abu netral tipis, bukan pastel. */
  --color-success: #1F6F3F;
  --color-success-bg: #F2F6F3;
  --color-warning: #9A5B00;
  --color-warning-bg: #F8F5F0;
  --color-danger: #A32020;
  --color-danger-bg: #FAF3F3;
  --color-info: #12608F;
  --color-info-bg: #F2F5F8;

  /* Teks. Hierarki utama halaman ini hidup di sini, bukan di warna latar. */
  --text-main: #111111;
  --text-secondary: #555555;
  --text-muted: #8A8A8A;
  --text-dark: #000000;
  --text-inverse: #FFFFFF;

  /* Bayangan nyaris hilang. Yang tersisa hanya untuk lapisan yang benar-benar
     mengambang di atas halaman (modal, dropdown, bottom sheet) -- di sana
     bayangan menjelaskan susunan, bukan menghias. */
  --shadow-xs: none;
  --shadow-sm: none;
  --shadow-md: 0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.07);
  --shadow-modal: 0 8px 32px rgba(0, 0, 0, 0.12);

  /* Radius diturunkan satu tingkat: sudut yang terlalu bulat membaca sebagai
     "ramah", dan itu bukan nada yang dicari di sini. */
  --radius-xs: 4px;
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-full: 9999px;
}

/* Base Reset */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Global type scale.
   Every font-size in this stylesheet and in the templates is written in rem, so this
   single value scales all UI text at once. Bump it to enlarge the whole interface.
   Receipt and @media print rules deliberately stay in physical px/mm: they have to
   match the paper, not the screen. */
html {
  font-size: 18px;
}

body {
  font-family: var(--font-body);
  background-color: var(--bg-main);
  color: var(--text-main);
  min-height: 100vh;
  display: flex;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* App Container Layout */
.app-container {
  display: flex;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background-color: var(--bg-main);
}

/* Modern Sidebar */
.sidebar {
  width: 270px;
  background-color: var(--bg-sidebar);
  border-right: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  z-index: 100;
  box-shadow: var(--shadow-sm);
  transition: width 0.3s ease;
}

.sidebar-header {
  padding: 22px 20px;
  display: flex;
  align-items: center;
  gap: 14px;
  border-bottom: 1px solid var(--border-color);
}

/* Blok aksen polos, bukan gradien. Gradien adalah tekstur, dan di keluarga ini
   tekstur nyaris tidak ada -- yang tersisa hanya bidang, garis, dan jarak. */
.brand-logo {
  width: 40px;
  height: 40px;
  background: var(--color-primary);
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.125rem;
  color: var(--text-inverse);
}

.brand-info h1 {
  font-family: var(--font-heading);
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-main);
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.brand-info p {
  font-size: 0.625rem;
  color: var(--text-muted);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.sidebar-nav {
  flex: 1;
  padding: 16px 14px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.nav-section-title {
  font-size: 0.625rem;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 18px 12px 6px;
  font-weight: 600;
  letter-spacing: 0.1em;
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  color: var(--text-secondary);
  text-decoration: none;
  border-radius: var(--radius-sm);
  font-size: 0.8438rem;
  font-weight: 500;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.nav-item i {
  font-size: 0.9375rem;
  width: 20px;
  text-align: center;
  color: var(--text-muted);
  transition: color 0.15s ease;
}

.nav-item:hover {
  background-color: var(--bg-hover);
  color: var(--text-main);
}

.nav-item:hover i {
  color: var(--text-main);
}

/* Halaman aktif ditandai teks yang menghitam dan menebal, bukan blok warna.
   Sidebar ini punya sebelas tautan; satu blok terisi di antaranya sudah cukup
   menonjol tanpa perlu latar berwarna. */
.nav-item.active {
  background-color: var(--bg-hover);
  color: var(--text-main);
  font-weight: 600;
}

.nav-item.active i {
  color: var(--color-primary);
}

.sidebar-footer {
  padding: 16px 14px;
  border-top: 1px solid var(--border-color);
  background-color: var(--bg-sidebar);
}

.user-profile-badge {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
}

.user-avatar {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-primary-light);
  color: var(--color-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 0.9375rem;
}

.user-meta {
  flex: 1;
  min-width: 0;
}

.user-name {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-main);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.user-role {
  font-size: 0.6562rem;
  font-weight: 700;
  color: var(--color-secondary);
  text-transform: uppercase;
}

/* Collapsed sidebar: icon rail. Only meaningful >=1024px, since below that the
   sidebar is already off-canvas and those rules sit later in this file. */
.sidebar.collapsed {
  width: 76px;
}

.sidebar.collapsed .brand-info,
.sidebar.collapsed .nav-section-title,
.sidebar.collapsed .nav-item span,
.sidebar.collapsed .user-meta {
  display: none;
}

.sidebar.collapsed .sidebar-header,
.sidebar.collapsed .user-profile-badge {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}

/* The 76px rail cannot fit avatar + logout side by side, so stack them. */
.sidebar.collapsed .sidebar-footer {
  padding: 12px 8px;
}

.sidebar.collapsed .user-profile-badge {
  flex-direction: column;
  gap: 8px;
  padding-top: 10px;
  padding-bottom: 10px;
}

.sidebar.collapsed .nav-item {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}

.sidebar.collapsed .sidebar-toggle {
  display: none;
}

/* Suppress the width transition on first paint so a restored collapsed state does
   not animate open then snap shut on every page load. */
.sidebar.no-transition {
  transition: none;
}

.sidebar-toggle {
  margin-left: auto;
  border: none;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 6px;
  border-radius: var(--radius-xs);
  font-size: 0.875rem;
  transition: all 0.15s ease;
}

.sidebar-toggle:hover {
  background: var(--bg-surface);
  color: var(--color-primary);
}

/* Floating re-expand handle, the only affordance left once the rail is collapsed */
.sidebar-expand {
  display: none;
  border: none;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 6px;
  border-radius: var(--radius-xs);
  font-size: 0.875rem;
}

.sidebar.collapsed .sidebar-expand {
  display: block;
  margin: 8px auto 0;
}

.sidebar-expand:hover {
  background: var(--bg-surface);
  color: var(--color-primary);
}

/* Profile page */
.profile-layout {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 22px;
  align-items: start;
}

.profile-identity {
  display: flex;
  align-items: center;
  gap: 16px;
}

.profile-avatar {
  width: 68px;
  height: 68px;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: var(--text-inverse);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-size: 1.75rem;
  font-weight: 800;
}

.profile-username {
  font-family: var(--font-heading);
  font-size: 1.375rem;
  font-weight: 800;
  color: var(--text-main);
  line-height: 1.2;
  word-break: break-word;
}

.profile-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--border-light);
}

.profile-facts dt {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--text-muted);
  font-weight: 700;
  margin-bottom: 4px;
}

.profile-facts dd {
  font-size: 0.8438rem;
  font-weight: 700;
  color: var(--text-main);
}

.profile-pages {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* Main Wrapper */
.main-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  background-color: var(--bg-main);
}

/* Topbar */
.topbar {
  height: 68px;
  padding: 0 28px;
  background-color: var(--bg-sidebar);
  border-bottom: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: space-between;
  box-shadow: var(--shadow-xs);
  flex-shrink: 0;
}

.page-title h2 {
  font-family: var(--font-heading);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--text-main);
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 14px;
}

.shift-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  font-size: 0.7812rem;
  font-weight: 700;
  border: 1px solid transparent;
}

.shift-status-pill.open {
  background-color: var(--color-success-bg);
  color: var(--color-success);
  border-color: rgba(46, 125, 50, 0.25);
}

.shift-status-pill.closed {
  background-color: var(--color-danger-bg);
  color: var(--color-danger);
  border-color: rgba(211, 47, 47, 0.25);
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: currentColor;
}

/* Content Area */
.content-area {
  flex: 1;
  padding: 24px 28px;
  overflow-y: auto;
}

/* Kartu. Dibatasi garis rambut, bukan bayangan: begitu bayangannya hilang,
   yang memisahkan kartu dari halaman tinggal garis dan jarak -- dan itu cukup. */
.card {
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 26px;
  transition: border-color 0.15s ease;
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 22px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border-light);
}

/* Judul kartu tidak lagi berteriak. Yang membedakannya dari isi adalah bobot
   dan jarak di bawahnya, bukan ukuran yang melompat. */
.card-title {
  font-family: var(--font-heading);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-main);
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Stats KPI Grid */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 18px;
  margin-bottom: 24px;
}

/* Kartu KPI. Garis aksen tebal di sisi kiri dilepas: warnanya sama di semua
   kartu, jadi ia tidak pernah membedakan apa pun -- hanya menambah berat pada
   deretan angka yang justru harus jadi hal paling menonjol di halaman. */
.stat-card {
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 20px 22px;
  display: flex;
  align-items: center;
  gap: 16px;
}

.stat-icon {
  width: 42px;
  height: 42px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.125rem;
  flex-shrink: 0;
}

.stat-info {
  flex: 1;
}

.stat-label {
  font-size: 0.6875rem;
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 6px;
}

/* Angkanya yang jadi jangkar visual halaman, bukan kotaknya. */
.stat-value {
  font-family: var(--font-heading);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-main);
  line-height: 1.1;
}

/* Buttons (Material 3 Touch Style) */
/* Tombol. Umpan balik hover hanya mengubah warna -- tidak ada yang terangkat,
   bersinar, atau berbayang. Di halaman sepadat POS, tombol yang melompat tiap
   kali kursor lewat membuat seluruh baris aksi terasa gelisah. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: var(--radius-sm);
  font-size: 0.8438rem;
  font-weight: 600;
  cursor: pointer;
  border: 1px solid transparent;
  text-decoration: none;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  font-family: var(--font-body);
}

.btn-primary {
  background-color: var(--color-primary);
  color: var(--text-inverse);
}

.btn-primary:hover {
  background-color: var(--color-primary-hover);
}

/* Sekunder = garis tipis di atas latar polos. Bedanya dengan primary adalah
   ada-tidaknya bidang terisi, bukan dua warna yang bersaing. */
.btn-secondary {
  background-color: transparent;
  border-color: var(--border-color);
  color: var(--text-secondary);
}

.btn-secondary:hover {
  background-color: var(--bg-hover);
  border-color: var(--text-muted);
  color: var(--text-main);
}

.btn-success {
  background-color: var(--color-success);
  color: var(--text-inverse);
}

.btn-success:hover {
  background-color: #17532F;
}

.btn-danger {
  background-color: var(--color-danger);
  color: var(--text-inverse);
}

.btn-danger:hover {
  background-color: #7E1818;
}

.btn-warning {
  background-color: var(--color-warning);
  color: var(--text-inverse);
}

.btn-sm {
  padding: 7px 13px;
  font-size: 0.75rem;
  border-radius: var(--radius-xs);
}

.btn-lg {
  padding: 14px 24px;
  font-size: 1rem;
  border-radius: var(--radius-md);
}

.btn-icon {
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: var(--radius-sm);
}

.btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none !important;
  box-shadow: none !important;
}

/* Form Controls */
.form-group {
  margin-bottom: 16px;
}

.form-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 7px;
}

.form-control {
  width: 100%;
  padding: 10px 14px;
  background-color: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  color: var(--text-main);
  font-size: 0.8438rem;
  font-family: var(--font-body);
  transition: border-color 0.15s ease;
}

/* Fokus ditandai garisnya sendiri yang menghitam, bukan cincin tebal di
   sekelilingnya. Di formulir rapat seperti Catat Pembelian, cincin 3px membuat
   kotak yang sedang diisi terlihat membengkak dan menggeser tetangganya. */
.form-control:focus {
  outline: none;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 1px var(--border-focus);
}

.form-control::placeholder {
  color: var(--text-muted);
  opacity: 0.8;
}

/* Tabel. Hanya garis horizontal: kolom dipisahkan oleh perataan dan jarak,
   dan garis vertikal pada tabel selebar layar hanya menambah kebisingan. */
.table-responsive {
  width: 100%;
  overflow-x: auto;
}

.custom-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8125rem;
  text-align: left;
}

.custom-table th {
  background-color: transparent;
  color: var(--text-muted);
  font-weight: 600;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border-color);
  text-transform: uppercase;
  font-size: 0.6562rem;
  letter-spacing: 0.09em;
}

.custom-table td {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border-light);
  color: var(--text-main);
  vertical-align: middle;
}

.custom-table tr:hover td {
  background-color: var(--bg-surface);
}

/* Lencana status. Latar netral tipis + teks berwarna: warnanya tetap terbaca
   sekilas, tanpa deretan blok warna penuh yang membuat tabel terlihat belang. */
.badge-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--radius-xs);
  font-size: 0.6875rem;
  font-weight: 600;
}

.badge-chip.primary {
  background-color: var(--color-primary-light);
  color: var(--color-primary);
}

.badge-chip.success {
  background-color: var(--color-success-bg);
  color: var(--color-success);
}

.badge-chip.warning {
  background-color: var(--color-warning-bg);
  color: var(--color-warning);
}

.badge-chip.danger {
  background-color: var(--color-danger-bg);
  color: var(--color-danger);
}

.badge-chip.info {
  background-color: var(--color-info-bg);
  color: var(--color-info);
}

/* Modal Backdrop & Dialog */
.modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(25, 15, 10, 0.45);
  backdrop-filter: blur(4px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  padding: 16px;
  box-sizing: border-box;
}

.modal-backdrop.show {
  opacity: 1;
  pointer-events: auto;
}

.modal-dialog {
  background-color: var(--bg-card);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-modal);
  border: 1px solid var(--border-color);
  transform: scale(0.98);
  /* Melambat lalu berhenti, tanpa memantul melewati ukuran akhirnya. Pantulan
     menarik mata ke gerakannya sendiri; di sini modal cuma perlu muncul. */
  transition: transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

.modal-backdrop.show .modal-dialog {
  transform: scale(1);
}

/* Banyak modal membungkus body+footer dalam <form>. Tanpa aturan ini form
   tersebut jadi flex item biasa dengan min-height:auto, menolak menyusut, dan
   footernya (tombol Simpan) terdorong keluar layar di HP. */
.modal-dialog > form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.modal-header {
  padding: 18px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border-color);
  flex-shrink: 0;
}

.modal-title {
  font-family: var(--font-heading);
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--color-primary);
}

.modal-close {
  background: transparent;
  border: none;
  /* Dulu 1.5rem untuk glif "x" yang tipis. Ikon fa-xmark digambar jauh lebih
     tebal, jadi pada ukuran yang sama ia mendominasi judul dialog. */
  font-size: 1.125rem;
  color: var(--text-muted);
  cursor: pointer;
  line-height: 1;
  padding: 4px 8px;
  border-radius: var(--radius-xs);
  transition: color 0.2s ease;
}

.modal-close:hover {
  color: var(--color-danger);
}

.modal-body {
  padding: 20px 24px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* Tinggi body mengikuti sisa ruang dialog, bukan hasil calc() yang menebak
     tinggi header+footer. Tebakan itu meleset begitu footer menumpuk dua
     tombol di HP, dan tombol Simpan terdorong keluar layar. min-height:0
     wajib supaya flex item ini boleh menyusut dan menggulung isinya. */
  flex: 1 1 auto;
  min-height: 0;
}

.modal-footer {
  padding: 14px 24px;
  border-top: 1px solid var(--border-color);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  background-color: var(--bg-surface);
  border-radius: 0 0 var(--radius-xl) var(--radius-xl);
  flex-shrink: 0;
}

/* Toast Notifications */
#toast-container {
  position: fixed;
  top: 24px;
  right: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 9999;
}

.toast {
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 14px 18px;
  box-shadow: var(--shadow-lg);
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 0.8438rem;
  font-weight: 500;
  color: var(--text-main);
  min-width: 280px;
  animation: slideInToast 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes slideInToast {
  from { transform: translateX(12px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

/* Garis penanda dipertahankan -- berhasil dan gagal harus bisa dibedakan
   sebelum teksnya sempat dibaca -- tapi menyusut ke 2px supaya setebal garis
   lain di antarmuka ini. Ikonnya sudah membawa warna yang sama. */
.toast.success { border-left: 2px solid var(--color-success); }
.toast.success i { color: var(--color-success); font-size: 1rem; }
.toast.error { border-left: 2px solid var(--color-danger); }
.toast.error i { color: var(--color-danger); font-size: 1rem; }
.toast.warning { border-left: 2px solid var(--color-warning); }
.toast.warning i { color: var(--color-warning); font-size: 1rem; }
.toast.info { border-left: 2px solid var(--color-info); }
.toast.info i { color: var(--color-info); font-size: 1rem; }

/* ==========================================================================
   POS Specific Styles (Product Cards, Split Cart Panel, Cash Presets)
   ========================================================================== */

.pos-layout {
  display: grid;
  /* Katalog | Member | Keranjang. The right region holds the last two side by side. */
  grid-template-columns: 1fr 728px;
  gap: 22px;
  height: calc(100vh - 116px);
}

.pos-catalog-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.pos-categories-bar {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding-bottom: 8px;
  margin-bottom: 12px;
  flex-shrink: 0;
}

.category-chip {
  padding: 8px 18px;
  background-color: var(--text-inverse);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  font-size: 0.8125rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.2s ease;
  box-shadow: var(--shadow-xs);
}

.category-chip:hover {
  border-color: var(--color-secondary);
  color: var(--color-primary);
  background-color: var(--bg-surface);
}

.category-chip.active {
  background-color: var(--color-primary);
  color: var(--text-inverse);
  border-color: var(--color-primary);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.pos-search-bar {
  margin-bottom: 14px;
  flex-shrink: 0;
}

/* Grid, bukan multi-kolom. Dengan column-width browser harus menata ulang
   SELURUH kartu setiap kali daftarnya berubah atau area ini di-scroll -- itu
   sumber tersendat paling terasa di HP saat mengetik di pencarian menu. */
.menu-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(165px, 1fr));
  gap: 12px;
  align-content: start;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-right: 6px;
  padding-bottom: 18px;
  flex: 1;
}

.menu-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  background-color: var(--text-inverse);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
  position: relative;
  box-shadow: var(--shadow-xs);
}

.menu-card:hover {
  transform: translateY(-2px);
  border-color: var(--color-secondary);
  box-shadow: var(--shadow-md);
}

.menu-card-img {
  width: 100%;
  height: 80px;
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.75rem;
  color: var(--text-inverse);
  margin-bottom: 8px;
  object-fit: cover;
  overflow: hidden;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
  /* Kartu kini sebaris tingginya (grid); gambar tidak boleh ikut memipih. */
  flex: 0 0 80px;
}

.menu-card-title {
  font-family: var(--font-heading);
  font-size: 0.8438rem;
  font-weight: 700;
  color: var(--text-main);
  margin-bottom: 2px;
  line-height: 1.25;
}

.menu-card-desc {
  font-size: 0.6875rem;
  color: var(--text-muted);
  margin-bottom: 6px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.3;
}

.menu-card-desc:empty {
  display: none;
  margin-bottom: 0;
}

.menu-card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  /* Harga menempel di dasar kartu, sejajar antar kartu satu baris. */
  margin-top: auto;
  padding-top: 6px;
}

.menu-card-price {
  font-family: var(--font-heading);
  font-size: 0.8438rem;
  font-weight: 800;
  color: var(--color-primary);
  /* "Rp 28.000" tidak boleh pecah jadi dua baris saat kartunya menyempit */
  white-space: nowrap;
}

/* Keranjang kosong. Di desktop boleh lapang; di HP tidak.
   Sebagai bottom sheet, blok ini ada DI ATAS panel member, jadi saat keyboard
   naik dan area terlihat tinggal ~400px, isinya cuma bidang putih ini -- kotak
   pencarian membernya terdorong ke bawah layar. */
.cart-empty {
  text-align: center;
  color: var(--text-muted);
  margin-top: 40px;
}

.cart-empty i {
  font-size: 2.375rem;
  margin-bottom: 12px;
  opacity: 0.35;
  color: var(--color-primary);
}

.cart-empty-title {
  font-size: 0.8125rem;
  font-weight: 600;
}

.cart-empty-hint {
  font-size: 0.7188rem;
  margin-top: 4px;
}

/* POS Cart Sidebar Panel */
/* The right column is no longer one card: it holds two independent panes, side by
   side, so the member panel can never push the order list out of view. */
.pos-cart-panel {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 14px;
  height: 100%;
  min-height: 0;
  background: none;
  border: none;
  box-shadow: none;
  overflow: visible;
}

.pos-side-card {
  background-color: var(--text-inverse);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.mobile-cart-close {
  display: none;
}

/* Checkout pane: fixed right column holding member, totals and the pay button */
.pos-side-card.checkout-card {
  flex: 0 0 348px;
  min-width: 0;
}

/* Order lines pane: takes the remaining width and scrolls internally.
   min-height/min-width 0 is what actually lets the inner list scroll in a flex child. */
.pos-side-card.cart-card {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
}

.pos-card-head {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background-color: var(--bg-surface);
  cursor: pointer;
  flex-shrink: 0;
  user-select: none;
}

.pos-card-head.static {
  cursor: default;
}

.pos-card-title {
  font-family: var(--font-heading);
  font-size: 0.9375rem;
  font-weight: 800;
  color: var(--color-primary);
  display: flex;
  align-items: center;
  gap: 8px;
}

.pos-card-chevron {
  color: var(--text-muted);
  font-size: 0.75rem;
  transition: transform 0.2s ease;
}

.checkout-card.collapsed .pos-card-chevron {
  transform: rotate(180deg);
}

.checkout-card.collapsed .customer-selector {
  display: none;
}

/* Totals + payment sit at the bottom of the checkout pane and never scroll away */
.checkout-card .cart-summary {
  margin-top: auto;
}

.customer-selector {
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  background-color: var(--bg-surface);
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.member-search-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.18);
  max-height: 220px;
  overflow-y: auto;
  z-index: 100;
  margin-top: 4px;
}

.member-search-item {
  padding: 11px 13px;
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  border-bottom: 1px solid var(--border-light);
  transition: background 0.15s ease;
}

.member-search-item:last-child {
  border-bottom: none;
}

.member-search-item:hover {
  background: var(--color-primary-light);
}

.member-search-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-primary-light);
  color: var(--color-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.8125rem;
  flex-shrink: 0;
}

.member-selected-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  padding: 13px 14px;
}

.member-avatar-mini {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--color-primary);
  color: var(--text-inverse);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.75rem;
  flex-shrink: 0;
}

.cart-items-list {
  flex: 1 1 auto;
  /* Guarantee the order list keeps room even when the member/loyalty boxes above
     are all expanded, instead of being squeezed to a couple of visible rows. */
  min-height: 180px;
  overflow-y: auto;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.cart-item-row {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 14px 14px;
  background-color: var(--bg-surface);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-light);
}

.cart-item-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.cart-item-info h5 {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-main);
  margin-bottom: 2px;
}

.cart-item-info span {
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--color-primary);
}

.cart-item-discount-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--bg-card);
  padding: 4px 8px;
  border-radius: var(--radius-xs);
  border: 1px dashed var(--border-color);
  font-size: 0.6875rem;
}

.cart-item-discount-input {
  width: 90px;
  padding: 2px 6px;
  font-size: 0.7188rem;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-xs);
  text-align: right;
}

.qty-stepper {
  display: flex;
  align-items: center;
  gap: 6px;
  background-color: var(--text-inverse);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-full);
  padding: 2px 6px;
}

.qty-btn {
  background: transparent;
  border: none;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 0.8125rem;
  color: var(--color-primary);
  cursor: pointer;
  border-radius: 50%;
  transition: background 0.15s ease;
}

.qty-btn:hover {
  background-color: var(--color-primary-light);
}

.cart-summary {
  padding: 18px 20px;
  flex-shrink: 0;
  border-top: 1px solid var(--border-color);
  background-color: var(--bg-surface);
  font-size: 0.75rem;
}

.summary-row {
  display: flex;
  justify-content: space-between;
  color: var(--text-secondary);
  margin-bottom: 8px;
}

.summary-row.discount {
  color: var(--color-danger);
  font-weight: 600;
}

/* Tax disclosure sits under the total in a lighter weight: menu prices are
   tax-inclusive, so this is information, not another line on the bill. */
.summary-tax-note {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 6px;
  padding-left: 10px;
  font-size: 0.6875rem;
  color: var(--text-muted);
}

.summary-row.total {
  font-family: var(--font-heading);
  font-size: 1.1875rem;
  font-weight: 800;
  color: var(--color-primary);
  border-top: 1px dashed var(--border-color);
  padding-top: 12px;
  margin-top: 12px;
  margin-bottom: 0;
}

/* Loyalty actions collapsed into one chip row instead of three stacked cards */
.loyalty-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.loyalty-chip-row:empty {
  display: none;
}

.loyalty-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 10px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-color);
  background: var(--bg-card);
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.15s ease;
}

.loyalty-chip:hover {
  border-color: var(--color-secondary);
  background: var(--color-secondary-light);
}

.loyalty-chip.warning {
  border-color: var(--color-warning);
  background: var(--color-warning-bg);
  color: var(--color-warning);
}

.loyalty-chip-value {
  font-weight: 800;
  color: var(--color-primary);
}

.loyalty-reward {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-color);
  background: var(--bg-surface);
  font-size: 0.6875rem;
  min-width: 0;
  flex: 1 1 160px;
}

.loyalty-reward-select {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: transparent;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--color-primary);
  cursor: pointer;
  outline: none;
}

/* Payment modal: figures a cashier can read at arm's length */
.pay-total-box,
.pay-change-box {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border-radius: var(--radius-md);
  margin-bottom: 16px;
}

.pay-total-box {
  background: var(--color-primary-light);
  border: 1px solid var(--border-color);
}

.pay-change-box {
  background: var(--color-success-bg);
  border: 1px solid rgba(46, 125, 50, 0.25);
  margin-top: 12px;
  margin-bottom: 0;
}

.pay-total-label {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-secondary);
}

.pay-total-value,
.pay-change-value {
  font-family: var(--font-heading);
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1.1;
}

.pay-total-value {
  color: var(--color-primary);
}

.pay-change-value {
  color: var(--color-success);
}

.pay-method-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}

.pay-method-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-color);
  background: var(--bg-card);
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.15s ease;
}

.pay-method-btn:hover {
  border-color: var(--color-secondary);
  background: var(--color-secondary-light);
}

.pay-method-btn.active {
  border-color: var(--color-primary);
  background: var(--color-primary);
  color: var(--text-inverse);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}

/* Quick Cash Presets */
.cash-preset-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  margin: 8px 0;
}

.cash-preset-btn {
  padding: 12px 6px;
  background-color: var(--text-inverse);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-xs);
  font-size: 0.7188rem;
  font-weight: 700;
  color: var(--color-primary);
  cursor: pointer;
  text-align: center;
  transition: all 0.15s ease;
}

.cash-preset-btn:hover, .cash-preset-btn.active {
  background-color: var(--color-primary-light);
  border-color: var(--color-primary);
}

/* ==========================================================================
   Thermal Receipt Component & High-Accuracy Printing
   ========================================================================== */

.thermal-receipt-card {
  width: 100%;
  max-width: 330px;
  box-sizing: border-box;
  background-color: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 20px 16px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.4;
  color: #111111;
  margin: 0 auto;
  position: relative;
  word-break: break-word;
}

.receipt-header {
  text-align: center;
  margin-bottom: 12px;
}

.receipt-logo {
  display: none !important;
}

.receipt-store-title {
  font-weight: 800;
  font-size: 15px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

.receipt-meta-info {
  font-size: 10.5px;
  color: #444;
  margin-top: 3px;
  line-height: 1.35;
}

.receipt-divider {
  border-top: 1px dashed #777;
  margin: 10px 0;
}

.receipt-divider.double {
  border-top: 2px solid #333;
  margin: 10px 0;
}

.receipt-row {
  display: flex;
  justify-content: space-between;
  margin-bottom: 3px;
}

.receipt-row.bold {
  font-weight: 800;
  font-size: 13px;
}

.receipt-footer {
  text-align: center;
  font-size: 10.5px;
  color: #444;
  margin-top: 14px;
  line-height: 1.4;
}

/* Mobile Hamburger Toggle Button */
.mobile-menu-toggle {
  display: none !important;
}

.mobile-cart-bar {
  display: none;
}

/* ==========================================================================
   Dropdown yang bisa dicari

   Dipasang otomatis oleh pindaiDropdownCari() di app.js pada tiap <select>
   yang pilihannya sudah banyak. <select> aslinya tetap ada di dalam .combo --
   disembunyikan, tapi tetap yang memegang nilainya.
   ========================================================================== */

.combo {
  position: relative;
  width: 100%;
}

/* Bukan display:none: elemen yang tidak dirender tidak ikut divalidasi browser,
   dan `required` pada dropdown wajib (mis. bahan di Catat Pembelian) akan
   berhenti bekerja tanpa satu pun pesan. */
.combo > select {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: 0;
  opacity: 0;
  pointer-events: none;
  bottom: 0;
  left: 50%;
}

.combo-input {
  cursor: pointer;
  padding-right: 36px;
  text-overflow: ellipsis;
}

.combo.terbuka .combo-input {
  cursor: text;
  border-color: var(--border-focus);
  box-shadow: 0 0 0 1px var(--border-focus);
}

.combo-caret {
  position: absolute;
  right: 13px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.625rem;
  color: var(--text-muted);
  pointer-events: none;
  transition: transform 0.15s ease;
}

.combo.terbuka .combo-caret {
  transform: translateY(-50%) rotate(180deg);
}

.combo-panel {
  display: none;
  position: absolute;
  z-index: 1200;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 260px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  padding: 4px;
}

.combo.terbuka .combo-panel {
  display: block;
}

.combo-option {
  padding: 9px 12px;
  border-radius: var(--radius-xs);
  font-size: 0.8125rem;
  color: var(--text-main);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Satu penanda saja untuk baris yang sedang disorot, dipakai bersama oleh
   tetikus dan panah keyboard -- dua penanda berbeda di daftar yang sama
   membuat mata ragu mana yang akan terpilih saat Enter ditekan. */
.combo-option:hover,
.combo-option.aktif {
  background: var(--bg-hover);
}

.combo-option.dipilih {
  font-weight: 600;
}

.combo-option.dipilih::after {
  content: '\f00c';                 /* fa-check */
  font-family: 'Font Awesome 6 Free';
  font-weight: 900;
  float: right;
  font-size: 0.625rem;
  color: var(--color-primary);
  margin-left: 10px;
}

.combo-kosong {
  padding: 14px 12px;
  font-size: 0.75rem;
  color: var(--text-muted);
  text-align: center;
}

/* Di layar sentuh daftarnya melabuh ke bawah layar, bukan mengambang di bawah
   kotaknya. Sebagai panel mengambang di dalam bottom sheet POS atau modal
   pendek, daftarnya terpotong tepi induknya dan pilihan terbawah tidak pernah
   bisa diketuk -- persis masalah yang dulu sudah muncul pada hasil cari member. */
@media (max-width: 768px) {
  .combo-panel {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    max-height: 50vh;
    max-height: 50dvh;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    border-bottom: 0;
    padding: 8px;
    z-index: 3000;
  }

  .combo-option {
    padding: 13px 14px;
    font-size: 0.875rem;
  }
}

/* ==========================================================================
   Comprehensive Mobile & Tablet Responsive Layout
   ========================================================================== */

/* Three columns need roughly 1400px of viewport: 728px for the two right panes, plus
   a catalog wide enough to still show two menu columns. Below that they stack again
   rather than all three going too narrow to use. Move this breakpoint if your terminal
   screen is wider or narrower than that. */
@media (max-width: 1400px) {
  .pos-layout {
    grid-template-columns: 1fr 452px;
  }

  .pos-cart-panel {
    flex-direction: column;
  }

  /* Stacked: the order lines keep the flexible height, checkout sizes to content */
  .pos-side-card.cart-card {
    flex: 1 1 auto;
  }

  .pos-side-card.checkout-card {
    flex: 0 0 auto;
  }

  .customer-selector {
    flex: 0 1 auto;
    max-height: 32vh;
  }
}

@media (max-width: 1024px) {
  .profile-layout {
    grid-template-columns: 1fr;
  }

  /* Tablets are usually the touch terminal — give the whole UI another notch */
  html {
    font-size: 19px;
  }

  .app-container {
    overflow-x: hidden;
  }

  .sidebar {
    position: fixed;
    left: -290px;
    top: 0;
    bottom: 0;
    width: 280px;
    z-index: 1050;
    transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: var(--shadow-lg);
  }

  .sidebar.open {
    left: 0;
  }

  .sidebar-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(2px);
    z-index: 1040;
    display: none;
    opacity: 0;
    transition: opacity 0.3s ease;
  }

  .sidebar-backdrop.show {
    display: block;
    opacity: 1;
  }

  .mobile-menu-toggle {
    display: inline-flex !important;
  }

  .main-wrapper {
    width: 100vw !important;
    max-width: 100vw !important;
  }

  .topbar {
    padding: 12px 16px;
  }

  .content-area {
    padding: 16px;
  }
}

@media (max-width: 768px) {
  .page-title h2 {
    font-size: 1.0625rem;
  }

  .topbar-actions .shift-text {
    display: none;
  }

  .topbar-actions .btn-text {
    display: none;
  }

  .stats-grid {
    grid-template-columns: 1fr 1fr;
    gap: 12px;
  }

  .stat-card {
    padding: 14px;
  }

  .stat-value {
    font-size: 1rem;
  }

  .modal-backdrop {
    padding: 8px !important;
  }

  .modal-dialog {
    width: 100% !important;
    max-width: 100% !important;
    margin: auto !important;
    max-height: 94vh !important;
    border-radius: var(--radius-lg) !important;
  }

  .modal-header {
    padding: 14px 18px !important;
  }

  .modal-footer {
    padding: 12px 18px !important;
  }

  .modal-body {
    padding: 12px !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch !important;
  }

  .thermal-receipt-card {
    width: 100% !important;
    max-width: 100% !important;
    padding: 14px 10px !important;
    font-size: 11px !important;
  }

  /* Grid collapsing on mobile for all multi-column pages */
  [style*="grid-template-columns: 1fr 1.35fr"],
  [style*="grid-template-columns: 1fr 1fr"],
  [style*="grid-template-columns: 2fr 1fr"],
  [style*="grid-template-columns: 1fr 2fr"],
  [style*="grid-template-columns: repeat(4, 1fr)"] {
    grid-template-columns: 1fr !important;
    gap: 16px !important;
  }

  .stats-grid {
    grid-template-columns: 1fr 1fr !important;
    gap: 10px !important;
  }

  .menu-grid {
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 10px;
  }

  /* Table horizontal touch scroll */
  .table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    width: 100%;
    margin-bottom: 10px;
  }
}

@media (max-width: 480px) {
  /* Turun dari skala tablet: di HP, teks 19.5px membuat judul menu pecah dua baris
     dan hanya menyisakan satu setengah baris kartu yang terlihat. */
  html {
    font-size: 18px;
  }

  .stats-grid {
    grid-template-columns: 1fr !important;
  }
  .topbar {
    padding: 10px 12px;
  }
  .content-area {
    padding: 12px 10px;
  }
  .card-header {
    flex-direction: column;
    align-items: flex-start !important;
    gap: 10px;
  }
  .card-header > button, .card-header .btn {
    width: 100%;
  }
  .menu-grid {
    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    gap: 8px;
  }
}

/* ==========================================================================
   Touch & Small-Screen Refinements

   Dipisah dari blok @media di atas supaya jelas mana aturan layout dan mana
   aturan kenyamanan sentuh. Urutannya sengaja setelah semua breakpoint agar
   menang tanpa perlu !important.
   ========================================================================== */

/* Scroll dikunci saat sidebar / bottom sheet terbuka: tanpa ini halaman di
   belakang ikut ter-scroll dan posisi baca pengguna hilang. */
body.scroll-locked {
  overflow: hidden;
}

/* Target sentuh. Jari butuh sekitar 44px; btn-sm sebelumnya hanya ~33px,
   dan itu tombol yang paling sering dipakai (filter, aksi di tabel). */
@media (pointer: coarse) {
  .btn {
    min-height: 44px;
  }

  .btn-sm {
    min-height: 38px;
    padding: 8px 14px;
  }

  .btn-icon {
    width: 42px;
    height: 42px;
  }

  .form-control,
  select.form-control {
    min-height: 44px;
  }

  .modal-close {
    width: 42px;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .nav-item {
    min-height: 46px;
  }

  /* Baris tabel lebih longgar supaya tidak salah tekan */
  .custom-table td {
    padding-top: 16px;
    padding-bottom: 16px;
  }
}

@media (max-width: 1024px) {
  /* Tinggi viewport dinamis: bar alamat browser mobile yang muncul-hilang
     membuat 100vh memotong isi modal dan bottom sheet. */
  .modal-dialog {
    /* vh dulu, dvh menimpanya. Mesin yang belum mengenal dvh (Chromium di
       bawah 108 -- dan paket WebView di HP diperbarui terpisah dari Chrome,
       jadi bisa jauh tertinggal) MEMBUANG deklarasi dvh diam-diam. Tanpa
       cadangan ini panel kehilangan batas tingginya sama sekali, memanjang
       melampaui layar, dan yang terlihat tinggal bidang kosong. */
    max-height: 90vh;
    max-height: 90dvh;
  }

  /* Tabel lebar (laporan sesi kas punya 11 kolom) tidak boleh digencet sampai
     tiap kata pecah per huruf. Biarkan menggeser horizontal dengan header lengket. */
  .table-responsive {
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }

  .table-responsive .custom-table {
    min-width: 680px;
  }

  /* Wajib bersamaan dengan min-width tabel di atas. Item grid/flex punya
     min-width:auto, jadi tabel selebar 680px membuat kartu induknya menolak
     menyusut dan seluruh halaman meluber keluar layar (halaman Pengaturan
     paling terasa). min-width:0 mengembalikan hak menyusut, dan tabelnya
     tetap bisa digeser di dalam .table-responsive. */
  .content-area .card,
  .content-area .table-responsive,
  .content-area [style*="display: grid"] > * {
    min-width: 0;
  }

  .custom-table th,
  .custom-table td {
    white-space: nowrap;
  }

  /* Kolom keterangan tetap boleh membungkus, kalau tidak barisnya jadi sangat panjang */
  .custom-table td.wrap,
  .custom-table th.wrap {
    white-space: normal;
    min-width: 180px;
  }

  /* Deretan tab laporan (8 tab) jadi strip geser, bukan tembok tombol setinggi layar.
     Kepala dokumen laporan ikut menumpuk supaya alamat toko tidak jadi kolom sempit. */
  .report-doc-header {
    flex-direction: column;
    gap: 12px;
  }

  .report-doc-header > div:last-child {
    text-align: left !important;
  }

  #report-tabs-nav {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 4px;
  }

  #report-tabs-nav::-webkit-scrollbar {
    display: none;
  }

  #report-tabs-nav .rep-tab-btn {
    flex: 0 0 auto;
  }
}

/* Tablet lanskap: POS tetap dua kolom, panel keranjang dirampingkan supaya katalog
   menu tidak tersisa sesempit satu setengah kartu. */
@media (min-width: 769px) and (max-width: 1024px) and (orientation: landscape) {
  .pos-layout {
    grid-template-columns: 1fr 380px;
  }

  .content-area {
    padding: 18px 20px;
  }
}

/* ==========================================================================
   POS sebagai bottom sheet.

   Berlaku di HP dan juga di tablet PORTRAIT: pada iPad tegak, panel keranjang
   yang menetap hanya menyisakan dua kolom menu: katalog jadi sempit padahal
   layarnya lebar. Sebagai bottom sheet, katalog dapat lebar penuh.
   ========================================================================== */
@media (max-width: 768px),
       (max-width: 1024px) and (orientation: portrait) {
  .pos-layout {
    grid-template-columns: 1fr;
    height: auto;
    min-height: calc(100vh - 65px);
    padding-bottom: calc(75px + env(safe-area-inset-bottom));
  }

  .pos-cart-panel {
    position: fixed;
    bottom: 0;
    /* Digeser dengan transform, bukan `bottom`. Menganimasikan `bottom` memaksa
       tata letak dihitung ulang tiap frame untuk seluruh isi panel; transform
       hanya menggeser lapisan yang sudah jadi. */
    transform: translateY(100%);
    left: 0;
    right: 0;
    /* Setinggi isinya, bukan selalu 85% layar: pesanan satu item tidak lagi
       menyisakan area kosong besar di bawah tombol Bayar. */
    height: auto;
    max-height: 88vh;
    max-height: 88dvh;
    z-index: 1000;
    background: var(--bg-card);
    border-top-left-radius: var(--radius-lg);
    border-top-right-radius: var(--radius-lg);
    box-shadow: 0 -6px 16px rgba(0, 0, 0, 0.22);
    transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
    /* TANPA will-change di sini, dan itu disengaja. will-change: transform
       memaksa panel ini jadi satu lapisan komposit PERMANEN. Panelnya tinggi,
       bisa digulung, dan berbayang besar -- tiap kali isinya berubah (satu
       huruf diketik) atau jendela diubah ukurannya oleh keyboard, seluruh
       lapisan harus diraster ulang. Petak yang belum selesai diraster
       digambar putih, dan itulah kedipan putih yang menghalangi layar saat
       kasir mengetik nama member. Animasinya sendiri tetap mulus: Chromium
       otomatis mempromosikan lapisan selama transisi transform berjalan. */
    display: flex;
    flex-direction: column;
    border-left: none;
    border-top: 3px solid var(--color-primary);
    /* As a bottom sheet the two panes read as one continuous surface: drop their
       individual card chrome and let the sheet own the scrolling. */
    gap: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: env(safe-area-inset-bottom);
  }

  .pos-cart-panel.open {
    transform: translateY(0);
  }

  /* Pegangan visual bahwa panel ini bisa ditutup */
  .pos-cart-panel::before {
    content: '';
    position: sticky;
    top: 0;
    z-index: 3;
    display: block;
    width: 44px;
    height: 4px;
    margin: 8px auto 4px;
    border-radius: var(--radius-full);
    background: var(--border-color);
    flex: 0 0 auto;
  }

  .pos-sheet-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 999;
    display: none;
  }

  .pos-sheet-backdrop.show {
    display: block;
  }

  .pos-side-card {
    border: none;
    border-radius: 0;
    box-shadow: none;
  }

  .pos-side-card + .pos-side-card {
    border-top: 1px solid var(--border-color);
  }

  .pos-side-card.checkout-card,
  .pos-side-card.cart-card {
    flex: 0 0 auto;
  }

  .customer-selector {
    max-height: none;
    overflow: visible;
  }

  .cart-items-list {
    overflow: visible;
    min-height: 0;
  }

  /* ~180px -> ~60px: satu baris ikon + judul, petunjuknya sudah jelas dari
     katalog yang terlihat di belakang sheet. */
  .cart-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    margin: 0;
    padding: 14px 0;
  }

  .cart-empty i {
    font-size: 1.25rem;
    margin-bottom: 0;
  }

  .cart-empty-hint {
    display: none;
  }

  .mobile-cart-close {
    display: inline-flex;
  }

  .mobile-cart-bar {
    display: flex !important;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    height: calc(60px + env(safe-area-inset-bottom));
    background: var(--color-primary);
    color: var(--text-inverse);
    padding: 0 18px env(safe-area-inset-bottom);
    align-items: center;
    justify-content: space-between;
    z-index: 990;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.35);
    cursor: pointer;
  }

  .mobile-cart-bar .cart-badge {
    background: var(--color-secondary);
    color: var(--text-inverse);
    padding: 3px 8px;
    border-radius: var(--radius-full);
    font-size: 0.7188rem;
    font-weight: 700;
  }
}

@media (max-width: 768px) {
  /* Topbar ringkas: judul satu baris, status sesi tidak melipat jadi dua baris.
     Sebelumnya "Terminal Kasir (POS)" + pill sesi memakan hampir seperlima layar. */
  .topbar {
    gap: 10px;
  }

  .page-title h2 {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 42vw;
  }

  .shift-status-pill {
    white-space: nowrap;
    max-width: 45vw;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 0.6875rem;
    padding: 6px 10px;
  }

  /* Grup judul yang boleh menyusut, grup status yang dipertahankan: tanpa ini
     pill sesi terdorong keluar layar dan terpotong di tepi kanan. */
  .topbar > div:first-child {
    flex: 0 1 auto;
    min-width: 0;
  }

  .topbar-actions {
    gap: 8px;
    flex: 0 0 auto;
    min-width: 0;
  }

  /* Hasil pencarian member: mengalir di dalam panel, bukan mengambang.
     Sebagai dropdown mengambang di bottom sheet, daftarnya menutupi tombol Bayar
     dan terpotong tepi panel sehingga hasil terakhir tidak bisa diketuk. */
  .member-search-dropdown {
    position: static;
    /* Relatif tinggi layar yang tersisa: dengan keyboard terbuka, 260px tetap
       mendorong tombol Bayar keluar dari area yang terlihat. */
    max-height: 38vh;
    max-height: 38dvh;
    box-shadow: none;
    margin-top: 6px;
    -webkit-overflow-scrolling: touch;
  }

  .menu-card-footer {
    flex-wrap: wrap;
    row-gap: 4px;
  }

  /* Tombol di header kartu memenuhi lebar: lebih mudah ditekan, lebih rapi */
  .card-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  /* Judul dan grup tombol masing-masing satu baris penuh, tapi tombolnya
     dibiarkan membungkus dan berbagi lebar. Memaksa tiap tombol 100% membuat
     baris filter (Pengeluaran, Monitoring) meluber keluar layar. */
  .card-header > * {
    width: 100%;
  }

  .card-header > div {
    flex-wrap: wrap;
  }

  .card-header .btn {
    flex: 1 1 auto;
  }

  /* Baris flex horizontal yang sengaja ditumpuk di layar sempit */
  .stack-on-mobile {
    flex-direction: column;
    align-items: flex-start !important;
    gap: 10px !important;
  }

  .stack-on-mobile > * {
    margin-left: 0 !important;
    width: 100%;
  }

  /* Grup tombol di dalamnya membungkus, tidak memanjang keluar layar */
  .stack-on-mobile > div {
    flex-wrap: wrap;
  }

  .stack-on-mobile .btn {
    flex: 1 1 auto;
  }

  /* Baris filter periode / aksi laporan: geser horizontal daripada menumpuk */
  .filter-period-btn,
  .filter-exp-btn {
    flex: 1 1 auto;
  }

  /* Toolbar aksi laporan (Excel/PDF) menumpuk penuh supaya tidak terpotong */
  .report-actions {
    width: 100%;
  }

  .report-actions .btn {
    flex: 1 1 auto;
  }
}

@media (max-width: 480px) {
  /* Ukuran kertas struk tidak dipakai saat kerja di HP; ruang topbar lebih berharga */
  #topbar-paper-size {
    display: none;
  }

  .page-title h2 {
    max-width: 50vw;
  }

  /* "Member & Pembayaran" jangan sampai melipat dua baris dan memakan tinggi
     panel yang justru dibutuhkan untuk daftar pesanan. */
  .pos-card-title {
    font-size: 0.8125rem;
  }

  /* Gambar kartu menu dipendekkan: di layar 360px tingginya membuat hanya dua
     baris menu yang terlihat, padahal kasir butuh cepat memindai katalog. */
  .menu-card-img {
    height: 62px;
    font-size: 1.5rem;
  }

  .badge-chip {
    font-size: 0.625rem;
  }

  .modal-footer {
    flex-direction: column-reverse;
    gap: 8px;
  }

  .modal-footer .btn {
    width: 100%;
  }
}

/* ==========================================================================
   Pratinjau struk di layar

   Isi & tata letaknya sama persis dengan yang keluar di kertas (dibangun dari
   model baris yang sama di escpos.js), tapi tampilannya dibuat menyerupai
   gulungan kertas thermal: tepi sobek, garis pemisah sungguhan, spasi lega.
   ========================================================================== */

/* Latar redup di belakang kertas, kalau tidak tepi sobeknya putih di atas putih
   dan struk tidak terbaca sebagai lembaran. */
#receipt-modal-body,
#report-receipt-body {
  background: var(--border-color);
  padding: 18px 12px !important;
}

/* Lebar isi dikunci tepat selebar kolom printer (32 untuk 58mm, 48 untuk 80mm).
   Tanpa ini kartu lebih lebar daripada barisnya: nilai yang rata kanan berhenti
   jauh sebelum tepi sementara garis pemisah memanjang penuh, dan struk terlihat
   miring. --receipt-cols diisi oleh escpos.js sesuai ukuran kertas. */
.receipt-paper {
  position: relative;
  box-sizing: content-box !important;
  width: calc(var(--receipt-cols, 32) * 1ch) !important;
  max-width: none !important;
  background: #FFFFFF;
  border: none;
  border-radius: 0;
  padding: 22px 18px 26px !important;
  margin: 6px auto;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.16);
  color: #111111;
  font-size: 13px;
  letter-spacing: 0;
}

@media (max-width: 480px) {
  .receipt-paper {
    font-size: 12px;
    padding: 18px 14px 22px !important;
  }
}

/* Tanpa hiasan tepi sobek: zigzag CSS-nya terbaca sebagai coretan miring, bukan
   kertas robek. Lembaran polos dengan bayangan lembut sudah cukup jelas. */

.receipt-line {
  white-space: pre;
  font-family: inherit;
  line-height: 1.5;
}

.receipt-line.center {
  text-align: center;
}

.receipt-line.bold {
  font-weight: 700;
}

/* Nama toko */
.receipt-line.title {
  font-size: 1.5em;
  font-weight: 800;
  letter-spacing: 0.04em;
  line-height: 1.4;
  margin-bottom: 2px;
}

/* Baris TOTAL: satu-satunya angka yang harus langsung ketemu mata.
   Lebar huruf sengaja tidak diperbesar -- printer pun hanya menggandakan tinggi,
   dan font yang lebih lebar membuat 32 karakternya menjulur keluar dari kertas. */
.receipt-line.total {
  font-weight: 800;
  padding: 4px 0;
  line-height: 1.7;
}

.receipt-rule {
  border-top: 1px dashed #BBBBBB;
  margin: 7px 0;
}

.receipt-rule.strong {
  border-top: 1px solid #888888;
}

/* ==========================================================================
   Print CSS Engine: Thermal Receipt & Reports
   ========================================================================== */

/* ==========================================================================
   Penyempurnaan Responsif

   Ditaruh setelah seluruh breakpoint di atas, mengikuti kebiasaan berkas ini:
   yang terakhir menang tanpa perlu !important.
   ========================================================================== */

/* Lebar baca. Di monitor lebar, formulir satu kolom yang dibiarkan melar
   membuat label dan isiannya berjauhan sampai mata harus melompat. */
.page-narrow {
  max-width: 900px;
}

/* --------------------------------------------------------------------------
   Tabel di layar sempit: tiap baris jadi satu kartu

   Sebelumnya tabel hanya bisa digeser ke samping. Di layar 360px itu berarti
   kolom paling kanan -- yang justru berisi tombol aksi -- praktis tidak pernah
   terlihat kecuali pengguna tahu harus menggeser. Label tiap sel disalin dari
   <thead> oleh pasangLabelKolom() di app.js, jadi tidak ada satu templat pun
   yang perlu disunting.
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .table-responsive {
    overflow-x: visible;
  }

  /* Blok geser-horizontal untuk tabel lebar (lihat min-width: 680px di
     breakpoint 768px) tidak berlaku lagi di sini: barisnya sudah jadi kartu,
     jadi tidak ada yang perlu digeser. Selektornya harus sama spesifiknya
     dengan yang ditimpa, kalau tidak tabelnya tetap 680px dan seluruh halaman
     ikut meluber. */
  .table-responsive .custom-table {
    min-width: 0;
  }

  .custom-table,
  .custom-table tbody,
  .custom-table tr,
  .custom-table td {
    display: block;
    width: 100%;
  }

  /* nowrap juga datang dari blok geser itu. Dalam bentuk kartu, nilai panjang
     harus boleh membungkus -- kalau tidak, nama bahan yang panjang mendorong
     kartunya melebihi lebar layar. */
  .custom-table th,
  .custom-table td {
    white-space: normal;
  }

  /* Judul kolom sudah pindah ke tiap sel; barisnya sendiri tidak perlu lagi. */
  .custom-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .custom-table tr {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    padding: 4px 0;
    margin-bottom: 10px;
  }

  .custom-table tr:hover td {
    background: transparent;
  }

  /* Label DI ATAS nilai, keduanya rata kiri dan berlebar penuh.
     Tiga susunan lain sudah dicoba dan semuanya kandas pada hal yang sama --
     judul kolom di sini ditulis untuk header tabel lebar ("Nama Bahan Baku /
     Kemasan", "Kontak (Telp / Email)", "Harga Pokok / Satuan") dan tidak muat
     di kolom sempit mana pun:

     - flex: tiap anak sel jadi item terpisah yang berjejer ke samping, jadi sel
       bertumpuk di halaman HPP terjepit lalu tumpah keluar kartu.
     - label absolut di kiri: keluar dari alur, tidak menambah tinggi sel, jadi
       label dua baris menabrak label baris berikutnya.
     - label mengapung di kiri: tumpang tindihnya hilang, tapi label memakan tiga
       baris untuk nilai sependek "Kopi", dan deretan tombol Aksi yang tinggal
       60% lebar pecah jadi dua baris tak rata.

     Ditumpuk, tidak ada lagi tawar-menawar lebar: label selalu satu baris, nilai
     dan tombol dapat lebar penuh, dan tinggi tiap baris jadi seragam. */
  .custom-table td {
    display: block;
    text-align: left;
    padding: 9px 14px;
    border-bottom: 1px solid var(--border-light);
  }

  .custom-table tr td:last-child {
    border-bottom: 0;
  }

  .custom-table td[data-label]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 0.625rem;
    line-height: 1.4;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    font-weight: 600;
  }

  /* Deretan tombol aksi: satu baris rata kiri dengan jarak tetap. Tanpa margin
     eksplisit jaraknya bergantung pada spasi di markup dan jadi tidak rata
     begitu barisnya membungkus. */
  .custom-table td .btn {
    margin: 2px 6px 2px 0;
  }

  /* Sebagian sel menulis white-space: nowrap di atribut style-nya sendiri
     (mis. kolom Aksi di halaman HPP). Dalam bentuk kartu itu menahan tombolnya
     tetap satu baris sampai keluar kartu, jadi harus ditimpa -- dan atribut
     style hanya bisa dikalahkan dengan !important. */
  .custom-table td[style] {
    white-space: normal !important;
  }

  /* Sel tanpa label (baris "Memuat...", "Belum ada data") tetap satu blok penuh
     dan rata tengah seperti aslinya. */
  .custom-table td:not([data-label]) {
    display: block;
    text-align: center;
  }

  /* Tombol aksi mendapat barisnya sendiri, tidak lagi terjepit di tepi kanan. */
  .custom-table td .btn {
    flex: 0 0 auto;
  }
}

/* --------------------------------------------------------------------------
   Tablet potret (768-1024px)

   Sebelumnya lompat langsung dari tata letak lebar ke satu kolom. Di iPad atau
   tab Samsung yang dipakai berdiri, itu menyisakan setengah layar kosong pada
   halaman yang isinya justru banyak.
   -------------------------------------------------------------------------- */
@media (min-width: 768px) and (max-width: 1024px) and (orientation: portrait) {
  .content-area {
    padding: 20px 22px;
  }

  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 14px;
  }

  .card {
    padding: 22px;
  }
}

/* --------------------------------------------------------------------------
   Kisi berkolom tetap yang ditulis di atribut style

   Ada 32 tempat di sembilan templat yang menulis
   `style="display: grid; grid-template-columns: 1fr 1fr"` langsung di markup --
   formulir Catat Pembelian, Tambah Menu, Member, dan seterusnya. Di layar 360px
   dua kolom itu tidak muat, dan karena kisi tidak menyusut di bawah lebar
   isinya, SELURUH halaman jadi bisa digeser ke samping: kartu terpotong dan
   tombol simpan berada di luar layar.

   Yang dipakai di sini BUKAN menumpuk semuanya jadi satu kolom. Percobaan
   pertama begitu, dan hasilnya salah dengan cara yang lain: tombol hapus di
   baris racikan resep memenuhi lebar layar jadi bilah kosong berikon kecil,
   dan nilai rupiah yang rata kanan menggantung sendirian satu baris penuh --
   terbaca seperti tata letak yang meleset, bukan tata letak untuk layar sempit.

   Jadi kisinya diubah jadi baris yang MEMBUNGKUS. Isian tumbuh dan turun baris
   saat tidak muat, sementara tombol tetap seukuran isinya. Aturannya satu dan
   ikut menangani kisi sebaris yang ditambahkan nanti. !important memang
   diperlukan: yang ditimpa adalah atribut style. Pakai kelas .keep-grid pada
   elemen yang memang harus tetap berkolom di layar sempit.
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
  [style*="grid-template-columns"]:not(.keep-grid) {
    display: flex !important;
    flex-wrap: wrap;
    align-items: center;
  }

  /* 160px: dua isian pendek boleh berbagi satu baris di tablet kecil, tapi di
     layar 360px keduanya turun sendiri tanpa perlu breakpoint tambahan. */
  [style*="grid-template-columns"]:not(.keep-grid) > * {
    flex: 1 1 160px;
    min-width: 0;
  }

  /* Tombol seukuran isinya. Inilah bedanya dengan menumpuk: tombol hapus tetap
     sebesar tombol hapus, bukan selebar layar. */
  [style*="grid-template-columns"]:not(.keep-grid) > .btn,
  [style*="grid-template-columns"]:not(.keep-grid) > button {
    flex: 0 0 auto;
  }
}

/* Ruang tepi lebih hemat di HP: 28px kiri-kanan pada layar 360px berarti
   sepertujuh lebarnya habis sebelum isinya mulai. */
@media (max-width: 640px) {
  .content-area {
    padding: 16px 14px;
  }

  .card {
    padding: 18px 16px;
    border-radius: var(--radius-sm);
  }

  .stats-grid {
    gap: 10px;
  }

  .stat-card {
    padding: 16px;
  }
}

/* ==========================================================================
   Pintasan papan ketik
   ========================================================================== */

.baris-pintasan {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border-light);
  font-size: 0.8125rem;
  color: var(--text-secondary);
}

.baris-pintasan:last-child {
  border-bottom: 0;
}

.baris-pintasan kbd {
  flex: 0 0 148px;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-main);
  background: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-xs);
  padding: 3px 8px;
  text-align: center;
  white-space: nowrap;
}

/* Tombol pintasan tidak ada gunanya di layar sentuh: pintasannya sendiri
   memang dimatikan di sana (lihat pakaiPintasan() di app.js). */
@media (max-width: 1024px), (pointer: coarse) {
  .hanya-desktop {
    display: none !important;
  }
}

@media print {
  /* Hiasan pratinjau tidak boleh ikut tercetak */
  .receipt-paper {
    box-shadow: none !important;
    background: #FFF !important;
    margin: 0 !important;
    max-width: 100% !important;
  }

  #receipt-modal-body,
  #report-receipt-body {
    background: #FFF !important;
    padding: 0 !important;
  }

  /* Global Hide for Non-Print elements */
  .no-print, .sidebar, .topbar, .modal-header, .modal-footer, button, #toast-container, .mobile-cart-bar {
    display: none !important;
  }

  body {
    background: #FFF !important;
    color: #000 !important;
    margin: 0 !important;
    padding: 0 !important;
  }

  /* Receipt slips print through their own iframe (see printReceipt in app.js), so the
     old body.printing-receipt isolation rules are gone. What is left below only covers
     printing a modal that happens to be open. */
  .modal-backdrop.show {
    position: static !important;
    display: block !important;
    background: transparent !important;
    padding: 0 !important;
    margin: 0 auto !important;
    width: 100% !important;
    height: auto !important;
    opacity: 1 !important;
  }

  .modal-backdrop.show .modal-dialog {
    box-shadow: none !important;
    border: none !important;
    max-width: 100% !important;
    width: 100% !important;
    padding: 0 !important;
    margin: 0 auto !important;
    max-height: none !important;
    overflow: visible !important;
    transform: none !important;
  }

  .modal-backdrop.show .modal-body {
    padding: 0 !important;
    max-height: none !important;
    overflow: visible !important;
  }

  /* Thermal Receipt Card Print Styling (72mm standard thermal POS width) */
  .thermal-receipt-card, #printable-receipt {
    display: block !important;
    visibility: visible !important;
    width: 72mm !important;
    max-width: 100% !important;
    border: none !important;
    box-shadow: none !important;
    padding: 2mm 3mm !important;
    margin: 0 auto !important;
    font-family: 'Space Mono', 'Courier New', monospace !important;
    font-size: 11px !important;
    line-height: 1.35 !important;
    color: #000 !important;
    background: #FFF !important;
  }

  .thermal-receipt-card * {
    visibility: visible !important;
    color: #000 !important;
  }

  .receipt-divider {
    border-top: 1px dashed #000 !important;
    margin: 6px 0 !important;
  }

  .receipt-divider.double {
    border-top: 2px solid #000 !important;
    margin: 8px 0 !important;
  }

  /* Report Print Mode - isolates full multi-page printable report */
  body.printing-report .report-printable-area {
    display: block !important;
    visibility: visible !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 20px !important;
  }

  body.printing-report .report-printable-area * {
    visibility: visible !important;
    color: #000 !important;
  }

  body.printing-report .custom-table {
    border: 1px solid #000 !important;
  }

  body.printing-report .custom-table th, 
  body.printing-report .custom-table td {
    border: 1px solid #ccc !important;
    color: #000 !important;
  }
}

/* ==========================================================================
   Gamification Visuals: Lucky Spin & Mystery Box
   ========================================================================== */

.lucky-wheel-container {
  position: relative;
  width: 260px;
  height: 260px;
  margin: 0 auto 20px;
}

.lucky-wheel {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 8px solid #C89058;
  background: conic-gradient(
    #6F4E37 0deg 60deg,
    #E6B98D 60deg 120deg,
    #8F6647 120deg 180deg,
    #D4A373 180deg 240deg,
    #5A3D2A 240deg 300deg,
    #F4EAE1 300deg 360deg
  );
  box-shadow: 0 0 24px rgba(200, 144, 88, 0.4);
  transition: transform 3.5s cubic-bezier(0.15, 0.9, 0.2, 1);
}

.lucky-wheel-pointer {
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 14px solid transparent;
  border-right: 14px solid transparent;
  border-top: 24px solid #D32F2F;
  z-index: 10;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
}

.mystery-box-anim {
  width: 140px;
  height: 140px;
  margin: 20px auto;
  background: #FFF;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 4.5rem;
  box-shadow: 0 10px 30px rgba(111, 78, 55, 0.2);
  cursor: pointer;
  animation: boxWiggle 1.4s infinite;
}

@keyframes boxWiggle {
  0%, 100% { transform: rotate(0deg) scale(1); }
  25% { transform: rotate(-8deg) scale(1.04); }
  75% { transform: rotate(8deg) scale(1.04); }
}

/* ==========================================================================
   Pemberitahuan & kehalusan antarmuka
   ========================================================================== */

.notif-bell {
  position: relative;
  background: transparent;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  width: 34px; height: 34px;
  cursor: pointer;
  color: var(--text-secondary);
  /* Transisi hanya pada properti yang tidak memicu layout ulang. */
  transition: background-color 0.15s ease, color 0.15s ease;
}
.notif-bell:hover { background: var(--color-primary-bg, #F4EAE1); color: var(--color-primary); }

.notif-dot {
  position: absolute; top: 5px; right: 6px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--color-danger);
  box-shadow: 0 0 0 2px #FFF;
}

.notif-panel {
  position: absolute; right: 0; top: calc(100% + 8px);
  width: min(360px, calc(100vw - 32px));
  max-height: 70vh; overflow-y: auto;
  background: #FFF;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: 0 10px 30px rgba(70, 45, 30, 0.18);
  z-index: 1200;
}
.notif-head {
  font-size: 0.75rem; font-weight: 800; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-muted);
  padding: 12px 14px 8px;
}
.notif-item {
  display: flex; gap: 10px;
  padding: 10px 14px;
  border-top: 1px solid var(--border-light);
  font-size: 0.8125rem;
}
.notif-item .notif-ico { flex: 0 0 18px; margin-top: 2px; }
.notif-item.danger  .notif-ico { color: var(--color-danger); }
.notif-item.warning .notif-ico { color: var(--color-warning); }
.notif-item.info    .notif-ico { color: var(--color-primary); }
.notif-item strong { display: block; margin-bottom: 2px; }
.notif-item span   { color: var(--text-muted); font-size: 0.75rem; line-height: 1.45; }
.notif-item a { color: var(--color-primary); font-size: 0.75rem; font-weight: 700; }
.notif-empty { padding: 14px; color: var(--text-muted); font-size: 0.8125rem; text-align: center; }

/* Rangka pemuatan. Menahan tinggi baris supaya isi tidak melompat saat data
   datang -- lompatan itu yang paling terasa sebagai "tidak halus", jauh lebih
   daripada lamanya menunggu. */
.skeleton {
  background: linear-gradient(90deg, #EFE9E3 25%, #F7F3EF 37%, #EFE9E3 63%);
  background-size: 400% 100%;
  animation: skeleton-sweep 1.2s ease-in-out infinite;
  border-radius: 4px;
  color: transparent !important;
}
@keyframes skeleton-sweep {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
.skeleton-row { height: 14px; margin: 9px 0; }

/* Hormati pengguna yang mematikan animasi di sistemnya. */
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
  * { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* Bilah progres navigasi. Reload halaman penuh tetap terjadi, tapi jeda yang
   disertai indikator terasa jauh lebih pendek daripada jeda hening. */
.nav-progress {
  position: fixed; top: 0; left: 0;
  height: 2px; width: 0;
  background: var(--color-primary);
  z-index: 3000;
  opacity: 0;
  pointer-events: none;
}
.nav-progress.running {
  opacity: 1;
  /* Berhenti di 90%: sisanya tidak bisa diketahui, dan bilah yang penuh lalu
     menggantung terasa seperti macet. */
  animation: nav-progress-run 2.4s ease-out forwards;
}
@keyframes nav-progress-run {
  0%   { width: 0; }
  40%  { width: 55%; }
  100% { width: 90%; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-progress.running { animation: none; width: 90%; }
}

/* ==========================================================================
   Layar cari member (HP)

   Ditulis dengan properti paling jinak yang ada. Tidak ada transform,
   transisi, bayangan, backdrop-filter, maupun sudut membulat pada elemen
   sebesar layar -- semuanya pemicu lapisan komposit, dan lapisan komposit
   besar yang diraster ulang tiap keyboard bergerak persis yang melahirkan
   kedipan putih di cangkang WebView.
   ========================================================================== */
.member-screen {
  display: none;
}

@media (max-width: 768px),
       (max-width: 1024px) and (orientation: portrait) {
  .member-screen.open {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1100;
    background: #FFF;
  }

  /* Sheet keranjang dikeluarkan dari pohon render selama layar ini terbuka.
     display:none, bukan sekadar digeser: yang tidak dirender tidak bisa
     meninggalkan petak putih. */
  body.member-screen-open .pos-cart-panel,
  body.member-screen-open .pos-sheet-backdrop,
  body.member-screen-open .mobile-cart-bar {
    display: none !important;
  }

  .member-screen-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-color);
    background: #FAF8F5;
  }

  .member-screen-head .form-control {
    flex: 1;
    min-width: 0;
    height: 40px;
    font-size: 0.8125rem;
  }

  .member-screen-back {
    background: transparent;
    border: none;
    font-size: 1rem;
    color: var(--color-primary);
    padding: 8px 10px;
    cursor: pointer;
  }

  .member-screen-body {
    position: absolute;
    top: 61px;
    left: 0;
    right: 0;
    bottom: 0;
    overflow-y: auto;
    padding: 4px 0 24px;
  }

  /* Baris hasil memakai gaya dropdown yang sudah ada, tanpa kotak mengambang. */
  .member-screen-body .member-search-item {
    border-bottom: 1px solid var(--border-light);
  }
}
