@import url('https://fonts.googleapis.com/css2?family=Archivo:wght@400;600;700;800&display=swap');

/* ==========================================================================
   GearPOS — Design tokens ("Modernist" glass theme)
   ========================================================================== */
:root {
  --color-bg: #eef0f3;
  --color-text: #201e1d;
  --color-text-muted: rgba(32, 30, 29, .55);
  --color-text-faint: rgba(32, 30, 29, .45);
  --color-divider: rgba(32, 30, 29, .1);
  --color-divider-strong: rgba(32, 30, 29, .16);

  --color-accent: #ec3013;
  --color-accent-hover: #dd2b0f;
  --color-accent-active: #ae1800;
  --color-accent-dark: #7c1405;
  --color-accent-rgb: 236, 48, 19;
  --color-accent-soft: rgba(236, 48, 19, .09);
  --color-accent-soft-2: rgba(236, 48, 19, .14);
  --color-accent-light: #ff9783;

  --color-ok: #1a7f4f;
  --color-ok-soft: rgba(26, 127, 79, .12);
  --color-warn: #ae7a00;
  --color-warn-soft: rgba(255, 176, 32, .16);
  --color-danger: #ae1800;
  --color-danger-soft: rgba(255, 151, 131, .26);

  --font-heading: 'Archivo', system-ui, sans-serif;
  --font-body: 'Archivo', system-ui, sans-serif;

  --glass-bg: rgba(255, 255, 255, .94);
  --glass-bg-strong: rgba(255, 255, 255, .97);
  --glass-border: rgba(255, 255, 255, .9);

  --radius-xs: 8px;
  --radius-sm: 11px;
  --radius-md: 14px;
  --radius-lg: 18px;
  --radius-xl: 24px;
  --radius-2xl: 32px;
  --radius-pill: 999px;

  --shadow-sm: 0 4px 16px rgba(32, 30, 29, .07);
  --shadow-md: 0 8px 30px rgba(32, 30, 29, .09);
  --shadow-lg: 0 20px 50px rgba(32, 30, 29, .18);
  --shadow-xl: 0 30px 70px rgba(32, 30, 29, .28);
}

/* ==========================================================================
   Theme colors — override the --color-accent* group via [data-theme] on
   <html>. "merah" is the default defined on :root above, so it has no block
   here. Only these 5 vars change per theme; --color-ok/warn/danger etc. stay
   the same across all themes (semantic colors, not brand accent).
   ========================================================================== */
html[data-theme="hijau"] {
  --color-accent: #16a34a; --color-accent-hover: #128a3e; --color-accent-active: #0f6b30; --color-accent-dark: #0a4d22;
  --color-accent-rgb: 22, 163, 74;
  --color-accent-soft: rgba(22, 163, 74, .09); --color-accent-soft-2: rgba(22, 163, 74, .14); --color-accent-light: #86efac;
}
html[data-theme="orange"] {
  --color-accent: #ea580c; --color-accent-hover: #d14f0a; --color-accent-active: #a33f08; --color-accent-dark: #7a2f06;
  --color-accent-rgb: 234, 88, 12;
  --color-accent-soft: rgba(234, 88, 12, .09); --color-accent-soft-2: rgba(234, 88, 12, .14); --color-accent-light: #fdba74;
}
html[data-theme="biru"] {
  --color-accent: #2563eb; --color-accent-hover: #1d56d1; --color-accent-active: #1743a3; --color-accent-dark: #113176;
  --color-accent-rgb: 37, 99, 235;
  --color-accent-soft: rgba(37, 99, 235, .09); --color-accent-soft-2: rgba(37, 99, 235, .14); --color-accent-light: #93c5fd;
}
html[data-theme="ungu"] {
  --color-accent: #7c3aed; --color-accent-hover: #6d2fd6; --color-accent-active: #5623a8; --color-accent-dark: #3f1a7a;
  --color-accent-rgb: 124, 58, 237;
  --color-accent-soft: rgba(124, 58, 237, .09); --color-accent-soft-2: rgba(124, 58, 237, .14); --color-accent-light: #c4b5fd;
}

* , *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: 800;
  letter-spacing: -.02em;
  margin: 0 0 6px;
}
a { color: var(--color-accent-active); text-decoration: none; }
a:hover { color: var(--color-accent-dark); }
button { font-family: inherit; }
input, textarea, select { font-family: inherit; }
/* Checkbox/radio browser bawaan warnanya biru default OS/browser, TIDAK
   ikut tema warna tenant (`GP.applyTheme()`, var `--color-accent`) — set
   GLOBAL di sini (bukan cuma `.checkbox-row input`) supaya SEMUA checkbox
   di app (matriks Hak Akses, filter tabel, dll) konsisten ikut tema. */
input[type="checkbox"], input[type="radio"] { accent-color: var(--color-accent); }
::selection { background: rgba(var(--color-accent-rgb), .25); }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(32, 30, 29, .2); border-radius: 99px; }

.text-muted { color: var(--color-text-muted); }
.text-faint { color: var(--color-text-faint); }
.text-accent { color: var(--color-accent-active); }
.hidden { display: none !important; }

/* ==========================================================================
   Background ambient blobs (decorative, matches mockup)
   ========================================================================== */
.bg-blobs { position: fixed; inset: 0; overflow: hidden; z-index: 0; pointer-events: none; }
.bg-blobs span {
  position: absolute; border-radius: 50%; filter: blur(0px);
}
.bg-blobs .b1 { left: -160px; top: -140px; width: 620px; height: 620px; background: radial-gradient(circle at 38% 38%, rgba(var(--color-accent-rgb),.28), rgba(var(--color-accent-rgb),0) 70%); }
.bg-blobs .b2 { right: -140px; bottom: -180px; width: 680px; height: 680px; background: radial-gradient(circle at 50% 50%, rgba(255,151,131,.32), rgba(255,151,131,0) 68%); }
.bg-blobs .b3 { left: 45%; top: 55%; width: 420px; height: 420px; background: radial-gradient(circle at 50% 50%, rgba(32,30,29,.10), rgba(32,30,29,0) 70%); }

/* ==========================================================================
   Glass surface primitive
   ========================================================================== */
.glass {
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
}

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-heading); font-weight: 800; font-size: 13px; line-height: 1.2;
  border-radius: var(--radius-md); border: 1px solid transparent;
  padding: 11px 16px; cursor: pointer; white-space: nowrap;
  transition: background .14s, box-shadow .14s, transform .1s, border-color .14s, opacity .14s;
  background: transparent; color: var(--color-text);
}
.btn:active { transform: scale(.97); }
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }
/* `:disabled` cuma berlaku buat elemen form asli (`<button>`/`<input>`) —
   dipakai buat tombol `<a class="btn">` (mis. "Kirim ke kasir" di modal
   detail Work Order) yang perlu terlihat+berperilaku nonaktif tanpa ganti
   jadi `<button>`. `pointer-events:none` WAJIB ada di sini (beda dari
   `:disabled` yang otomatis diblokir browser) — tanpa itu, `<a>` tetap bisa
   diklik/navigasi normal walau kelihatan pudar. */
.btn.disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
.btn-primary { background: var(--color-accent); color: #fff; box-shadow: 0 6px 18px rgba(var(--color-accent-rgb),.3); }
.btn-primary:hover:not(:disabled) { background: var(--color-accent-hover); box-shadow: 0 8px 22px rgba(var(--color-accent-rgb),.36); color: #fff; }
.btn-secondary { border-color: var(--color-divider-strong); background: rgba(255,255,255,.5); color: var(--color-text); }
.btn-secondary:hover:not(:disabled) { background: rgba(32,30,29,.06); color: var(--color-text); }
.btn-ghost { color: var(--color-accent-active); padding-inline: 8px; }
.btn-ghost:hover { background: var(--color-accent-soft); color: var(--color-accent-active); }
/* color:var(--color-danger) (BUKAN --color-accent-active) — --color-danger
   itu semantik, SENGAJA TIDAK berubah lintas tema (lihat definisi :root),
   sedangkan --color-accent-active IKUT tema toko. Kebetulan di tema Merah
   default nilainya SAMA PERSIS (#ae1800) jadi bug ini "tersembunyi" sampai
   tema diganti — teks tombol "Ya, hapus" ikut jadi ungu/hijau/dst padahal
   hover background-nya (--color-danger-soft) TETAP merah, dua warna yang
   tidak nyambung. Ditemukan user lewat screenshot di tema Ungu. */
.btn-danger { color: var(--color-danger); border-color: var(--color-divider-strong); }
.btn-danger:hover { background: var(--color-danger-soft); color: var(--color-danger); }
.btn-block { width: 100%; }
.btn-sm { padding: 8px 12px; font-size: 12px; border-radius: var(--radius-sm); }
.btn-lg { padding: 15px 18px; font-size: 14.5px; border-radius: var(--radius-lg); }
.btn-icon { width: 34px; height: 34px; padding: 0; border-radius: var(--radius-sm); border: 1px solid var(--color-divider-strong); cursor: pointer; background: transparent; color: inherit; display: flex; align-items: center; justify-content: center; line-height: 1; font-size: 18px; }
.btn-icon:hover { background: rgba(32,30,29,.06); }
.btn svg { display: block; flex: none; }

.pill-btn {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 7px 13px; border-radius: var(--radius-pill); border: 1px solid var(--color-divider-strong);
  background: rgba(255,255,255,.62); font-family: var(--font-heading); font-weight: 800; font-size: 11.5px;
  color: var(--color-text); transition: all .14s;
}
.pill-btn:hover { background: rgba(32,30,29,.06); }
.pill-btn.active { border-color: var(--color-accent); background: var(--color-accent-soft-2); color: var(--color-accent-active); }

/* ==========================================================================
   Theme color swatch picker (Pengaturan > Aplikasi)
   ========================================================================== */
.theme-swatch {
  display: flex; flex-direction: column; align-items: center; gap: 8px; cursor: pointer; border: none; background: none;
  padding: 6px; border-radius: var(--radius-md); width: 92px;
}
.theme-swatch:hover { background: rgba(32,30,29,.05); }
.theme-swatch .dot {
  width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 16px rgba(32,30,29,.18); border: 3px solid transparent; transition: border-color .14s, transform .14s;
  color: #fff;
}
.theme-swatch.active .dot { border-color: var(--color-text); transform: scale(1.06); }
.theme-swatch .label { font-family: var(--font-heading); font-weight: 800; font-size: 12px; }

/* ==========================================================================
   Forms
   ========================================================================== */
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.field label { font-size: 11.5px; color: var(--color-text-muted); font-weight: 600; }
.field label .req { color: var(--color-accent-active); margin-left: 2px; }
.field-row { display: grid; gap: 12px; }
.field-row.cols-2 { grid-template-columns: 1fr 1fr; }
.field-row.cols-3 { grid-template-columns: 1fr 1fr 1fr; }
.field-row.cols-3-lead { grid-template-columns: 1.6fr 1fr 1fr; }

/* Dua <input> ditampilkan sebagai SATU kotak (mis. "Merek & model") — nilainya
   tetap 2 field terpisah di balik layar, cuma dirapatkan visualnya buat hemat
   tempat. Input pertama sengaja lebih sempit (mis. merek biasanya pendek). */
.input-group {
  display: flex; align-items: stretch; border: 1px solid var(--color-divider-strong);
  border-radius: var(--radius-md); background: rgba(255,255,255,.75);
  transition: border-color .14s, box-shadow .14s;
}
.input-group:focus-within { border-color: var(--color-accent); box-shadow: 0 0 0 3px rgba(var(--color-accent-rgb),.14); }
.input-group input {
  flex: 1; min-width: 0; border: 0; outline: none; background: transparent; color: inherit;
  padding: 11px 13px; font-size: 13.5px;
}
.input-group input::placeholder { color: rgba(32,30,29,.4); }
.input-group input:first-child { flex: 0 0 36%; border-right: 1px solid var(--color-divider); }
.input, .select, textarea.input {
  width: 100%; border: 1px solid var(--color-divider-strong); border-radius: var(--radius-md);
  padding: 11px 13px; font-size: 13.5px; background: rgba(255,255,255,.75); color: inherit; outline: none;
  transition: border-color .14s, box-shadow .14s;
}
/* Kotak bergaya .input tapi non-interaktif (mis. "Cabang" read-only yang ikut
   sesi aktif, dipakai Pengeluaran & Bay) — pakai `<div class="input input-display">`.
   WAJIB set line-height eksplisit di sini: <div> mewarisi line-height dari body
   (1.55) sedangkan <input> asli pakai default UA browser (~normal/1.2), jadi
   tanpa ini kotaknya kelihatan lebih TINGGI dari input di sebelahnya (gotcha
   yang sama seperti <a> vs <button>, lihat gotcha #15 di CLAUDE.md). */
.input-display { background: rgba(32,30,29,.04); color: var(--color-text-muted); display: flex; align-items: center; line-height: 1.2; }
select.select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 38px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23201e1d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center; background-size: 16px 16px;
}
select.select option {
  background-color: #fff; color: var(--color-text); padding: 8px 10px;
}
select.select option:hover, select.select option:focus {
  background-color: var(--color-accent-soft-2); color: var(--color-accent-active);
}
select.select option:checked {
  background-color: var(--color-accent) !important; color: #fff !important;
}
/* Input yang jadi read-only sementara (mis. Merek/Model/Tahun di WO Baru
   setelah plat ke-match dari database — lihat workorder-baru.js) — dibuat
   TERLIHAT beda dari input yang memang biasa boleh diketik, konsisten
   secara visual dengan `.input-display` (background muted serupa) TANPA
   perlu ganti markup jadi `<div>`. `:read-only` HANYA berlaku kalau
   attribute `readonly` benar-benar dipasang (bukan cuma `disabled` polos). */
.input:read-only { background: rgba(32,30,29,.04); color: var(--color-text-muted); cursor: default; }
.chip:disabled { opacity: .5; cursor: not-allowed; }
.input:hover, .select:hover { border-color: rgba(32,30,29,.3); }
.input:focus, .select:focus, textarea.input:focus { border-color: var(--color-accent); box-shadow: 0 0 0 3px rgba(var(--color-accent-rgb),.14); }
textarea.input { min-height: 80px; resize: vertical; }
.input::placeholder { color: rgba(32,30,29,.4); }
.input-prefix-group, .input-suffix-group { display: flex; gap: 8px; }
.input-prefix, .input-suffix { flex: none; display: flex; align-items: center; padding: 0 12px; border: 1px solid var(--color-divider-strong); border-radius: var(--radius-md); background: rgba(255,255,255,.75); font-family: var(--font-heading); font-weight: 800; font-size: 13px; }
.field-note { font-size: 11.5px; color: var(--color-text-muted); }
.field-note.ok, .text-faint.ok { color: var(--color-accent-active); }
/* color:var(--color-danger) (BUKAN --color-accent-dark) — pesan error WAJIB
   selalu merah biar tetap kelihatan jelas "ini salah" di tema apapun, sama
   alasannya dengan fix .btn-danger di atas. --color-accent-dark ikut tema
   (jadi ungu/hijau/dst), kebetulan sama dengan merah cuma di tema Merah
   default — ditemukan user lewat screenshot pesan error di tema Ungu. */
.field-note.err, .text-faint.err { color: var(--color-danger); font-weight: 700; }

/* Validation error state — border/background box error, SELALU merah semantik
   (--color-danger), bukan warna tema — sama alasan seperti di atas. */
.input.err, .select.err { border-color: var(--color-danger); background: var(--color-danger-soft); box-shadow: 0 0 0 3px rgba(174,24,0,.1); }
.field-box.err { border: 1px solid var(--color-danger); border-radius: var(--radius-md); background: var(--color-danger-soft); padding: 8px; margin: -8px; }

/* Rich-text editor ringan (toolbar Bold/Italic/Underline/List + area
   contenteditable, GP.richEditor di app.js) — dipakai field "Isi Konten"
   Halaman & Blog. SENGAJA toolbar buatan sendiri (bukan library seperti
   Quill/TinyMCE) — cukup buat kebutuhan dasar (tebal/miring/garis
   bawah/list), tanpa nambah file vendor baru. Dibungkus sendiri (bukan
   `.input`) karena bentuknya toolbar+body, bukan kotak input polos. */
.rte { border: 1px solid var(--color-divider-strong); border-radius: var(--radius-md); background: rgba(255,255,255,.75); overflow: hidden; transition: border-color .14s, box-shadow .14s; }
.rte:focus-within { border-color: var(--color-accent); box-shadow: 0 0 0 3px rgba(var(--color-accent-rgb),.14); }
.rte.err { border-color: var(--color-danger); background: var(--color-danger-soft); box-shadow: 0 0 0 3px rgba(174,24,0,.1); }
.rte-toolbar { display: flex; align-items: center; gap: 2px; flex-wrap: wrap; padding: 6px 8px; border-bottom: 1px solid var(--color-divider); background: rgba(32,30,29,.03); }
.rte-toolbar button { width: 28px; height: 28px; flex: none; border: 0; border-radius: 7px; background: transparent; color: var(--color-text); cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 13px; font-family: var(--font-heading); transition: background .12s, color .12s; }
.rte-toolbar button:hover { background: rgba(32,30,29,.08); }
.rte-toolbar button.active { background: rgba(var(--color-accent-rgb),.16); color: var(--color-accent); }
.rte-sep { width: 1px; height: 18px; background: var(--color-divider-strong); margin: 0 4px; flex: none; }
.rte-body { min-height: 140px; max-height: 320px; overflow-y: auto; padding: 11px 13px; font-size: 13.5px; line-height: 1.55; outline: none; }
/* Post Website ditulis di modal-lg (lebih lega dari modal RTE lain seperti
   Halaman) — area menulisnya ikut ditinggikan supaya post panjang kelihatan
   lebih banyak sekaligus, bukan cuma modalnya doang yang lebar. */
.modal-lg .rte-body { max-height: 440px; }
.rte-body.is-empty::before { content: attr(data-placeholder); display: block; color: rgba(32,30,29,.4); pointer-events: none; }
.rte-body p { margin: 0 0 8px; }
.rte-body p:last-child { margin-bottom: 0; }
.rte-body ul, .rte-body ol { padding-left: 22px; margin: 6px 0; }

/* Input dengan ikon di kiri (mis. username/password login) + tombol toggle opsional di kanan (lihat/sembunyikan password) */
.field-icon-wrap { position: relative; }
.field-icon-wrap > .input { padding-left: 42px; }
.field-icon-wrap.has-toggle > .input { padding-right: 76px; }
.field-icon-wrap .field-icon {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  display: flex; color: rgba(32,30,29,.4); pointer-events: none;
}
.field-icon-wrap .field-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  border: 1px solid var(--color-divider-strong); border-radius: 10px; background: rgba(255,255,255,.85);
  padding: 6px 11px; font-size: 11.5px; font-weight: 700; color: var(--color-text-muted);
  cursor: pointer; transition: background .14s, color .14s;
}
.field-icon-wrap .field-toggle:hover { background: rgba(32,30,29,.06); color: var(--color-text); }

/* Input dengan tombol ikon polos nempel di kanan (mis. buka modal pencarian
   lengkap) — beda dari .field-icon-wrap di atas yang selalu asumsikan ikon
   KIRI juga terpasang; pakai ini kalau field-nya TIDAK butuh ikon kiri. */
.field-btn-wrap { position: relative; }
.field-btn-wrap > .input { padding-right: 84px; }
.field-btn-wrap .field-btn {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  height: 30px; padding: 0 12px; border-radius: 9px; border: 1px solid var(--color-divider-strong);
  background: rgba(255,255,255,.85); display: flex; align-items: center; gap: 6px; justify-content: center;
  cursor: pointer; color: var(--color-text-muted); font-size: 11.5px; font-weight: 700;
  transition: background .14s, color .14s;
}
.field-btn-wrap .field-btn:hover { background: rgba(32,30,29,.06); color: var(--color-text); }

/* Badge kecil penunjuk shortcut keyboard (mis. "F9" di tombol Bayar Kasir) —
   dipakai DUA cara: (1) diselipkan langsung sebagai anak `.btn`/`.pill-btn`
   (elemen itu sudah flex+gap, jadi badge otomatis nempel rapi di ujung tanpa
   CSS posisi tambahan), atau (2) dibungkus `.input-kbd-wrap` buat nempel di
   kanan sebuah `<input>` polos (pola sama seperti `.field-btn-wrap`, cuma
   versinya buat teks bukan tombol aksi). */
.kbd-badge {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  padding: 1px 6px; border-radius: 5px; background: rgba(32,30,29,.08);
  border: 1px solid var(--color-divider-strong); font-family: var(--font-heading);
  font-weight: 800; font-size: 10px; color: var(--color-text-muted); line-height: 1.5;
}
.btn-primary .kbd-badge { background: rgba(255,255,255,.22); border-color: rgba(255,255,255,.32); color: rgba(255,255,255,.9); }
.input-kbd-wrap { position: relative; }
.input-kbd-wrap > .input { padding-right: 42px; }
.input-kbd-wrap > .kbd-badge { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); }

/* Pemilih file bergaya sendiri (2026-09-23) — pengganti `<input type="file">`
   polos (tombol "Choose File" bawaan OS/browser, tidak bisa diberi gaya dan
   tampak asing di tengah UI custom app ini). Kontrak markup: `<label
   class="file-picker" id="...-picker"><input type="file" hidden-secara-css>
   <svg ikon><span class="file-picker-text">...</span><span class=
   "file-picker-browse">Pilih File</span></label>` — `<label>` MEMBUNGKUS
   input aslinya (bukan `for=`/`id` terpisah), jadi klik di MANA SAJA di
   kotak ini (termasuk teks "Pilih File") otomatis membuka dialog file
   TANPA JS tambahan. JS cuma perlu isi `.file-picker-text` + toggle
   `.has-file` saat event `change` (lihat `katalog-import.js`/
   `customer-import.js` — dua pemakaian saat ini, pola DIDUPLIKASI sengaja
   sama seperti print helper, bukan di-share lewat `GP`). Input asli
   disembunyikan visual-only (BUKAN `display:none` — itu bikin sebagian
   browser skip dari tab order/pemicu label), pola sama teknik
   "visually-hidden" aksesibilitas standar. */
.file-picker {
  display: flex; align-items: center; gap: 10px; position: relative;
  border: 1px dashed var(--color-divider-strong); border-radius: var(--radius-md);
  padding: 10px 12px; cursor: pointer; background: rgba(255,255,255,.75);
  transition: border-color .14s, background .14s;
}
.file-picker:hover { border-color: var(--color-accent); background: var(--color-accent-soft); }
.file-picker input[type="file"] {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.file-picker-icon { flex: none; display: flex; color: var(--color-text-muted); transition: color .14s; }
.file-picker:hover .file-picker-icon { color: var(--color-accent-active); }
.file-picker-text { flex: 1; min-width: 0; font-size: 13px; color: var(--color-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-picker.has-file .file-picker-text { color: var(--color-text); font-weight: 700; }
.file-picker-browse {
  flex: none; font-family: var(--font-heading); font-weight: 700; font-size: 11.5px;
  padding: 6px 14px; border-radius: 999px; background: var(--color-accent-soft-2);
  color: var(--color-accent-active); transition: background .14s, color .14s;
}
.file-picker:hover .file-picker-browse { background: var(--color-accent); color: #fff; }

.chip-group { display: flex; flex-wrap: wrap; gap: 7px; }

.chip-scroll { display: flex; align-items: center; gap: 4px; min-width: 0; max-width: 100%; }
/* Modifier buat chip-scroll yang berbagi SATU baris dengan kontrol lain di sebelah kirinya
   (mis. tab/tombol sort) dan perlu rata-kanan — margin FIXED (bukan cuma ml-auto) supaya
   jarak dari sibling di kirinya tidak collapse jadi 0 saat kontennya overflow (lihat gotcha
   margin-auto-collapse di CLAUDE.md). Chip-scroll yang berdiri sendiri di barisnya sendiri
   (mis. Katalog) TIDAK perlu modifier ini — biarkan rata kiri & lebar penuh alami. */
.chip-scroll.chip-scroll-inline { flex: 1 1 auto; margin-left: 14px; justify-content: flex-end; }
.chip-group.chip-group-scroll {
  flex-wrap: nowrap; overflow-x: auto; min-width: 0;
  scrollbar-width: none; -ms-overflow-style: none; scroll-behavior: smooth;
}
.chip-group.chip-group-scroll::-webkit-scrollbar { display: none; }
.chip-group.chip-group-scroll .pill-btn { flex: none; }
.chip-scroll-arrow {
  flex: none; width: 26px; height: 26px; border-radius: 50%; padding: 0;
  border: 1px solid var(--color-divider-strong); background: #fff; color: var(--color-text);
  display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background .14s;
}
.chip-scroll-arrow:hover { background: rgba(32,30,29,.06); }
.chip-scroll-arrow[hidden] { display: none; }
/* Tag multi-select (cari + pilih, hasil pilihan jadi "pill" di DALAM kotak
   input itu sendiri, tiap pill punya tombol × buat deselect) — pola kotak
   SAMA `.input-group` (border+radius+focus-within), dipakai pertama kali
   buat "Spesialis Merk" Cabang (gantiin grid chip lama yang kepanjangan
   buat ~45 opsi, permintaan user 2026-09-19: pola ala select2). */
.tag-select {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  border: 1px solid var(--color-divider-strong); border-radius: var(--radius-md);
  padding: 6px 8px; background: rgba(255,255,255,.75);
  transition: border-color .14s, box-shadow .14s;
}
.tag-select:focus-within { border-color: var(--color-accent); box-shadow: 0 0 0 3px rgba(var(--color-accent-rgb),.14); }
.tag-select-pill {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  background: var(--color-accent-soft-2); color: var(--color-accent-active);
  border-radius: 999px; padding: 5px 6px 5px 11px; font-size: 12px; font-weight: 700; white-space: nowrap;
}
.tag-select-pill button {
  border: none; background: rgba(255,255,255,.6); color: inherit; width: 16px; height: 16px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 10px; line-height: 1; padding: 0;
}
.tag-select-pill button:hover { background: #fff; }
.tag-select-input {
  flex: 1 1 100px; min-width: 90px; border: 0; outline: none; background: transparent;
  font-size: 13.5px; padding: 5px 3px; color: inherit;
}
.tag-select-input::placeholder { color: rgba(32,30,29,.4); }
.chip {
  padding: 9px 14px; border-radius: var(--radius-md); border: 1.5px solid var(--color-divider-strong);
  background: rgba(255,255,255,.62); cursor: pointer; font-size: 12.5px; font-weight: 700;
  transition: all .14s; text-align: left;
}
.chip:hover { background: rgba(32,30,29,.05); }
.chip.active { border-color: var(--color-accent); background: var(--color-accent-soft-2); color: var(--color-accent-active); }
.chip .chip-sub { display: block; font-weight: 400; font-size: 11px; color: var(--color-text-muted); margin-top: 1px; }

.switch-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
.checkbox-row { display: flex; align-items: flex-start; gap: 9px; font-size: 12.5px; cursor: pointer; }
.checkbox-row input { accent-color: var(--color-accent); width: 15px; height: 15px; margin-top: 1px; }

/* ==========================================================================
   Cards / KPI
   ========================================================================== */
.card {
  background: rgba(255,255,255,.7); border: 1px solid rgba(32,30,29,.08);
  border-radius: var(--radius-lg); padding: 14px;
}
.card-hover { transition: transform .14s, box-shadow .14s, border-color .14s; }
.card-hover:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: rgba(var(--color-accent-rgb),.4); }

.kpi-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.kpi-card {
  background: rgba(255,255,255,.7); border: 1px solid rgba(32,30,29,.08);
  border-radius: var(--radius-lg); padding: 14px 16px; min-width: 0;
  display: flex; align-items: flex-start; gap: 12px;
}
.kpi-icon {
  width: 38px; height: 38px; flex: none; border-radius: 12px;
  background: rgba(32,30,29,.06); color: rgba(32,30,29,.55);
  display: flex; align-items: center; justify-content: center;
}
.kpi-icon.accent { background: var(--color-accent-soft-2); color: var(--color-accent-active); }
.kpi-icon svg { display: block; }
.kpi-label { font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--color-text-muted); }
.kpi-value { font-family: var(--font-heading); font-weight: 800; font-size: 24px; letter-spacing: -.03em; margin-top: 3px; }
.kpi-note { font-size: 11.5px; color: var(--color-text-muted); margin-top: 2px; }
/* Every module's kpi() helper wraps label/value/note in a bare, unclassed
   <div> as the second flex child of .kpi-card (icon is the first) — without
   min-width:0 that column refuses to shrink below its content's natural
   width (e.g. a long "Rp 2.780.550" value), letting it creep past the card's
   own edge on narrow screens. Fixed structurally here so it applies to every
   module's KPI cards at once, not just one file's. */
.kpi-card > div:last-child { min-width: 0; }

/* Varian ringkas — taruh kelas `kpi-compact` di wrapper (`<div>` pembungkus
   label/value/note) buat naruh `.kpi-note` SEBARIS dengan `.kpi-value`
   (bukan di baris terpisah di bawahnya) — kartu jadi cuma 2 baris (label,
   lalu angka+catatan) alih-alih 3, buat konteks yang butuh KPI lebih padat/
   tidak makan banyak tempat vertikal (mis. Papan Work Order, lihat
   workorder.js). OPT-IN per pemakaian — `.kpi-value`/`.kpi-note` default
   TIDAK diubah, supaya modul lain yang sudah pakai 3-baris tidak ikut
   berubah tanpa diminta. */
.kpi-compact .kpi-value, .kpi-compact .kpi-note { display: inline-block; }
.kpi-compact .kpi-note { margin-top: 0; margin-left: 6px; }

/* ==========================================================================
   Tags / badges
   ========================================================================== */
.badge {
  display: inline-flex; align-items: center; gap: 4px; font-size: 10.5px; font-weight: 700;
  padding: 3px 9px; border-radius: var(--radius-pill); white-space: nowrap;
}
.badge-neutral { background: rgba(32,30,29,.08); color: rgba(32,30,29,.65); }
.badge-accent { background: var(--color-accent-soft-2); color: var(--color-accent-active); }
.badge-dark { background: var(--color-text); color: #fff; }
.badge-ok { background: var(--color-ok-soft); color: var(--color-ok); }
.badge-warn { background: var(--color-warn-soft); color: var(--color-warn); }
.badge-danger { background: var(--color-danger-soft); color: var(--color-danger); }

/* ==========================================================================
   Tables
   ========================================================================== */
.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table th {
  text-align: left; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--color-text-muted); padding: 10px 12px; border-bottom: 2px solid var(--color-divider);
  white-space: nowrap;
}
.table td { padding: 10px 12px; border-bottom: 1px solid var(--color-divider); vertical-align: middle; }
.table tbody tr { cursor: pointer; transition: background .12s; }
.table tbody tr:hover { background: rgba(32,30,29,.035); }
.table .col-right { text-align: right; }
.table .row-actions { opacity: 0; transition: opacity .12s; display: flex; gap: 4px; justify-content: flex-end; }
.table tbody tr:hover .row-actions { opacity: 1; }
.table-empty { padding: 30px 16px; text-align: center; color: var(--color-text-muted); font-size: 12.5px; border: 1px dashed rgba(32,30,29,.2); border-radius: var(--radius-md); }

.pagination-bar { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; align-items: center; }
.pagination { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.pagination .pg-btn {
  min-width: 30px; height: 30px; padding: 0 8px; border-radius: var(--radius-sm); border: 1px solid var(--color-divider-strong);
  background: rgba(255,255,255,.6); font-size: 12px; font-weight: 700; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.pagination .pg-btn.active { background: var(--color-accent); color: #fff; border-color: var(--color-accent); }
.pagination .pg-btn:disabled { opacity: .4; cursor: not-allowed; }
.pagination .pg-ellipsis { padding: 0 4px; color: var(--color-text-faint); }

/* ==========================================================================
   Tabs
   ========================================================================== */
.tabs { display: flex; gap: 22px; border-bottom: 1px solid var(--color-divider); }
.tabs .tab-btn {
  background: none; border: none; cursor: pointer; padding: 9px 0 11px; font-family: var(--font-heading);
  font-weight: 800; font-size: 13px; color: var(--color-text-muted); display: flex; align-items: center; gap: 7px;
  box-shadow: inset 0 -2px 0 transparent; margin-bottom: -1px;
}
.tabs .tab-btn.active { color: var(--color-text); box-shadow: inset 0 -2px 0 var(--color-accent); }
.tabs .tab-count { font-size: 10.5px; font-weight: 700; padding: 1px 7px; border-radius: var(--radius-pill); background: rgba(32,30,29,.08); color: var(--color-text-muted); }
.tabs .tab-btn.active .tab-count { background: var(--color-accent-soft-2); color: var(--color-accent-active); }

/* ==========================================================================
   Sidebar / App shell
   ========================================================================== */
/* height:100% (BUKAN 100vh) SENGAJA — `vh` dihitung dari viewport ASLI
   (fisik) lalu SELURUH render di-scale oleh fitur "Ukuran Tampilan"
   (CSS `zoom` di <html>, lihat GP.applyZoom() di app.js), jadi kalau pakai
   `100vh` di sini, hasil render zoom<100% akan menyisakan gap kosong di
   bawah viewport (mis. zoom 90% pada viewport 900px cuma menghasilkan
   tinggi rendered 810px, gap 90px). `height:100%` mengikuti rantai
   `html,body{height:100%}` (lihat atas) yang justru resolve dengan benar
   di bawah zoom, karena unit persen dihitung ulang relatif terhadap
   ancestor-nya di ruang koordinat yang sama (ikut ter-scale bersama),
   bukan dipatok ke viewport fisik seperti vh/vw. */
.app-shell { position: relative; height: 100%; display: flex; padding: 14px; gap: 14px; box-sizing: border-box; z-index: 1; overflow: hidden; }
.sidebar {
  position: relative; z-index: 30; width: 236px; flex: none; border-radius: var(--radius-xl);
  box-shadow: var(--shadow-md), inset 0 1px 0 rgba(255, 255, 255, .7); display: flex; flex-direction: column; padding: 18px 12px;
  /* max-height:100% (BUKAN "calc(100% - 28px)") — dulu waktu `.app-shell`
     masih `height:100vh`, "-28px" itu perlu buat mengompensasi padding
     14px atas+bawah MILIK app-shell sendiri (yang belum ke-exclude dari
     referensi 100vh yang dipatok ke viewport MENTAH). Sekarang referensi
     "%"-nya sudah relatif ke CONTENT-BOX .app-shell (yang otomatis SUDAH
     tidak menghitung padding-nya sendiri) — jadi "-28px" itu MENGURANGI
     DUA KALI dan bikin sidebar 28px lebih pendek dari yang seharusnya
     (celah kosong di bawah, ditemukan dari screenshot user). Dibuktikan
     lewat percobaan langsung: lepas max-height sepenuhnya bikin sidebar
     PAS 894px (match persis content-box .app-shell via align-items:stretch
     bawaan flex, tanpa perlu constraint tambahan apapun). */
  max-height: 100%; transition: width .2s ease;
}
.sidebar.collapsed { width: 76px; }
.sidebar-collapse-btn {
  position: absolute; top: 30px; right: -12px; width: 24px; height: 24px; border-radius: 50%; z-index: 31;
  background: #fff; border: 1px solid var(--color-divider-strong); box-shadow: var(--shadow-sm);
  display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--color-text-muted);
  opacity: 0; pointer-events: none; transition: background .14s, color .14s, opacity .14s;
}
.sidebar:hover .sidebar-collapse-btn { opacity: 1; pointer-events: auto; }
.sidebar-collapse-btn:hover { background: rgba(32,30,29,.06); color: var(--color-text); }
.sidebar-collapse-btn svg { transition: transform .2s ease; }
.sidebar.collapsed .sidebar-collapse-btn svg { transform: rotate(180deg); }

/* Collapsed state: icon-only rail, labels/sub-menus hidden */
.sidebar.collapsed .sidebar-brand { justify-content: center; padding: 0 0 16px; }
.sidebar.collapsed .brand-text,
.sidebar.collapsed .nav-link span,
.sidebar.collapsed .nav-group-btn span,
.sidebar.collapsed .nav-section-label,
.sidebar.collapsed .sidebar-widget,
.sidebar.collapsed .profile-meta-wrap,
.sidebar.collapsed .branch-switcher,
.sidebar.collapsed .plan-badge-wrap,
.sidebar.collapsed .profile-chev { display: none; }
.sidebar.collapsed .nav-link, .sidebar.collapsed .nav-group-btn { justify-content: center; padding: 10px 0; }
.sidebar.collapsed .nav-group-items { display: none !important; }
.sidebar.collapsed .profile-btn { justify-content: center; padding: 8px 0; }
.sidebar-brand { position: relative; display: flex; align-items: center; gap: 10px; padding: 0 6px 16px; }
.sidebar-brand .logo {
  width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; flex: none; overflow: hidden; box-sizing: border-box; border-radius: var(--radius-sm);
}
.sidebar-brand .logo.logo-default {
  background: linear-gradient(140deg, var(--color-accent-light), var(--color-accent));
  box-shadow: 0 6px 16px rgba(var(--color-accent-rgb),.34); padding: 8px;
}
.sidebar-brand .logo img { width: 100%; height: 100%; object-fit: contain; }
.sidebar-brand .name { font-family: var(--font-heading); font-weight: 800; font-size: 15px; line-height: 1.1; }
.sidebar-brand .sub { font-size: 10.5px; color: var(--color-text-muted); }

.plan-badge-wrap { padding: 0 6px 12px; }
.plan-badge-wrap .badge { cursor: default; }

/* Lonceng notifikasi — ditempel di baris brand (satu-satunya bagian shell
   yang tampil sama persis di semua halaman tanpa perlu diulang per-view).
   `.bell-dot`/`.notif-panel` SENGAJA base-rule `display:none` (bukan
   attribute `hidden`) — ditoggle via `.show()/.hide()` jQuery di JS, lihat
   gotcha CLAUDE.md soal `[hidden]` kalah cascade dari class ber-display
   eksplisit. `.sidebar.collapsed` sembunyikan lonceng ini juga (pola sama
   elemen sidebar lain yang textual/butuh ruang), badge count masih
   kebawa lewat title attribute kalau perlu nanti. */
.bell-btn {
  position: relative; flex: none; margin-left: auto; width: 32px; height: 32px; border-radius: 50%; border: none; cursor: pointer;
  background: transparent; color: var(--color-text-muted); display: flex; align-items: center; justify-content: center;
  transition: background .14s, color .14s;
}
.bell-btn:hover { background: rgba(32,30,29,.06); color: var(--color-text); }
.bell-btn.open { background: var(--color-accent-soft); color: var(--color-accent-active); }
.sidebar.collapsed .bell-btn { display: none; }
.bell-dot {
  display: none; position: absolute; top: 4px; right: 4px; min-width: 15px; height: 15px; padding: 0 3px; border-radius: 999px;
  background: var(--color-accent); color: #fff; font-size: 9.5px; font-weight: 800; line-height: 15px; text-align: center;
  box-shadow: 0 0 0 2px var(--glass-bg-strong);
}
.notif-panel {
  display: none; position: absolute; left: 6px; right: 6px; top: calc(100% - 4px); z-index: 40;
  background: #fff; border: 1px solid var(--color-divider-strong); border-radius: var(--radius-md); box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.notif-panel-head { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 8px; }
.notif-panel-head .title { font-family: var(--font-heading); font-weight: 800; font-size: 12.5px; }
.notif-panel-head .mark-read { font-size: 10.5px; font-weight: 700; color: var(--color-accent-active); background: none; border: none; cursor: pointer; padding: 0; }
.notif-panel-head .mark-read:disabled { color: var(--color-text-faint); cursor: default; }
.notif-list { max-height: 420px; overflow-y: auto; } /* panel hanya memuat 5 item, jadi praktis tanpa scroll */
/* Daftar lengkap di modal "Semua Notifikasi" — tinggi mengikuti modal, bukan 320px seperti panel lonceng. */
.notif-list-full { max-height: 60vh; }
.notif-list-full .notif-item { padding: 12px 18px; }
.notif-list-full .notif-item .msg { font-size: 13px; }
.notif-list-full .notif-item .time { font-size: 11px; }
.notif-all-btn { display: block; width: 100%; padding: 10px 14px; border: none; border-top: 1px solid var(--color-divider); background: transparent; cursor: pointer; font-size: 11.5px; font-weight: 800; color: var(--color-accent-active); text-align: center; }
.notif-all-btn:hover { background: rgba(32,30,29,.04); }
.notif-item {
  display: flex; gap: 9px; padding: 10px 14px; border-top: 1px solid var(--color-divider);
  cursor: pointer; text-decoration: none; color: inherit;
}
.notif-item:hover { background: rgba(32,30,29,.04); }
.notif-item.unread { background: var(--color-accent-soft); }
.notif-item.unread:hover { background: var(--color-accent-soft-2); }
/* Kondisi live (mis. stok kritis) — SENGAJA beda class dari `.unread` (walau
   tampilan sama) supaya tidak kena selector klik-tandai-dibaca JS, lihat
   isLiveNotif() di sidebar.js. Tidak pernah "hilang" karena diklik, cuma
   hilang begitu kondisinya sendiri tidak lagi kritis. */
.notif-item.live { background: var(--color-warn-soft); }
.notif-item.live:hover { background: var(--color-warn-soft); filter: brightness(0.98); }
.notif-item.live .ic.ok { background: rgba(174,122,0,.14); color: var(--color-warn); }
.notif-item .ic { width: 28px; height: 28px; border-radius: 9px; flex: none; display: flex; align-items: center; justify-content: center; }
.notif-item .ic.ok { background: var(--color-ok-soft); color: var(--color-ok); }
.notif-item .ic.accent { background: var(--color-accent-soft-2); color: var(--color-accent-active); }
.notif-item .body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.notif-item .msg { font-size: 11.5px; font-weight: 700; line-height: 1.4; }
.notif-item .time { font-size: 10px; color: var(--color-text-faint); }
.notif-empty { padding: 24px 14px; text-align: center; font-size: 11.5px; color: var(--color-text-muted); }

/* Switcher "cabang aktif" — cuma dirender (via JS, `hidden` dilepas) untuk role
   ber-izin `pengguna.read`. Ditaruh di antara brand & nav, area yang sama yang
   ditandai user buat penempatan ini. */
.branch-switcher { position: relative; padding: 0 6px 12px; }
.branch-switcher-btn {
  display: flex; align-items: center; gap: 7px; width: 100%; padding: 8px 10px; border-radius: var(--radius-md);
  border: 1px solid var(--color-divider-strong); background: rgba(255,255,255,.55); cursor: pointer;
  color: var(--color-text); font-family: var(--font-heading); font-weight: 700; font-size: 11.5px; transition: background .14s;
}
.branch-switcher-btn:hover { background: rgba(var(--color-accent-rgb),.09); }
.branch-switcher-btn svg:first-child { flex: none; color: var(--color-accent); }
.branch-switcher-btn svg:last-child { flex: none; color: var(--color-text-muted); transition: transform .16s; }
.branch-switcher.open .branch-switcher-btn svg:last-child { transform: rotate(180deg); }
.branch-switcher-menu {
  position: absolute; left: 6px; right: 6px; top: calc(100% - 6px); z-index: 40; padding: 6px;
  background: #fff; border: 1px solid var(--color-divider-strong); border-radius: var(--radius-md); box-shadow: var(--shadow-md);
  max-height: 260px; overflow-y: auto;
}
.branch-switcher-menu .item {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 9px; border-radius: var(--radius-sm);
  border: none; background: transparent; cursor: pointer; text-align: left; font-size: 12px; font-weight: 700; color: var(--color-text);
}
.branch-switcher-menu .item:hover { background: rgba(32,30,29,.06); }
.branch-switcher-menu .item.active { background: var(--color-accent-soft-2); color: var(--color-accent-active); }
.branch-switcher-menu .item .sub { font-weight: 500; color: var(--color-text-muted); font-size: 10.5px; padding-top: 5px; }
.sidebar hr.sep { border: none; height: 1px; background: var(--color-divider); margin: 0 6px 12px; }
.sidebar-nav { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; padding-right: 2px; }

/* Scrollbar sidebar cuma kelihatan saat hover — DESKTOP SAJA (>860px, sama
   breakpoint mobile drawer di bawah). Track/thumb TETAP reserve lebar 6px
   terus-menerus (bukan discroll-width:none baru muncul pas hover) supaya
   tidak ada reflow/jitter konten sidebar pas kursor masuk/keluar — cuma
   WARNA thumb-nya yang di-toggle transparan/kelihatan. Sengaja TIDAK
   disentuh di mobile (breakpoint 860px) — perangkat sentuh tidak punya
   hover state yang bisa diandalkan (gotcha sama yang sudah dicatat di
   tombol edit/hapus baris tabel), scrollbar sentuh bawaan OS sudah cukup. */
@media (min-width: 861px) {
  .sidebar-nav { scrollbar-width: thin; scrollbar-color: transparent transparent; }
  .sidebar-nav:hover { scrollbar-color: rgba(32,30,29,.28) transparent; }
  .sidebar-nav::-webkit-scrollbar { width: 6px; }
  .sidebar-nav::-webkit-scrollbar-track { background: transparent; }
  .sidebar-nav::-webkit-scrollbar-thumb { background: transparent; border-radius: 99px; transition: background-color .15s ease; }
  .sidebar-nav:hover::-webkit-scrollbar-thumb { background: rgba(32,30,29,.28); }
}

.nav-section-label {
  font-family: var(--font-heading); font-weight: 800; font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--color-text-muted); padding: 14px 11px 4px; margin-top: 4px;
}
.nav-section-label:first-child { margin-top: 0; padding-top: 4px; }

.nav-link, .nav-group-btn {
  display: flex; align-items: center; gap: 10px; padding: 10px 11px; border-radius: var(--radius-md);
  cursor: pointer; border: none; background: transparent; width: 100%; text-align: left; box-sizing: border-box;
  font-family: var(--font-heading); font-weight: 800; font-size: 13px; color: var(--color-text); transition: background .12s, color .12s;
}
.nav-link:hover, .nav-group-btn:hover { background: rgba(var(--color-accent-rgb),.09); }
.nav-link.active { background: var(--color-accent); color: #fff; box-shadow: 0 4px 14px rgba(var(--color-accent-rgb),.3); }
.nav-link svg, .nav-group-btn svg { flex: none; width: 18px; height: 18px; }
.nav-group-btn .chev { margin-left: auto; transition: transform .16s; color: rgba(32,30,29,.4); }
.nav-group-btn .count { font-size: 10.5px; font-weight: 700; color: var(--color-text-muted); margin-left: auto; }
.nav-group-btn.open .chev { transform: rotate(90deg); }
.nav-group-items { display: flex; flex-direction: column; gap: 1px; margin: 2px 0 6px 20px; padding-left: 11px; border-left: 1.5px solid rgba(32,30,29,.12); }
.nav-sub-link {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 11px; cursor: pointer;
  font-family: var(--font-heading); font-weight: 800; font-size: 12px; color: var(--color-text); background: none; border: none; width: 100%; text-align: left;
}
.nav-sub-link:hover { background: rgba(var(--color-accent-rgb),.09); }
.nav-sub-link.active { background: var(--color-accent); color: #fff; }
.nav-sub-link .dot { width: 5px; height: 5px; border-radius: 50%; background: rgba(32,30,29,.3); flex: none; }
.nav-sub-link.active .dot { background: #fff; }
.nav-sub-link .badge-mini { margin-left: auto; font-size: 10px; font-weight: 700; padding: 1px 7px; border-radius: 99px; background: rgba(32,30,29,.08); color: var(--color-text-muted); }

.sidebar-widget { border-radius: var(--radius-lg); background: rgba(32,30,29,.045); padding: 12px 13px; margin-bottom: 10px; }
.sidebar-widget .lbl { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--color-text-muted); }
.sidebar-widget .val { font-family: var(--font-heading); font-weight: 800; font-size: 22px; letter-spacing: -.03em; }
.sidebar-widget .bar { height: 6px; border-radius: 99px; background: rgba(32,30,29,.12); margin-top: 8px; overflow: hidden; }
.sidebar-widget .bar-fill { height: 100%; border-radius: 99px; background: linear-gradient(90deg, #ff563c, var(--color-accent)); }

.profile-btn { display: flex; align-items: center; gap: 9px; padding: 7px 6px; border-radius: var(--radius-md); background: none; border: none; cursor: pointer; width: 100%; text-align: left; }
.profile-btn:hover { background: rgba(var(--color-accent-rgb),.07); }
.avatar-circle { width: 33px; height: 33px; border-radius: 50%; background: var(--color-text); color: #fff; display: flex; align-items: center; justify-content: center; font-family: var(--font-heading); font-weight: 800; font-size: 11.5px; flex: none; }
.profile-btn .pname { font-family: var(--font-heading); font-weight: 800; font-size: 12.5px; }
.profile-btn .pmeta { font-size: 10.5px; color: var(--color-text-muted); }

.main-col { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.main-area { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; gap: 14px; position: relative; z-index: 1; overflow: hidden; }

/* Mobile top bar + hamburger — hidden on desktop, shown only under the phone
   breakpoint below. Lives OUTSIDE #app-content (which the SPA router replaces
   on every navigation) so it survives route changes. */
#mobile-topbar { display: none; align-items: center; gap: 10px; flex: none; }
#mobile-topbar .logo {
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; flex: none; overflow: hidden; box-sizing: border-box;
  border-radius: var(--radius-xs); background: linear-gradient(140deg, var(--color-accent-light), var(--color-accent)); padding: 6px;
}
#mobile-topbar .logo img { width: 100%; height: 100%; object-fit: contain; }
#mobile-topbar .name { font-family: var(--font-heading); font-weight: 800; font-size: 14px; }
#mobile-menu-btn {
  width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--color-divider-strong); background: var(--glass-bg);
  display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--color-text); flex: none;
}
#mobile-menu-btn .ic-close { display: none; }
#mobile-menu-btn.is-open .ic-burger { display: none; }
#mobile-menu-btn.is-open .ic-close { display: block; }
#mobile-nav-backdrop {
  display: none; position: fixed; inset: 0; background: rgba(20,18,17,.45); z-index: 190;
  opacity: 0; pointer-events: none; transition: opacity .2s ease;
}
#mobile-nav-backdrop.active { display: block; opacity: 1; pointer-events: auto; }
.page-panel { background: var(--glass-bg); border: 1px solid var(--glass-border); box-shadow: var(--shadow-md), inset 0 1px 0 rgba(255, 255, 255, .7); border-radius: var(--radius-xl); }

.page-header { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.page-title { font-size: 24px; letter-spacing: -.03em; }
.page-sub { font-size: 12px; color: var(--color-text-muted); }
.search-box {
  display: flex; align-items: center; gap: 9px; border-radius: var(--radius-md); padding: 0 6px 0 14px;
  background: var(--glass-bg); border: 1px solid var(--glass-border); box-shadow: var(--shadow-sm);
}
.search-box input { flex: 1; min-width: 0; border: 0; outline: none; background: transparent; padding: 11px 0; font-size: 13px; color: inherit; }
.search-box svg { flex: none; color: rgba(32,30,29,.45); }

/* ==========================================================================
   Modal / Dialog
   ========================================================================== */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(32,30,29,.5);
  display: flex; align-items: center; justify-content: center; z-index: 200; padding: 20px;
}
.modal-box {
  /* max-height:100% (BUKAN 88vh) — .modal-backdrop pembungkusnya sudah
     position:fixed;inset:0 (mengisi viewport TANPA lewat vh/vw, jadi tetap
     akurat di bawah zoom) + padding:20px sebagai jarak napas, jadi cukup
     100% relatif ke situ — lihat gotcha vh-vs-zoom di .app-shell atas. */
  width: 100%; max-width: 560px; max-height: 100%; display: flex; flex-direction: column;
  background: rgba(255,255,255,.97);
  border: 1px solid rgba(255,255,255,.92); box-shadow: var(--shadow-xl), inset 0 1px 0 rgba(255, 255, 255, .7); border-radius: var(--radius-2xl); overflow: hidden;
  animation: gpModalIn .18s ease-out;
}
/* When a modal's content is wrapped in a real <form> (so Enter-to-submit works),
   the form must transparently continue the head/body/foot flex layout. */
.modal-box > form { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.modal-box.modal-lg { max-width: 760px; }
.modal-box.modal-xl { max-width: 960px; }
/* Hampir memenuhi seluruh viewport (bukan cuma max-width lebih besar) — dipakai
   modal yang isinya tabel lebar/padat (mis. Ajukan Permintaan Pembelian) yang
   butuh ruang jauh lebih besar dari .modal-xl. height:100% (bukan cuma
   max-height, seperti default) supaya box SELALU setinggi area tersedia,
   bukan cuma menyusut sesuai konten. */
.modal-box.modal-full { max-width: 100%; width: 100%; height: 100%; }
.modal-head { padding: 18px 22px; display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; border-bottom: 1px solid rgba(32,30,29,.09); flex: none; }
.modal-head .kicker { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--color-text-muted); }
.modal-head .title { font-size: 21px; letter-spacing: -.03em; }
.modal-body { padding: 20px 22px; overflow-y: auto; flex: 1; min-height: 0; }
.modal-foot { padding: 14px 22px 18px; border-top: 1px solid rgba(32,30,29,.09); display: flex; align-items: center; gap: 10px; flex: none; flex-wrap: wrap; }
.modal-foot .text-faint, .modal-foot .text-muted { flex: 1 1 120px; min-width: 0; }
@keyframes gpModalIn { 0% { opacity: 0; transform: translateY(10px) scale(.98); } 100% { opacity: 1; transform: none; } }

/* Grup akordeon generik — dipakai `GP.permMatrix.render()` (matriks Kelola
   Hak Akses, role.js/karyawan.js) buat mengelompokkan modul permission
   SEPERTI struktur grup sidebar. Visualnya mirip `.bantuan-section` tapi
   SENGAJA class terpisah (bukan reuse) — nama "bantuan-*" cuma pantas
   dipakai di halaman Bantuan itu sendiri, komponen generik ini dipakai di
   konteks lain. */
.perm-group { border: 1px solid var(--color-divider); border-radius: var(--radius-lg); margin-bottom: 10px; overflow: hidden; }
.perm-group-head {
  all: unset; box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 10px 14px; cursor: pointer; font-family: var(--font-heading); font-weight: 700; font-size: 12.5px;
  background: rgba(32,30,29,.02);
}
.perm-group-head:hover { background: rgba(32,30,29,.04); }
.perm-group-chevron { flex: none; color: var(--color-text-muted); transition: transform .18s ease; }
.perm-group.open .perm-group-chevron { transform: rotate(180deg); }
.perm-group-body { display: none; }
.perm-group.open .perm-group-body { display: block; }
.perm-group-body .table { margin: 0; border-radius: 0; }

/* Dropdown "Lainnya" — menu generik buat toolbar/footer yang tombolnya
   kebanyakan berjejer sampai overflow (mis. footer modal detail Work
   Order — Cetak WO/Cetak Klaim Garansi/Batalkan, tombol yang JARANG
   diklik). Kontrak markup: <div class="action-menu-wrap"><button class=
   "btn btn-secondary">Lainnya <svg chevron></button><div class=
   "action-menu" hidden><button class="action-menu-item">...</button>...
   </div></div> — toggle `hidden` via JS, tutup kalau klik di luar wrapper
   atau item-nya sendiri diklik. Popover dibuka KE ATAS (`bottom:100%`,
   bukan `top:100%`) karena SATU-SATUNYA pemakaian saat ini ada di footer
   MODAL (dekat tepi bawah) — kalau nanti dipakai di toolbar bagian ATAS
   halaman, buka `top:100%` sebagai gantinya. */
.action-menu-wrap { position: relative; display: inline-flex; }
.action-menu {
  position: absolute; left: 0; bottom: calc(100% + 6px); z-index: 40; min-width: 210px; padding: 6px;
  background: #fff; border: 1px solid var(--color-divider-strong); border-radius: var(--radius-md); box-shadow: var(--shadow-md);
}
.action-menu-item {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 9px 10px; border-radius: var(--radius-sm);
  border: none; background: transparent; cursor: pointer; text-align: left; font-size: 12.5px; font-weight: 700;
  color: var(--color-text); white-space: nowrap;
}
.action-menu-item:hover:not(:disabled) { background: rgba(32,30,29,.06); }
.action-menu-item:disabled { opacity: .45; cursor: not-allowed; }
/* .danger dipisah dari .btn-danger — TETAP var(--color-danger) (bukan
   --color-accent), sama alasan gotcha lama .btn-danger: aksi destruktif
   selalu merah lintas tema, jangan ikut warna aksen. */
.action-menu-item.danger { color: var(--color-danger); }
.action-menu-item.danger:hover:not(:disabled) { background: var(--color-danger-soft); }

.confirm-box { max-width: 420px; }

/* ==========================================================================
   Top route-loading progress bar
   ========================================================================== */
#gp-progress-bar {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%;
  background: var(--color-accent); box-shadow: 0 0 8px rgba(var(--color-accent-rgb),.6);
  z-index: 999; opacity: 0; transition: width .25s ease, opacity .3s ease;
  pointer-events: none;
}

/* ==========================================================================
   Toast
   ========================================================================== */
#toast-stack { position: fixed; right: 20px; bottom: 20px; display: flex; flex-direction: column; gap: 8px; z-index: 500; }
/* Varian posisi kanan-ATAS — dipasang/dilepas per-modul via toggle class
   (bukan ganti posisi default global), lihat kasir.js. Dipakai pertama kali
   di Kasir karena toast bottom-right menutupi tombol Diskon/Bayar yang
   memang ditaruh di pojok kanan-bawah panel keranjang. */
#toast-stack.toast-stack-top { top: 20px; bottom: auto; flex-direction: column; }
.toast {
  display: flex; align-items: center; gap: 10px; padding: 12px 15px; border-radius: var(--radius-lg);
  background: rgba(32,30,29,.94); color: #fff; box-shadow: var(--shadow-lg); min-width: 260px; max-width: 360px;
  animation: gpToastIn .18s ease-out;
}
#toast-stack.toast-stack-top .toast { animation: gpToastInTop .18s ease-out; }
.toast.toast-danger { background: rgba(174, 24, 0, .95); }
.toast .toast-icon { width: 24px; height: 24px; border-radius: 8px; background: var(--color-accent); display: flex; align-items: center; justify-content: center; flex: none; }
.toast-danger .toast-icon { background: rgba(255, 255, 255, .22); }
.toast .toast-title { font-family: var(--font-heading); font-weight: 800; font-size: 12.5px; }
.toast .toast-note { font-size: 11px; color: rgba(255,255,255,.65); margin-left: 6px; }
@keyframes gpToastIn { 0% { opacity: 0; transform: translateY(10px); } 100% { opacity: 1; transform: none; } }
@keyframes gpToastInTop { 0% { opacity: 0; transform: translateY(-10px); } 100% { opacity: 1; transform: none; } }

/* ==========================================================================
   Misc utility
   ========================================================================== */
.flex { display: flex; }
.flex-1 { flex: 1; min-width: 0; }
.row { display: flex; align-items: center; }
.gap-4 { gap: 4px; } .gap-6 { gap: 6px; } .gap-8 { gap: 8px; } .gap-10 { gap: 10px; } .gap-12 { gap: 12px; } .gap-14 { gap: 14px; }
.mt-8 { margin-top: 8px; } .mt-12 { margin-top: 12px; } .mt-16 { margin-top: 16px; } .mt-20 { margin-top: 20px; }
.mb-8 { margin-bottom: 8px; } .mb-12 { margin-bottom: 12px; }
.ml-auto { margin-left: auto; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
/* Grid item default min-width:auto TIDAK menyusut di bawah lebar konten
   intrinsiknya (beda dari flex yang sudah lazim tahu butuh min-width:0) —
   kalau kontennya panjang (mis. baris "Nama mekanik · 3 WO aktif · Bay 3"
   di panel Beban Mekanik dashboard), kolom grid ikut melebar paksa dan
   nabrak kolom sebelahnya. WAJIB min-width:0 di SEMUA child langsung. */
.grid-2 > *, .grid-3 > * { min-width: 0; }
.grid-4 { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 12px; }

/* Layout modal "konten utama + sidebar detail" (mis. detail Kendaraan +
   panel Pemilik) — lebar sidebar WAJIB lewat class ini (bukan inline style)
   supaya breakpoint mobile bisa nimpa jadi full-width & stack ke bawah,
   lihat gotcha inline-width di CLAUDE.md (pola yang sama kayak .pf-identity-card). */
.modal-split { display: flex; gap: 14px; align-items: flex-start; }
.modal-split > .aside { width: 230px; flex: none; }
.divider { height: 1px; background: var(--color-divider); margin: 14px 0; }
.scroll-y { overflow-y: auto; }
/* Modifier generik — tempel ke elemen manapun yang SUDAH `overflow-x:auto`
   (inline atau class lain) buat sembunyikan scrollbar visualnya TANPA
   mematikan scroll-nya sendiri (masih bisa digeser/swipe) — pola sama
   `.bay-strip`/`.chip-group-scroll`, cuma diekstrak jadi utility supaya
   dipakai ulang di elemen satu-off (mis. `#poa-flow`, strip step Draf/
   Diterbitkan/dst di "Buat PO", ketahuan 2026-09-17) tanpa perlu bikin
   class khusus per halaman. */
.scroll-x-clean { scrollbar-width: none; -ms-overflow-style: none; }
.scroll-x-clean::-webkit-scrollbar { display: none; }
/* Anak langsung `.pill-btn`/`.tab-btn` WAJIB `flex:none` — tanpa ini,
   `flex-shrink:1` default mereka bikin TOMBOLNYA SENDIRI yang menyusut
   (teksnya patah jadi 2-3 baris, mis. "Paling dekat tempo" filter Kendaraan)
   begitu wrapper-nya (`overflow-x:auto`) kehabisan ruang, alih-alih wrapper
   yang overflow/discroll seperti maksudnya. Ketahuan 2026-09-17. */
.scroll-x-clean > .pill-btn, .scroll-x-clean > .tab-btn { flex: none; }
.empty-state { padding: 30px 18px; text-align: center; color: var(--color-text-muted); font-size: 12.5px; border: 1px dashed rgba(32,30,29,.2); border-radius: var(--radius-md); }
.spinner { width: 22px; height: 22px; border-radius: 50%; border: 3px solid rgba(32,30,29,.15); border-top-color: var(--color-accent); animation: gpSpin .7s linear infinite; margin: 20px auto; }
.spinner-inline { width: 14px; height: 14px; flex: none; border-radius: 50%; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; animation: gpSpin .6s linear infinite; display: inline-block; vertical-align: -2px; }
.btn-secondary .spinner-inline, .btn-ghost .spinner-inline { border-color: rgba(32,30,29,.25); border-top-color: var(--color-text); }
@keyframes gpSpin { to { transform: rotate(360deg); } }
.skeleton { background: rgba(32,30,29,.08); border-radius: 8px; animation: gpPulse 1.2s ease-in-out infinite; }
@keyframes gpPulse { 0%,100% { opacity: 1; } 50% { opacity: .5; } }

/* Loading route placeholder */
.route-loading { padding: 60px 0; display: flex; justify-content: center; }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 1100px) {
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .grid-3, .grid-4 { grid-template-columns: 1fr 1fr; }
}

/* Tablet/phone: sidebar becomes an off-canvas drawer instead of a column
   pushed above the content — a full-width nav stacked above the page would
   bury every module below a huge menu on a small screen. */
@media (max-width: 860px) {
  .app-shell { padding: 8px; gap: 0; }
  #mobile-topbar { display: flex; padding: 6px 4px 10px; }
  .sidebar {
    position: fixed; top: 8px; left: 8px; bottom: 8px; width: 260px; max-width: calc(100vw - 16px);
    z-index: 200; transform: translateX(calc(-100% - 8px)); max-height: none;
    transition: transform .22s ease;
  }
  .sidebar.mobile-open { transform: translateX(0); }
  .sidebar-collapse-btn { display: none !important; }

  /* Tombol aksi per-baris (edit/hapus) di tabel modul list DESKTOP sengaja
     `opacity:0`, cuma muncul saat `tr:hover` — perangkat sentuh TIDAK PUNYA
     hover state yang bisa diandalkan, jadi tombolnya nyaris tidak kelihatan/
     tidak bisa disentuh di HP. Tampilkan permanen di breakpoint ini. */
  .table .row-actions { opacity: 1; }

  /* Modul list (Karyawan, Cabang, dst) di desktop sengaja "pin" header+KPI lalu
     scroll INTERNAL cuma di dalam tabel (`.table-wrap.scroll-y`, flex:1) supaya
     header tidak ikut hilang saat scroll — di HP pola itu bikin tabel kepepet ke
     kotak kecil (apalagi layar pendek/landscape), datanya nyaris tidak kelihatan.
     Di breakpoint ini: matikan scroll internal tabel, biarkan tabel MEMANJANG
     natural ikut kontennya, dan biarkan SELURUH halaman (`.main-area`) yang
     scroll sebagai satu kesatuan — pola "page scroll" biasa, bukan "panel
     scroll". `!important` dipakai karena `flex:1`/inline style ada di tiap view. */
  .main-area { overflow-y: auto; overflow-x: hidden; }
  .table-wrap.scroll-y { overflow-y: visible !important; max-height: none !important; flex: none !important; }
  /* Melepas tinggi tabel saja TIDAK CUKUP — `.page-panel` PEMBUNGKUSNYA juga
     `flex:1;min-height:0` (inline, di 24 view list) supaya scroll internal di
     atas bekerja. `min-height:0` menghapus proteksi ukuran-minimum-sesuai-konten
     bawaan flex item, jadi panelnya ikut DIPAKSA mengecil ke alokasi flex-nya
     (bukan ke tinggi kontennya) walau tabel di dalamnya sudah lebih tinggi —
     hasilnya kartu panel terlihat "terpotong" (rounded corner-nya berhenti di
     tengah tabel) sementara isi tabel meluber visible ke luar kartu. Scoped
     pakai `:has()` cuma ke page-panel yang punya `.table-wrap.scroll-y` di
     dalamnya (descendant, BUKAN cuma anak langsung — Booking membungkus
     tabelnya satu level lagi di `#bk-list-view` karena ada tab List/Kalender,
     2026-09-17) — supaya TIDAK ikut kena `.page-panel` Kasir (isinya
     `.catalog-grid.scroll-y`, BUKAN tabel, sengaja TETAP fixed-height di HP). */
  .page-panel:has(.table-wrap.scroll-y) { flex: none !important; }

  /* Work Order Baru (`workorder-baru.js`, halaman PENUH bukan modal) — layout
     2 kolom (form kiri `.scroll-y` + ringkasan kanan `<aside class="pos-cart">
     width:340px`) SENGAJA row+stretch di desktop, TIDAK PERNAH ada breakpoint
     yang menumpuknya — di HP `.pos-cart` 340px fixed nyaris menghabiskan
     seluruh lebar 375px, mendesak kolom form jadi sliver tipis (mirip bug
     `.modal-split` Estimasi, TAPI di sini akar masalahnya width FIXED, bukan
     grid/flex min-content). `!important` WAJIB buat menang lawan inline style
     `width:340px`/`align-items:stretch` di JS. Sama seperti gotcha
     `.table-wrap.scroll-y` di atas — SETELAH ditumpuk vertikal, `.scroll-y`
     kiri (inline flex:1;min-height:0;overflow-y:auto) juga WAJIB dilepas dari
     scroll-INTERNAL-nya sendiri (discoped ke `.wob-layout > .scroll-y` SAJA,
     BUKAN `.scroll-y` global — class itu dipakai banyak halaman lain sebagai
     satu-satunya area scroll utama mereka) supaya `.main-area` yang jadi satu
     scroll gabungan, bukan 2 kotak scroll pendek bertumpuk. */
  .wob-layout { flex-direction: column !important; flex: none !important; min-height: 0 !important; }
  .wob-layout > .scroll-y { overflow-y: visible !important; flex: none !important; min-height: 0 !important; }
  .wob-aside { width: 100% !important; }

  /* Panel "Ukuran Tampilan" (Pengaturan > Aplikasi) SENGAJA cuma fitur
     komputer/layar besar — disembunyikan total di breakpoint ini, lihat
     GP.syncZoom() di app.js yang juga tidak PERNAH menerapkan zoom di
     lebar ini sama sekali (dua lapis: CSS sembunyikan kontrolnya, JS tidak
     menerapkan efeknya — konsisten walau kontrolnya dipaksa terlihat lewat
     devtools). */
  #ap-zoom-panel { display: none; }

  /* Badge shortcut keyboard (F2/F3/F4/F8/F9 dst) TIDAK RELEVAN di perangkat
     sentuh (tidak ada tombol F-key fisik) — disembunyikan total, sama
     filosofinya dengan panel "Ukuran Tampilan" di atas. */
  .kbd-badge { display: none; }

  /* KPI opt-in scroll horizontal (BUKAN default `.kpi-grid`, yang tetap 2x2
     ditumpuk — dipakai modul list biasa yang halamannya boleh SELURUHNYA
     scroll, lihat gotcha `.main-area` di atas). Awalnya cuma buat halaman
     papan kanban (`#wb-kpi` Work Order + `#pb-kpi` Papan Bay — `.wo-board`/
     `.bay-board` flex:1;min-height:0 TIDAK ikut pola page-scroll, KPI 2 baris
     makan jatah tinggi papan, kolom kanban jadi kebagian ruang pendek),
     permintaan user 2026-09-17. Diperluas ke halaman list biasa (Reminder
     Servis, hari yang sama) murni preferensi tampilan (kartu KPI lebih besar
     & mudah dibaca satu baris) — halaman list TIDAK PUNYA masalah tinggi
     papan seperti kanban, tapi user tetap mau pola ini. Class kedua
     `.kpi-scroll-x` opt-in per halaman, TIDAK ubah default `.kpi-grid`. */
  .kpi-scroll-x {
    /* `flex:none` WAJIB di sini (List Work Order, ketahuan 2026-09-17) — tanpa
       ini, di halaman yang sibling-nya `flex-shrink:0` (mis. `.page-panel:
       has(.table-wrap.scroll-y)` di atas, sengaja menolak menyusut supaya
       tabel panjang render penuh & `.main-area` yang scroll), `#wl-kpi` JADI
       satu-satunya item yang "boleh" menyusut. `overflow-x:auto` di sini
       DIAM-DIAM meniadakan proteksi min-content bawaan flex item (spec: auto
       min-size di main-axis jatuh ke 0 kalau overflow relevan-nya BUKAN
       `visible`) — flexbox lalu bebas meremas tingginya ke ~0px sebelum
       akhirnya `.main-area` discroll, kartu KPI-nya lenyap total. */
    display: flex; flex: none; grid-template-columns: none; overflow-x: auto; gap: 10px;
    padding-bottom: 2px; scrollbar-width: none; -ms-overflow-style: none;
  }
  .kpi-scroll-x::-webkit-scrollbar { display: none; }
  /* `width:156px` FIXED sempat dipakai, tapi nilai rupiah panjang (mis. "Rp
     21.535.815") di font-size 24px NABRAK lebar kartu & keluar bleeding ke
     kartu tetangga — coba diperbaiki dulu dengan mengecilkan font + patah
     baris paksa, TAPI hasilnya angka jadi 3 baris kependekan, susah dibaca.
     Ganti jadi `width:auto` (permintaan user 2026-09-17) — kartu di strip
     horizontal BEBAS melebar sesuai kontennya sendiri (beda dari `.kpi-grid`
     grid biasa yang kartunya HARUS sama rata & boleh menyusut), jadi angka
     SELALU satu baris utuh, tidak pernah terpotong/wrap. `white-space:nowrap`
     WAJIB di value+note (default browser boleh wrap di spasi "Rp 21.535.815"
     jadi 2 baris walau card-nya sendiri sudah auto-width) supaya benar-benar
     satu baris. `overflow:hidden` tetap dipertahankan sebagai jaring
     pengaman terakhir kalau suatu saat ada angka ekstrem panjangnya. */
  .kpi-scroll-x .kpi-card { flex: none; width: auto; min-width: 140px; overflow: hidden; }
  .kpi-scroll-x .kpi-value, .kpi-scroll-x .kpi-note, .kpi-scroll-x .kpi-label { white-space: nowrap; }
}

/* Phone: narrower still — collapse multi-column layouts to a single column
   and let the Kasir POS 3-zone layout stack instead of sitting side by side. */
@media (max-width: 560px) {
  .kpi-grid { grid-template-columns: 1fr 1fr; }
  /* `minmax(0,1fr)` (BUKAN `1fr` polos) — track grid `1fr` diam-diam
     implicit `minmax(auto,1fr)`, jadi TIDAK bisa menyusut di bawah
     min-content anak-anaknya (mis. `.input-group` yang salah satu child-nya
     `flex-shrink:0`, lihat catatan di bawah) — track ikut meluber keluar
     kartu walau cuma 1 kolom. */
  .grid-2, .grid-3, .field-row.cols-2, .field-row.cols-3, .field-row.cols-3-lead { grid-template-columns: minmax(0,1fr); }
  /* .grid-4 SENGAJA tetap 2 kolom di sini (bukan ikut collapse ke 1 kolom
     kayak .grid-2/.grid-3) — 4 item pas dibagi rata jadi kotak 2x2, lebih
     ringkas daripada ditumpuk 4 baris penuh di layar sempit. */
  .grid-4 { grid-template-columns: 1fr 1fr; }
  /* Modal split (mis. detail Kendaraan: konten kiri + panel Pemilik kanan)
     ditumpuk vertikal di HP, panel kanan jadi full-width di bawah konten.
     `align-items:flex-start` (desktop, row) sengaja TIDAK stretch supaya
     kolom kiri/kanan boleh beda tinggi — begitu `flex-direction` dibalik ke
     `column`, cross-axis-nya jadi HORIZONTAL, jadi `flex-start` yang sama
     malah bikin tiap kolom cuma selebar max-content-nya sendiri (TIDAK
     dibatasi lebar container), meluber ke kanan (overflow-x, kepotong kayak
     tabel Booking di atas tapi beda akar masalah). WAJIB `stretch` di sini
     supaya kedua kolom ikut lebar penuh container saat ditumpuk.
     `!important` di KEDUA baris WAJIB (ketahuan 2026-09-17 di halaman penuh
     "Buat PO", beda dari modal Estimasi yang aman tanpanya) — beberapa
     pemakai `.modal-split` nge-inline `align-items`/`.aside{width:340px}`
     SENDIRI di markup-nya (bukan cuma andalkan default class), dan inline
     style SELALU menang atas rule stylesheet manapun tanpa `!important`,
     media query ini jadi tidak pernah kepakai buat halaman itu. */
  .modal-split { flex-direction: column !important; align-items: stretch !important; }
  .modal-split > .aside { width: 100% !important; }
  /* `flex-shrink:0` dari rule desktop (`.input-group input:first-child { flex:
     0 0 36% }`) ikut kebawa ke sini kalau cuma override `flex-basis` — input
     pertama jadi TIDAK BISA menyusut sama sekali, bikin `.input-group`
     (mis. "Merek & model") meluber keluar kartu di layar sempit. WAJIB set
     ulang `flex-shrink:1` juga (bukan cuma basis) di breakpoint ini. */
  .input-group input:first-child { flex: 1 1 40%; }
  .page-title { font-size: 20px; }
  .modal-backdrop { padding: 10px; }
  .modal-head, .modal-body, .modal-foot { padding-left: 16px; padding-right: 16px; }
  /* Footer modal (Batal | pesan | Simpan, 17 file _edit.php pakai pola ini)
     ketat banget di layar sempit — pesan kepepet jadi setipis mungkin dan
     tombol Simpan hampir kepotong tepi. Pindahkan pesan ke baris sendiri di
     ATAS (order:-1 + lebar penuh), lalu Batal & Simpan sejajar sama lebar
     di baris bawahnya (bukan rata kanan-kiri lagi, ml-auto dinetralkan). */
  .modal-foot .ml-auto { margin-left: 0; }
  .modal-foot .text-faint, .modal-foot .text-muted { order: -1; flex-basis: 100%; margin-bottom: 2px; }
  .modal-foot .btn { flex: 1; }
  .pagination-bar { justify-content: center; }
  .pagination-bar .pagination-info-row { justify-content: center; }
  .pagination-bar .pagination { justify-content: center; width: 100%; }
  /* Kasir/login-specific overrides live in modules.css instead of here — that
     stylesheet loads AFTER this one, so an unconditional rule there (e.g.
     .method-grid's base 2-column definition) would win the cascade over an
     equal-specificity rule placed in this file, media query or not. */
}

/* ------------------------------------------------------------------
   Chart ringan (SVG tangan, tanpa library) — dipakai Analisis Tren
   (laporan-tren.js). Bar/line dibangun langsung sebagai elemen SVG dari
   JS, CSS di sini cuma urus chrome di sekitarnya (judul, wrapper scroll,
   tooltip hover) — bukan style bar/line itu sendiri (warnanya dikirim
   inline per elemen SVG, supaya diverging chart bisa beda warna per-bar
   tanpa perlu class tambahan).
   ------------------------------------------------------------------ */
.chart-title { font-family: var(--font-heading); font-weight: 800; font-size: 14px; }
/* Chart bisa lebih lebar dari panel (mis. rentang 24 bulan) — scroll
   horizontal DI DALAM kontainernya sendiri, jangan sampai halaman ikut
   melebar (aturan baku "wide content scrolls in its own container"). */
.chart-scroll { overflow-x: auto; }
.chart-mark { cursor: pointer; transition: opacity .12s; }
.chart-mark:hover, .chart-mark:focus { opacity: .8; outline: none; }
.chart-tip {
  position: fixed; z-index: 500; pointer-events: none;
  background: #201e1d; color: #fff; border-radius: 9px; padding: 7px 11px;
  font-size: 12px; line-height: 1.4; box-shadow: 0 8px 20px rgba(0,0,0,.22);
  opacity: 0; transform: translate(-50%, -100%); transition: opacity .1s;
  white-space: nowrap;
}
.chart-tip.show { opacity: 1; }
.chart-tip-label { color: rgba(255,255,255,.65); margin-bottom: 2px; }
/* Satu baris per series di dalam tooltip (dipakai chart 1-series MAUPUN
   chart perbandingan 2-series — bedanya cuma jumlah baris & apakah dot+nama
   series ditampilkan, lihat setTipContent() di laporan-tren.js). */
.chart-tip-row { display: flex; align-items: center; margin-top: 2px; }
.chart-tip-value { font-weight: 800; font-size: 13px; }
.chart-tip-dot { width: 8px; height: 8px; border-radius: 2px; flex: none; display: inline-block; margin-right: 6px; }
.chart-tip-rowlabel { color: rgba(255,255,255,.65); margin-left: 6px; }
.chart-empty { padding: 30px 0; text-align: center; color: var(--color-text-muted); font-size: 12.5px; }
/* Legend chart 2-series (mis. Omzet vs Transaksi diindeks) — "legend
   SELALU ada untuk >=2 series", pola sama dipakai kalau nanti ada chart
   multi-series lain. Series yang kebetulan tidak punya data pada rentang
   terpilih tetap ditampilkan di legend TAPI dipudarkan + dikasih catatan
   "(belum ada data)" — biar user tahu itu memang kosong, bukan bug. */
.chart-legend { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 10px; }
.chart-legend-item { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--color-text-muted); }
.chart-legend-item.is-muted { opacity: .55; }
.chart-legend-item .dot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; flex: none; }

/* Tombol "Cara membaca" + isi bantuan (GP.showHelp) */
.help-btn { display: inline-flex; align-items: center; gap: 6px; margin-left: 10px; padding: 4px 11px 4px 8px; border-radius: 999px; border: 1px solid var(--color-divider-strong); background: rgba(255,255,255,.55); color: var(--color-text-muted); font-size: 11.5px; font-weight: 700; cursor: pointer; vertical-align: middle; line-height: 1.2; }
.help-btn:hover { color: var(--color-accent); border-color: var(--color-accent); background: #fff; }
.help-body { font-size: 13px; line-height: 1.65; color: var(--color-text); }
.help-body h5 { font-family: var(--font-heading); font-weight: 800; font-size: 13px; margin: 18px 0 6px; }
.help-body h5:first-child { margin-top: 0; }
.help-body p { margin: 0 0 8px; }
.help-body ul { margin: 0 0 8px; padding-left: 20px; }
.help-body li { margin-bottom: 5px; }
.help-body .help-note { background: rgba(32,30,29,.05); border-radius: 12px; padding: 10px 14px; margin: 10px 0; font-size: 12.5px; }
.help-body .help-ex { background: var(--color-accent-soft, rgba(236,48,19,.07)); border-radius: 12px; padding: 10px 14px; margin: 10px 0; font-size: 12.5px; }

/* Kit grafik bersama (public/assets/js/charts.js) */
.gc-chart { min-width: 0; }
.gc-legend { margin-bottom: 6px; }
.gc-donut { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.gc-donut-list { flex: 1; min-width: 190px; display: flex; flex-direction: column; gap: 7px; }
.gc-donut-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; }
.gc-dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.gc-donut-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gc-donut-val { font-weight: 700; white-space: nowrap; }
.gc-donut-pct { width: 46px; text-align: right; color: var(--color-text-muted); font-size: 11.5px; }
.gc-heat-wrap { overflow-x: auto; }
.gc-heat { border-collapse: separate; border-spacing: 3px; width: 100%; font-size: 11px; table-layout: fixed; }
.gc-heat th { font-weight: 600; color: var(--color-text-muted); font-size: 11px; text-align: center; padding: 2px 0; }
.gc-heat tbody th { text-align: right; padding-right: 6px; width: 62px; white-space: nowrap; }
.gc-heat td { height: 28px; text-align: center; border-radius: 5px; background: rgba(32,30,29,.05); font-weight: 600; }
.gc-heat td.gc-heat-off { background: repeating-linear-gradient(45deg, rgba(32,30,29,.04), rgba(32,30,29,.04) 4px, transparent 4px, transparent 8px); }

/* Tautan sekunder di navigasi publik disembunyikan di layar kecil (tombol yang sama ada di hero). */
@media (max-width: 640px) { .pub-hide-sm { display: none !important; } }
