/* =============================================================================
   app.css — satu-satunya stylesheet aplikasi Replenishment
   =============================================================================

   Menggantikan 18 blok <style> yang tersebar di sembilan modul Python.
   Mengubah tampilan berarti menyunting file ini, bukan delapan belas tempat.

   Urutan berkas:
     1. Token         — warna, jarak, radius, bayangan; terang dan gelap
     2. Dasar         — reset, tipografi, elemen mentah
     3. Kerangka      — nav, halaman, header
     4. Komponen      — tombol, penanda, isian, kartu, panel
     5. Tabel         — termasuk kelompok kolom dan kolom beku
     6. Responsif     — tablet, HP
     7. Cetak         — selalu terang, selalu A4

   TEMA
   Nilai terang hidup di :root. Tema gelap ditimpa di dua tempat:
     - @media (prefers-color-scheme: dark) untuk pilihan "ikut sistem"
     - :root[data-theme="dark"] untuk pilihan eksplisit
   Atribut data-theme dirender server dari users.theme, jadi tidak ada
   kedipan tema saat halaman dimuat — berbeda dari penyimpanan di browser.

   FONT
   Sengaja memakai font sistem. Aplikasi ini dilayani di LAN dan Tailscale;
   Google Fonts gagal saat internet kantor putus, dan halaman yang hurufnya
   berubah-ubah tergantung koneksi lebih buruk daripada huruf sistem yang
   selalu ada. Untuk menaikkannya, lihat blok @font-face di bawah.
   ============================================================================= */


/* ---------------------------------------------------------------------------
   1. TOKEN
   --------------------------------------------------------------------------- */

:root {
  /* Permukaan */
  --bg:            #fbfbfa;
  --surface:       #ffffff;
  --surface-sunk:  #f6f5f3;
  --surface-quiet: #fcfbf8;

  /* Teks */
  --ink:      #14161a;
  --text:     #4a4844;
  --muted:    #6f6d68;
  --disabled: #a3a19c;

  /* Garis */
  --line:         #e6e4e0;
  --line-row:     #eeece8;
  --line-control: #d3d0ca;
  --line-input:   #b5b2ac;

  /* Semantik — arti tidak berubah dari aplikasi lama, hanya nilainya */
  --accent:       #1b3a5c;   /* dulu #007bff */
  --accent-hover: #12283f;
  --accent-soft:  #eef2f7;
  --accent-line:  #d5dfeb;

  --amber:       #a86a00;    /* dulu #d9a406 — di bawah Reorder Point */
  --amber-soft:  #fdf6e7;
  --amber-line:  #f0e3c4;

  --danger:      #9b2c2c;    /* dulu #d9534f — data bermasalah, kritis */
  --danger-soft: #fdf9f9;
  --danger-line: #e8dcdc;

  --success:      #2f6b4f;
  --success-soft: #e9f0ea;

  /* Nav mengikuti tema. Dulu ia gelap di kedua tema, dan itulah yang membuatnya
     satu-satunya kaca yang berdiri di atas substrat lebih terang dari dirinya --
     di situ 52% menjatuhkan teks nav redup ke 1,57:1 dan menunya tidak terbaca.
     Yang salah bukan angkanya, materialnya: kaca terang di atas halaman terang
     boleh tembus 52% dan tetap terbaca, karena teks di atasnya gelap. */
  --nav-bg:        #ffffff;
  --nav-text:      #14161a;
  /* Dinaikkan dari #6f6d68. Di substrat paling bermusuhan yang bisa dibangun --
     tombol hijau, chip amber, chip merah, chip biru berdempetan tepat di
     belakang bar -- #6f6d68 memberi 4,04:1 dan yang ini 5,08:1. Diambil 5,0 dan
     bukan 4,5 karena isi nyata berubah-ubah dan tidak akan diukur ulang tiap
     kali halaman baru lahir.

     Redup tidak perlu gagal untuk terlihat redup: yang aktif tetap 2,79 kali
     lebih tinggi, dan bedanya juga dibawa bobot, pill, dan aria-current. */
  --nav-text-dim:  #5f5e59;
  --nav-active:    rgba(20, 22, 26, 0.06);

  /* Bentuk */
  --r-sm: 3px;
  --r:    5px;
  --r-lg: 6px;

  --shadow-pop: 0 1px 2px rgba(20, 22, 26, 0.05), 0 8px 24px rgba(20, 22, 26, 0.07);
  --shadow-seg: 0 1px 2px rgba(20, 22, 26, 0.10);

  /* Translusensi — hanya untuk chrome yang mengambang, tidak pernah untuk data.
     Kaca Varian B (K27): lebih tembus dan lebih kuat blurnya daripada
     sebelumnya, karena ia sekarang punya sesuatu untuk ditembus. */
  /* 0,90, bukan 0,52. Yang membuat sesuatu terbaca sebagai kaca adalah blur
     berat, saturasi, dan kilau tepi -- bukan seberapa tembus ia. Material yang
     ditiru desain ini jauh lebih pekat daripada kelihatannya: yang menembus
     hanya rona dan gerak, bukan bentuk.

     0,52 diukur di mockup, tempat yang ada di belakang kaca adalah gradasi yang
     dirancang untuk itu. Chrome ini mengambang di atas ISI: baris tabel, tombol
     hijau, chip amber. Di sana tidak ada satu pun latar yang bisa dijanjikan,
     jadi kontrasnya harus dipikul oleh kacanya sendiri. */
  --glass-bg:   rgba(251, 251, 250, 0.90);
  --glass-blur: blur(30px) saturate(190%);
  --glass-nav:  rgba(255, 255, 255, 0.90);
  --glass-edge: inset 0 1px 0 rgba(255, 255, 255, 0.55);
  --glass-line: rgba(20, 22, 26, 0.10);
  --glass-drop: 0 6px 28px rgba(20, 22, 26, 0.10);

  /* Bidang latar. Tema terang punya warnanya sendiri, bukan warna gelap yang
     diterangkan: di atas #fbfbfa gradasi pekat terbaca sebagai kotor, bukan
     sebagai kedalaman, jadi di sini ia jauh lebih halus. */
  --field-1: rgba(27, 58, 92, 0.11);
  --field-2: rgba(168, 106, 0, 0.09);

  /* Huruf */
  --font-sans:  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                "Helvetica Neue", Arial, sans-serif;
  --font-serif: Georgia, "Times New Roman", serif;
  --font-mono:  ui-monospace, "Cascadia Mono", "Segoe UI Mono", Consolas,
                "Liberation Mono", Menlo, monospace;

  color-scheme: light;
}

/* Tema gelap — "ikut sistem" */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #0e1013;
    --surface:       #16191d;
    --surface-sunk:  #1c2026;
    --surface-quiet: #131619;

    --ink:      #f2f0ed;
    --text:     #d8d5d1;
    --muted:    #8f8d88;
    --disabled: #6e6c68;

    --line:         #262b32;
    --line-row:     #1f242a;
    --line-control: #2b3138;
    --line-input:   #3a4048;

    /* Aksen NAIK terangnya di tema gelap — bukan dibalik.
       Warna gelap di atas latar gelap tidak terbaca. */
    --accent:       #6da3e0;
    --accent-hover: #8fbcec;
    --accent-soft:  #1a2733;
    --accent-line:  #2f4a63;

    --amber:       #d9a441;
    --amber-soft:  #241f14;
    --amber-line:  #3a3020;

    --danger:      #e08585;
    --danger-soft: #241717;
    --danger-line: #3d2626;

    --success:      #6fbf95;
    --success-soft: #16241d;

    --nav-bg:       #16191d;
    /* Nav ikut tema sejak 5e21f7d, jadi kedua token teksnya WAJIB
       dinyatakan ulang di sini. Tanpa ini nilai :root yang menang -- dan itu
       teks gelap di atas bar gelap, di seluruh nav. */
    --nav-text:     #f2f0ed;
    --nav-text-dim: #b4b2ae;
    --nav-active:   rgba(255, 255, 255, 0.10);

    --shadow-pop: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.45);
    --shadow-seg: 0 1px 2px rgba(0, 0, 0, 0.5);

    /* Warna bidangnya diambil dari gradasi demo kaca di kanvas:
       #23406b dan #6b3a4a. */
    --glass-bg:   rgba(22, 25, 29, 0.90);
    --glass-drop: 0 6px 28px rgba(0, 0, 0, 0.38);
    --glass-nav:  rgba(22, 25, 29, 0.90);
    --glass-edge: inset 0 1px 0 rgba(255, 255, 255, 0.17);
    --glass-line: rgba(255, 255, 255, 0.17);
    --field-1: rgba(35, 64, 107, 0.58);
    --field-2: rgba(107, 58, 74, 0.44);

    color-scheme: dark;
  }
}

/* Tema gelap — pilihan eksplisit, menang atas preferensi sistem */
:root[data-theme="dark"] {
  --bg:            #0e1013;
  --surface:       #16191d;
  --surface-sunk:  #1c2026;
  --surface-quiet: #131619;

  --ink:      #f2f0ed;
  --text:     #d8d5d1;
  --muted:    #8f8d88;
  --disabled: #6e6c68;

  --line:         #262b32;
  --line-row:     #1f242a;
  --line-control: #2b3138;
  --line-input:   #3a4048;

  --accent:       #6da3e0;
  --accent-hover: #8fbcec;
  --accent-soft:  #1a2733;
  --accent-line:  #2f4a63;

  --amber:       #d9a441;
  --amber-soft:  #241f14;
  --amber-line:  #3a3020;

  --danger:      #e08585;
  --danger-soft: #241717;
  --danger-line: #3d2626;

  --success:      #6fbf95;
  --success-soft: #16241d;

  --nav-bg:       #16191d;
  /* Nav ikut tema sejak 5e21f7d, jadi kedua token teksnya WAJIB
     dinyatakan ulang di sini. Tanpa ini nilai :root yang menang -- dan itu
     teks gelap di atas bar gelap, di seluruh nav. */
  --nav-text:     #f2f0ed;
  --nav-text-dim: #b4b2ae;
  --nav-active:   rgba(255, 255, 255, 0.10);

  --shadow-pop: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.45);
  --shadow-seg: 0 1px 2px rgba(0, 0, 0, 0.5);

  --glass-bg:   rgba(22, 25, 29, 0.90);
  --glass-drop: 0 6px 28px rgba(0, 0, 0, 0.38);
  --glass-nav:  rgba(22, 25, 29, 0.90);
  --glass-edge: inset 0 1px 0 rgba(255, 255, 255, 0.17);
  --glass-line: rgba(255, 255, 255, 0.17);
  --field-1: rgba(35, 64, 107, 0.58);
  --field-2: rgba(107, 58, 74, 0.44);

  color-scheme: dark;
}

/* Untuk menaikkan huruf nanti: taruh woff2 di app/static/fonts/ lalu
   hidupkan blok ini dan ganti --font-sans / --font-serif / --font-mono.
   Vendor lokal, JANGAN dari Google Fonts — aplikasi ini harus jalan offline.

@font-face { font-family: "Instrument Sans"; src: url("/static/fonts/InstrumentSans.woff2") format("woff2");
             font-weight: 400 700; font-display: swap; }
@font-face { font-family: "Instrument Serif"; src: url("/static/fonts/InstrumentSerif.woff2") format("woff2");
             font-weight: 400; font-display: swap; }
@font-face { font-family: "IBM Plex Mono"; src: url("/static/fonts/IBMPlexMono.woff2") format("woff2");
             font-weight: 400 600; font-display: swap; }
*/


/* ---------------------------------------------------------------------------
   2. DASAR
   --------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }


/* ---------------------------------------------------------------------------
   LATAR — dua bidang warna di belakang segalanya (K27)
   ---------------------------------------------------------------------------
   Kaca hanya terbaca sebagai kaca kalau ada sesuatu yang membayang di
   belakangnya. Tanpa lapisan ini, backdrop-filter mem-blur satu warna rata dan
   hasilnya adalah warna rata yang sama -- ongkos tanpa gambar.

   Lapisan sendiri, bukan background di <body>: menempel di body berarti ikut
   menggulir, dan setiap gulir jadi repaint sepenuh halaman.

   Yang dianimasikan HANYA transform. Menganimasikan posisi, ukuran atau warna
   memaksa lapisan ini dicat ulang tiap frame; transform dikerjakan compositor,
   jadi gradasinya dirasterisasi sekali lalu digeser saja. Itu perbedaan antara
   latar yang gratis saat menggulir dan latar yang membuat tabel panjang
   tersendat.
   --------------------------------------------------------------------------- */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

.backdrop span {
  position: absolute;
  display: block;
  width: 92vmax;
  height: 92vmax;
  border-radius: 50%;
  will-change: transform;
}

.backdrop span:first-child {
  top: -36vmax;
  left: -24vmax;
  background: radial-gradient(circle closest-side, var(--field-1), transparent 72%);
  animation: field-drift-a 26s ease-in-out infinite alternate;
}

.backdrop span:last-child {
  bottom: -40vmax;
  right: -28vmax;
  background: radial-gradient(circle closest-side, var(--field-2), transparent 72%);
  animation: field-drift-b 34s ease-in-out infinite alternate;
}

/* 26 dan 34 detik satu putaran, dan keduanya sengaja tidak habis dibagi satu
   sama lain supaya polanya tidak berulang persis. Kalau kamu menyadarinya
   sedang bergerak, ia terlalu cepat. */
@keyframes field-drift-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(9vmax, 6vmax, 0) scale(1.15); }
}

@keyframes field-drift-b {
  from { transform: translate3d(0, 0, 0) scale(1.09); }
  to   { transform: translate3d(-8vmax, -7vmax, 0) scale(0.93); }
}


body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 13.5px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; font-weight: 600; }

h1 {
  font-family: var(--font-serif);
  font-weight: 400;
  font-size: 32px;
  line-height: 1.12;
  letter-spacing: -0.015em;
}

h2 { font-size: 15px; }
h3 { font-size: 13.5px; }

p { margin: 0 0 12px; color: var(--text); }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* Angka dan kode — selalu mono, selalu lebar digit seragam.
   Ini yang membuat ratusan baris bisa dipindai mata. */
.mono, .num, td.num, th.num, code, kbd {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.muted    { color: var(--muted); }
.disabled { color: var(--disabled); }
.nowrap   { white-space: nowrap; }

.label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.note      { font-size: 11.5px; color: var(--muted); line-height: 1.55; }
.note-warn { font-size: 11.5px; color: var(--danger); line-height: 1.55; }

/* Teks yang hanya dibaca pembaca layar. Tombol tema hanya berisi ikon;
   tanpa ini ia tidak punya nama yang bisa diucapkan. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* ---------------------------------------------------------------------------
   3. KERANGKA
   --------------------------------------------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 16px;
  height: 52px;
  padding: 0 26px;
  background: var(--nav-bg);
  border-bottom: 1px solid transparent;
}

/* Translusensi hanya di chrome. Diturunkan jadi solid bila pengguna
   meminta transparansi dikurangi, atau di layar sempit (blur mahal di HP). */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .nav {
    background: var(--glass-nav);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-bottom-color: var(--glass-line);
    box-shadow: var(--glass-drop);
  }
}

/* Di luar @supports latarnya opak. 52% tanpa blur di belakangnya bukan kaca,
   itu isi halaman yang menembus nav dan tidak terbaca lagi. */
@media (prefers-reduced-transparency: reduce) {
  .nav { background: var(--nav-bg); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

.nav__brand {
  font-family: var(--font-serif);
  font-size: 18px;
  color: var(--nav-text);
  margin-right: 18px;
  white-space: nowrap;
}

.nav__links { display: flex; gap: 2px; flex: 1 1 auto; min-width: 0; }

.nav__link {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 7px 13px;
  border-radius: var(--r);
  font-size: 13px;
  color: var(--nav-text-dim);
  white-space: nowrap;
}
.nav__link:hover { color: var(--nav-text); text-decoration: none; background: var(--nav-active); }
.nav__link[aria-current="page"] { color: var(--nav-text); font-weight: 600; background: var(--nav-active); }

.nav__badge {
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 15px;
  padding: 0 5px;
  border-radius: 8px;
  background: var(--amber);
  color: #fff;
}
.nav__badge--danger { background: var(--danger); }

/* min-width: 0 dan tanpa flex-shrink: 0. Sebuah kelompok flex yang menolak
   menyusut sementara isinya bisa panjang tidak dipotong saat kesempitan -- ia
   melebarkan dokumen, dan halamannya bisa digulir menyamping sampai kartu di
   bawahnya keluar layar. Gejalanya muncul di nav; bentuknya akan lahir lagi di
   tempat lain kalau yang ditambal cuma gejalanya. */
.nav__right { display: flex; align-items: center; gap: 10px; min-width: 0; }
.nav__who { font-size: 12.5px; color: var(--nav-text-dim); }

/* Isi berhenti melebar di 1600px: baris 21 kolom yang membentang 2560px
   lebih sulit diikuti mata, bukan lebih informatif. */
.page   { padding: 26px 26px 40px; max-width: 1600px; margin: 0 auto; }
.page__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: 20px; }
.page__meta { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; font-size: 12.5px; color: var(--muted); margin-top: 8px; }
.page__meta .sep { width: 3px; height: 3px; border-radius: 50%; background: var(--line-control); }
.page__actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0; }

.breadcrumb { font-size: 12.5px; color: var(--muted); margin-bottom: 12px; }
.breadcrumb .sep { color: var(--line-control); margin: 0 5px; }


/* ---------------------------------------------------------------------------
   4. KOMPONEN
   --------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 34px;
  padding: 7px 14px;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: var(--accent);
  color: #fff;
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { background: var(--accent-hover); color: #fff; text-decoration: none; }

.btn--quiet { background: var(--surface); color: var(--ink); border-color: var(--line-control); font-weight: 500; }
.btn--quiet:hover { background: var(--surface-sunk); color: var(--ink); }

.btn--danger { background: transparent; color: var(--danger); border-color: var(--line-control); font-weight: 500; }
.btn--danger:hover { background: var(--danger-soft); color: var(--danger); }

.btn--ok { background: var(--success); }
.btn--ok:hover { background: var(--success); filter: brightness(1.08); }

.btn--link { background: none; border: none; color: var(--accent); padding: 7px 2px; font-weight: 500; }
.btn--link:hover { background: none; text-decoration: underline; }

.btn[disabled], .btn[aria-disabled="true"] {
  background: var(--surface-sunk);
  color: var(--disabled);
  border-color: var(--line);
  cursor: not-allowed;
  pointer-events: none;
}

.chip {
  display: inline-block;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: 2px;
  vertical-align: 1px;
  white-space: nowrap;
}
.chip--rop     { background: var(--amber);   color: #fff; }
.chip--crit    { background: var(--danger);  color: #fff; }
.chip--draft   { background: var(--accent-soft);  color: var(--accent);  font-weight: 600; }
.chip--ok      { background: var(--success-soft); color: var(--success); }
.chip--waiting { background: var(--amber-soft);   color: var(--amber); }

/* Penanda kualitas data. Bentuk, bukan hanya warna — supaya tetap terbaca
   oleh mata yang tidak membedakan merah. */
.warn-mark { color: var(--danger); font-weight: 700; }

.field, input[type="text"], input[type="number"], input[type="email"],
input[type="password"], input[type="date"], select, textarea {
  min-height: 34px;
  padding: 6px 9px;
  border: 1px solid var(--line-input);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
}
input[type="number"] { font-family: var(--font-mono); text-align: right; }
input::placeholder { color: var(--disabled); }

/* Terkunci harus terlihat terkunci. Kotak yang tidak bisa diketik tapi tampak
   persis seperti yang bisa membuat orang mengetik ke dalamnya lalu mengira
   angkanya tersimpan — di Request yang sudah diajukan, itu angka pesanan.
   Keadaan, bukan kelas baru, sejalan dengan .btn[disabled] di atas. */
input[readonly], textarea[readonly] {
  background: var(--surface-sunk);
  color: var(--disabled);
  cursor: default;
}

.seg {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  border-radius: 7px;
  background: var(--surface-sunk);
}
.seg > * {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 13px;
  border-radius: var(--r);
  font-size: 12.5px;
  color: var(--muted);
  cursor: pointer;
  border: none;
  background: none;
  font-family: inherit;
}
/* Tombol berputar hanya muncul di layar yang tidak muat ketiganya. Kedua
   bentuk ada di HTML; CSS yang memilih, jadi tidak ada cabang di handler dan
   keduanya tetap submit biasa. */
.seg__cycle { display: none; }
@media (max-width: 359px) {
  .seg__pick  { display: none; }
  .seg__cycle { display: flex; }
}

.seg > [aria-pressed="true"], .seg > .is-on {
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  box-shadow: var(--shadow-seg);
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
}
.card__body { padding: 18px 20px; }
.card--warn   { background: var(--danger-soft); border-color: var(--danger-line); }
.card--notice { background: var(--amber-soft);  border-color: var(--amber-line); }

.banner {
  padding: 11px 14px;
  border: 1px solid var(--accent-line);
  border-radius: 4px;
  background: var(--accent-soft);
  color: var(--ink);
  font-size: 12.5px;
  margin-bottom: 14px;
}
.banner--warn   { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }
.banner--notice { background: var(--amber-soft);  border-color: var(--amber-line); }

.stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; margin-bottom: 24px; }
.stat__n { font-family: var(--font-mono); font-size: 26px; font-weight: 600; line-height: 1; }

.empty { padding: 40px 24px; text-align: center; }
.empty__title { font-size: 13.5px; font-weight: 600; margin-bottom: 4px; }

/* Panel filter — mengambang, jadi boleh berkaca */
.drawer {
  position: fixed;
  top: 52px; right: 0; bottom: 0;
  width: 392px;
  max-width: 100vw;
  z-index: 35;
  overflow-y: auto;
  background: var(--surface);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-pop);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .drawer {
    background: var(--glass-bg);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    box-shadow: var(--glass-edge), var(--shadow-pop);
  }
}

@media (prefers-reduced-transparency: reduce) {
  .drawer { background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
.drawer__sect { padding: 16px 18px; border-bottom: 1px solid var(--line-row); }
.drawer__foot {
  position: sticky; bottom: 0;
  display: flex; align-items: center; gap: 9px;
  padding: 14px 18px;
  background: var(--surface-quiet);
  border-top: 1px solid var(--line-row);
}

.filter-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.filter-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 8px;
  border: 1px solid var(--accent-line);
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 12px;
}
/* Tanda lepas di dalam chip. Kotak singgungnya diperbesar lewat padding
   negatif-margin, bukan lewat huruf yang lebih besar: yang sulit dikenai jari
   adalah sasarannya, bukan tanda silangnya. */
.filter-chip a {
  display: inline-flex; align-items: center; justify-content: center;
  margin: -4px -4px -4px 0; padding: 4px 6px;
  color: inherit; font-size: 13px; line-height: 1; text-decoration: none;
}
.filter-chip a:hover { color: var(--danger); text-decoration: none; }

/* Baris keadaan saringan: berapa dari berapa, lalu chip-nya. */
.filter-state {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  margin-bottom: 12px;
}
.filter-state__count { font-size: 12.5px; color: var(--muted); }

/* Baris formulir — satu sistem untuk setiap baris kendali di aplikasi ini.

   Sebelumnya tiap baris kendali diatur sendiri-sendiri, dan `.searchbox` hanya
   `flex-wrap: wrap` tanpa aturan perataan. Begitu lebarnya kurang beberapa
   piksel, tombol Cari jatuh sendirian ke baris kedua rata kiri, tanpa hubungan
   visual dengan kotak yang ia kirimkan. Itu bukan pilihan desain melainkan
   ketiadaan aturan, dan ketiadaan aturan menghasilkan bentuk yang berbeda di
   tiap lebar layar.

   Dua aturannya:

   1. Yang membungkus adalah KELOMPOK, bukan kendali satuan. Kendali duduk di
      dalam .form-row__fields atau .form-row__actions, dan kedua kelompok itu
      tetap utuh: sebuah kelompok boleh turun ke baris berikutnya seluruhnya,
      tidak boleh pecah. Karena itu Cari dan Kosongkan tidak pernah terpisah
      baris -- keduanya di dalam kelompok yang sama.

   2. Yang menahan tombol tetap di baris pertama bukan wrap melainkan SUSUT.
      Kelompok kendali `min-width: 0` dan `flex: 1 1 ...`, jadi ia menyusut
      lebih dulu; kelompok tombol `flex: 0 0 auto` dan tidak pernah menyusut.
      Barisnya baru benar-benar bertumpuk di lebar kartu, dan di sana SEMUANYA
      bertumpuk -- tidak ada satu kendali yang tertinggal sendirian. */
.form-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.form-row__fields {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  flex: 1 1 220px; min-width: 0;
}
/* Tanpa flex-wrap: kelompok tombol adalah satu kesatuan, dan pasangan
   Cari/Kosongkan yang boleh membungkus adalah pasangan yang bisa terpisah. */
.form-row__actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* Kotak cari lima halaman (K32). Satu bentuk, dipakai kelimanya: ia hidup di
   macro `search()` di _filters.html, bukan disalin per halaman.

   Kotaknya melar, pilihan statusnya tidak: nama supplier dan alamat surel
   sepanjang apa pun, sementara "Menunggu keputusan" punya lebar yang tetap. */
.searchbox input[type="search"] { flex: 1 1 180px; min-width: 0; max-width: 320px; }
.searchbox select { flex: 0 1 auto; min-width: 0; }

@media (max-width: 639px) {
  /* Layar HP: tiap kendali selebar penuh dan bertumpuk. Tinggi 44px-nya diurus
     @media (pointer: coarse) di §7, jadi tidak diulang di sini -- layar sempit
     dan layar sentuh bukan hal yang sama, dan menyamakannya akan memberi
     sasaran 44px kepada jendela desktop yang dikecilkan sementara tablet
     sentuh yang lebar tidak mendapatkannya. */
  .form-row { flex-direction: column; align-items: stretch; width: 100%; }
  .form-row__fields { flex-direction: column; align-items: stretch; flex: 1 1 auto; }
  .form-row__fields > * { width: 100%; max-width: none; }
  /* Tombolnya tetap berdampingan, membagi lebar sama rata: dua tombol yang
     bertumpuk membuat yang di bawah terbaca sebagai tindakan lanjutan. */
  .form-row__actions > * { flex: 1 1 0; }
}

/* Panel saringan — <details>, jadi ia membuka tanpa JavaScript sama sekali. */
.panel { position: relative; }
.panel > summary { list-style: none; cursor: pointer; }
.panel > summary::-webkit-details-marker { display: none; }
.panel[open] > summary { background: var(--surface-sunk); }
.panel__count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: 9px;
  background: var(--accent); color: var(--surface);
  font-size: 10.5px; font-weight: 700;
}

.drawer__form { display: flex; flex-direction: column; }
.drawer__sect .label { display: block; margin-bottom: 6px; }
.drawer__sect .field { width: 100%; }
.drawer__sect .note { margin-top: 6px; }
.drawer__sect + .drawer__sect .label { margin-top: 0; }
.drawer__sect select + .label,
.drawer__sect input + .label { margin-top: 14px; }

.field-row { display: flex; align-items: center; gap: 8px; }
.field-row .field { width: 96px; flex: 0 0 auto; }

.check { display: flex; align-items: center; gap: 8px; font-size: 12.5px; cursor: pointer; }
.check + .check { margin-top: 8px; }
.check input { flex: 0 0 auto; }
/* Dua kolom, dan itu batasnya: tiga kolom membuat labelnya terpotong justru di
   nama kolom yang paling panjang, dan kotak centang yang labelnya terpotong
   tidak bisa dipilih dengan yakin. */
.check-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 14px; }
.check-grid .check + .check { margin-top: 0; }

.cover      { font-family: var(--font-mono); font-size: 15px; font-weight: 600; }
.cover--rop  { color: var(--amber); }
.cover--crit { color: var(--danger); }
.cover--none { color: var(--disabled); }


/* ---------------------------------------------------------------------------
   5. TABEL
   --------------------------------------------------------------------------- */

.table-wrap {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 4px;
  overflow: auto;
  max-height: calc(100vh - 260px);
}

table { border-collapse: separate; border-spacing: 0; width: 100%; font-size: 12.5px; }

th, td {
  padding: 8px 10px;
  text-align: left;
  border-bottom: 1px solid var(--line-row);
  white-space: nowrap;
}

thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface-sunk);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
thead th a { color: var(--muted); }

/* Baris kelompok kolom — mengubah 21 kolom jadi lima blok yang terbaca */
thead tr.group th {
  top: 0;
  z-index: 3;
  text-align: center;
  background: var(--surface-sunk);
  border-bottom: 1px solid var(--line);
}
thead tr.group + tr th { top: 28px; }

.gsep { border-left: 1px solid var(--line); }

td.num, th.num { text-align: right; }

tbody tr:hover td { background: var(--surface-quiet); }

tr.flagged td { background: var(--amber-soft); }
tr.flagged td:first-child { box-shadow: inset 2px 0 0 var(--amber); }
tr.flagged:hover td { background: var(--amber-soft); filter: brightness(0.99); }

tr.decided td { background: var(--surface-quiet); color: var(--muted); }

/* Sel yang angkanya tidak berdiri di atas jendela penuh (K30 butir 1). Latar,
   bukan hanya kata: lima puluh baris kata kecil berhenti terbaca, dan yang
   dibaca orang lebih dulu adalah bentuk kolomnya. Warnanya amber yang sama
   dengan baris ter-flag -- artinya sama-sama "lihat ini dulu", bukan "salah". */
td.suspect { background: var(--amber-soft); }
/* Kelasnya sendiri, bukan note-warn. Keduanya kuning, tapi note-warn berarti
   "baris ini terlambat" dan yang ini berarti "angka ini tidak berdiri di atas
   jendela penuh" -- dua fakta yang harus bisa dicari terpisah, di layar dan
   di test. */
.note-suspect { color: var(--amber); }
tr.flagged td.suspect { filter: brightness(0.985); }

/* Kolom beku — hanya di layar lebar. Sengaja dimatikan di sempit:
   position:sticky pada sel tabel tidak bisa diandalkan di Safari iOS. */
@media (min-width: 1024px) {
  td.stick, th.stick { position: sticky; z-index: 1; background: var(--surface); }
  thead th.stick { z-index: 4; background: var(--surface-sunk); }
  tr.flagged td.stick { background: var(--amber-soft); }
  .stick--1 { left: 0; }
  .stick--2 { left: 104px; }
}

.row-detail td {
  white-space: normal;
  background: var(--surface-quiet);
  padding: 14px 18px 16px 112px;
}

/* --- Select2 pada dropdown saringan ----------------------------------------

   Select2 membawa temanya sendiri, dan temanya hanya punya satu: terang, dengan
   warna yang dipahat (#aaa, #ddd, #5897fb). Ditimpa seluruhnya dengan token
   kita, jadi ia mengikuti tema seperti setiap kendali lain di aplikasi ini.

   Yang TIDAK dipakai: `background: var(--accent)` untuk baris tersorot. Di tema
   gelap --accent justru NAIK terangnya (#6da3e0), jadi teks putih di atasnya
   jatuh ke sekitar 2,7:1. Yang dipakai --accent-soft dengan --ink, yang lolos
   di kedua tema karena keduanya berpasangan menurut temanya. Angkanya diukur,
   bukan dikira -- ada di test dan di pesan commit. */

/* Wadah dropdown. `position: relative` bukan hiasan: ia yang membuat
   dropdownParent bekerja, dan dropdownParent yang menahan dropdown dari
   menempel ke <body> lalu mendorong lebar halaman di HP. */
.select2-host { position: relative; display: block; min-width: 0; }
.form-row__fields > .select2-host { flex: 0 1 auto; }

.select2-container--default .select2-selection--single {
  height: auto; min-height: 32px;
  background: var(--surface);
  border: 1px solid var(--line-input);
  border-radius: var(--r);
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
  color: var(--ink); font-size: 12.5px; line-height: 30px; padding-left: 10px;
}
.select2-container--default .select2-selection--single .select2-selection__placeholder { color: var(--muted); }
.select2-container--default .select2-selection--single .select2-selection__arrow { height: 30px; }
.select2-container--default .select2-selection--single .select2-selection__arrow b { border-color: var(--muted) transparent transparent; }
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { border-color: transparent transparent var(--muted); }
.select2-container--default.select2-container--focus .select2-selection--single,
.select2-container--default.select2-container--open .select2-selection--single {
  border-color: var(--accent);
}

.select2-dropdown {
  background: var(--surface);
  border: 1px solid var(--line-control);
  border-radius: var(--r);
  box-shadow: var(--shadow-pop);
}
.select2-container--default .select2-results__option { color: var(--text); font-size: 12.5px; padding: 7px 10px; }

/* Tinggi daftar sekitar lima baris, lalu bergulir. Bawaan Select2 200px
   dihitung untuk barisnya sendiri yang lebih tinggi; baris kita 34px. */
.select2-container--default .select2-results > .select2-results__options { max-height: 170px; }

.select2-container--default .select2-results__option--highlighted[aria-selected] {
  background: var(--accent-soft); color: var(--ink);
}
.select2-container--default .select2-results__option[aria-selected="true"] {
  background: var(--surface-sunk); color: var(--ink); font-weight: 600;
}
.select2-container--default .select2-search--dropdown { padding: 8px; }
.select2-container--default .select2-search--dropdown .select2-search__field {
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line-input); border-radius: var(--r);
  padding: 6px 8px; font-family: inherit; font-size: 12.5px;
}
.select2-container--default .select2-results__message { color: var(--muted); }

/* Footer: satu baris tenang di dasar halaman, ikut bergulir. Ukuran dan warna
   yang sama dengan catatan kaki lain -- ia keterangan, bukan isi. */
.site-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px;
  margin-top: 28px; padding-top: 14px;
  border-top: 1px solid var(--line-row);
  font-size: 11.5px; color: var(--muted);
}

/* Lencana versi di nav. Kecil, mono, dan tidak pernah menang perhatian atas
   apa pun di sebelahnya -- biayanya ruang, dan ruang nav sudah dihitung
   sampai piksel di 360px. */
.nav__version {
  font-family: var(--font-mono);
  font-size: 11px; color: var(--nav-text-dim);
  padding: 2px 6px; border-radius: 4px;
  background: var(--nav-active);
}

/* Paginasi. Dulu `justify-content: space-between` -- yang berarti tombolnya
   terlempar ke dua ujung lebar berapa pun, dan pada layar lebar keduanya
   berjarak seribu piksel dari angka yang menjelaskannya.

   Sekarang di tengah, sebagai tiga kelompok yang membungkus bersama. Yang
   paling dicari orang bukan tombolnya melainkan angkanya, jadi angkanya di
   tengah dan tidak pernah disingkat. */
.pagination {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 8px 14px; margin-top: 14px;
  font-size: 12.5px; color: var(--muted);
}
.pagination__steps { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* Keadaan nonaktif TERLIHAT nonaktif dan tidak bisa diklik: ia <span>, bukan
   <a> yang dipudarkan. Tautan pudar yang tetap bisa diklik memuat ulang
   halaman yang sama, dan halaman yang memuat ulang dirinya sendiri terbaca
   sebagai aplikasi yang menggantung. Bukan opacity saja -- warna dan latar
   yang berbeda, supaya bedanya terbaca juga di layar terang di gudang. */
.pagination .is-off {
  background: var(--surface-sunk);
  color: var(--muted);
  border-color: var(--line-row);
  cursor: default;
  opacity: .7;
}

@media (max-width: 639px) {
  /* Angkanya naik ke atas tombolnya: di layar sempit ketiga kelompok memang
     bertumpuk, dan yang harus terbaca lebih dulu adalah seberapa jauh daftar
     ini sebenarnya, bukan tombol untuk berpindah di dalamnya. */
  .pagination__where { order: -1; width: 100%; text-align: center; }
  .pagination__steps { flex: 1 1 auto; }
  .pagination__steps > * { flex: 1 1 0; }
}


/* ---------------------------------------------------------------------------
   6. RESPONSIF
   ---------------------------------------------------------------------------
   Tabel 21 kolom tidak dikecilkan — ia berubah bentuk. Yang berubah adalah
   informasi mana yang tampil lebih dulu, bukan skalanya.
   --------------------------------------------------------------------------- */

/* --- Laptop: kolom lokasi diringkas, sisanya tetap tabel --- */
@media (max-width: 1439px) {
  .col-loc { display: none; }        /* delapan kolom lokasi */
  .col-loc-sum { display: table-cell; }  /* satu kolom ringkas, bisa dibuka */
}
@media (min-width: 1440px) {
  .col-loc-sum { display: none; }
}

/* --- Tablet --- */
@media (max-width: 1023px) {
  .page { padding: 20px 18px 32px; }
  h1 { font-size: 28px; }

  .nav { padding: 0 18px; height: 50px; }
  .nav__link--overflow { display: none; }   /* dua menu terakhir masuk ke titik tiga */
  .nav__more { display: flex; }

  .table-wrap { max-height: none; overflow: visible; }
  .col-narrow-hide { display: none; }       /* mutasi 3 & 12 bln, harga */

  .drawer { top: 0; width: 100%; max-width: 420px; }

  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .nav__more { display: none; }
}

/* --- HP: tabel jadi kartu --- */
@media (max-width: 639px) {
  /* Ruang di bawah seukuran tab bar, bukan angka bulat yang kebetulan cukup.
     Ini soal baris terakhir bisa dijangkau, BUKAN soal kontras: bar-nya
     position: fixed, jadi barisnya tetap lewat di belakangnya saat digulir.
     Kontrasnya dipikul kaca dan warna labelnya. */
  .page { padding: 14px 0 calc(57px + 26px + env(safe-area-inset-bottom, 0px)); }
  .page__head { flex-direction: column; align-items: stretch; gap: 12px; padding: 0 16px; }
  .page__actions { flex-wrap: wrap; }
  h1 { font-size: 22px; }

  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; padding: 0 16px; }

  /* Panel saringan jadi lembar dari bawah, sesuai papan. Bukan gaya: di layar
     selebar ini panel selebar 392px dari kanan menutupi hampir seluruh tabel,
     dan yang ingin dilihat orang saat menyaring adalah hasilnya.

     z-index di atas tab bawah (45) dengan sengaja: lembar yang terbuka memang
     mengambil alih layar, dan tab yang menyembul di atasnya justru terlihat
     seperti kesalahan gambar. Ia ditutup lewat ringkasannya sendiri. */
  .drawer {
    top: auto; left: 0; right: 0; bottom: 0;
    width: 100%; max-width: none; z-index: 46;
    max-height: 84vh;
    border-left: none;
    border-top: 1px solid var(--line);
    border-radius: 12px 12px 0 0;
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .filter-state { padding: 0 16px; }
  .check-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Kartu tidak boleh dikurung. Blok tablet sudah melepas overflow dan
     max-height, dan blok ini memang berada di dalamnya -- tapi bentuk kartu
     RUSAK tanpa pelepasan itu, jadi ia dinyatakan di sini juga. Kebenaran satu
     tata letak tidak boleh bergantung pada aturan milik breakpoint lain. */
  .table-wrap { border: none; border-radius: 0; background: none;
                overflow: visible; max-height: none; }
  table, thead, tbody, tr, td { display: block; width: auto; }

  /* Bentuk tiered milik 640-1023px, dan selektornya (.table--tiered tbody td)
     lebih khusus daripada `td` polos di atas -- jadi di 375px ia yang MENANG
     kecuali dinyatakan ulang di sini. Ini keluarga yang sama dengan catatan
     .table-wrap di bawah: kebenaran satu tata letak tidak boleh bergantung
     pada aturan milik breakpoint lain.
     
     Diukur, bukan didalilkan: tanpa tiga baris ini `td.cell-name` tetap
     `inline-flex`, keempat catatannya berbaris dalam SATU baris flex yang
     tidak membungkus, dan halaman melebar jadi 407px pada viewport 375px
     begitu satu catatan panjang muncul -- keadaan "tujuh Lokasi tidak
     terbaca", yang tidak pernah dilihat siapa pun sampai sapuan
     tools/ukur_lebar.py membukanya. Itu kejadian KETUJUH dari kelas cacat
     yang sama. */
  .table--tiered tbody tr { display: block; }
  .table--tiered tbody td { display: block; width: auto; }
  .table--tiered tbody td[data-label] { display: flex; }
  .table--tiered tbody td.cell-name { display: block; }
  thead { position: absolute; left: -9999px; }   /* header disembunyikan, label pindah ke sel */

  tbody tr {
    background: var(--surface);
    border-bottom: 1px solid var(--line-row);
    padding: 13px 16px;
  }
  tr.flagged { background: var(--amber-soft); border-left: 3px solid var(--amber); }
  tr.flagged td { background: none; box-shadow: none; }

  td {
    border: none;
    padding: 2px 0;
    white-space: normal;
    text-align: left !important;
  }

  /* Sel bertanda data-label tampil sebagai pasangan label + nilai */
  td[data-label] { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
  td[data-label]::before {
    content: attr(data-label);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    flex-shrink: 0;
  }

  /* Yang tidak masuk kartu HP sama sekali. col-loc TIDAK di sini lagi:
     di kartu yang dibuka, pasangan label-nilai per Lokasi justru pembawa
     umur Snapshot -- ia disembunyikan lipatan, bukan selamanya. */
  .col-loc-sum, .col-narrow-hide, .col-phone-hide { display: none !important; }

  /* Nama barang jadi judul kartu */
  td.cell-name { font-size: 13.5px; font-weight: 600; line-height: 1.35; padding-bottom: 6px; }
  td.cell-name::before { content: none; }

  .row-detail td { padding: 12px 0 0; }

  /* Tab bawah */
  .tabbar {
    position: fixed; bottom: 0; left: 0; right: 0; z-index: 45;
    /* Jumlah tab mengikuti peran, bukan tetap empat (K20): kolomnya tumbuh
       sendiri sebanyak tujuan yang benar-benar bisa dibuka orang itu. */
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    background: var(--surface);
    border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom, 0);
  }

  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .tabbar {
      background: var(--glass-bg);
      -webkit-backdrop-filter: var(--glass-blur);
      backdrop-filter: var(--glass-blur);
      box-shadow: var(--glass-edge);
    }
  }

  @media (prefers-reduced-transparency: reduce) {
    .tabbar { background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
  }
  .tabbar a {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    /* --ink, bukan --muted. Teks 10px tidak pernah boleh diredupkan, dan label
       navigasi bukan informasi sekunder -- ia nama tempat yang dituju. Ini
       penyebab kedua yang berdiri sendiri: petunjuknya adalah label AKTIF lolos
       kontras sejak awal sementara yang tidak aktif tidak. */
    gap: 3px; height: 56px; font-size: 10px; color: var(--ink);
  }
  .tabbar a[aria-current="page"] { color: var(--accent); font-weight: 600; }
  .tabbar a:hover { text-decoration: none; }

  /* 8px, bukan 10: pada pointer kasar sasaran sentuh K14 mematok pemilih tema
     di 142px dan Keluar di 44px, jadi nav butuh 355px lebar viewport untuk muat.
     Dua kali 2px jarak yang dihemat di sini yang membuat 360px -- lebar Android
     kecil yang paling umum -- lewat, alih-alih meleset satu piksel. */
  .nav { gap: 8px; padding: 0 14px; }
  .nav__brand { margin-right: 0; }
  .nav__links, .nav__who, .nav__version { display: none; }
}

@media (min-width: 640px) {
  .tabbar { display: none; }
}

/* --- Bentuk per ukuran untuk tabel lebar (.table--tiered) ---
   Papan "Strategi per ukuran": >= 1024px tabel penuh dengan kolom beku;
   640-1023px baris dua tingkat -- nama dan empat angka keputusan di tingkat
   satu, sisanya pasangan label-nilai di tingkat dua; < 640px kartu empat
   angka dengan sisanya di balik satu ketukan. Diaktifkan lewat kelas pada
   tabelnya, bukan lewat nama halaman. */

/* Tombol lipat kartu: mati di semua lebar kecuali HP, dan mati juga selama
   atribut hidden-nya belum dicabut JavaScript. */
.card-toggle { display: none; }

@media (max-width: 1023px) {
  .table--tiered thead { position: absolute; left: -9999px; }
  .table--tiered tbody tr {
    display: flex; flex-wrap: wrap; align-items: baseline;
    column-gap: 16px; row-gap: 4px;
    padding: 12px 16px; border-bottom: 1px solid var(--line-row);
  }
  .table--tiered tbody td {
    display: inline-flex; align-items: baseline; gap: 6px; width: auto;
    border: none; padding: 0; white-space: normal; text-align: left;
  }
  .table--tiered tbody td[data-label]::before {
    content: attr(data-label);
    font-size: 10px; font-weight: 600; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--muted); flex-shrink: 0;
  }
  /* Tingkat satu: nama sebagai judul, lalu empat angka keputusan dengan
     urutan tetap -- Mutasi jendela terpilih, Cover, Global Stock, Saran. */
  .table--tiered td.cell-name { order: -9; flex: 1 1 320px; font-weight: 600; }
  .table--tiered td.cell-name::before { content: none; }
  .table--tiered td.key-fig {
    flex-direction: column; align-items: flex-start; gap: 2px;
    font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums;
  }
  .table--tiered td.kf-1 { order: -8; }
  .table--tiered td.kf-2 { order: -7; }
  .table--tiered td.kf-3 { order: -6; }
  .table--tiered td.kf-4 { order: -5; }
  .table--tiered td.cell-actions { order: 9; }
  .table--tiered td.cell-actions::before { content: none; }
  /* Kolom per-Lokasi tetap diringkas di lebar ini; specificity td tiered di
     atas mengalahkan aturan .col-loc umum, jadi dinyatakan ulang di sini. */
  .table--tiered td.col-loc { display: none; }
}

@media (max-width: 639px) {
  .table--tiered tbody tr { padding: 13px 16px; row-gap: 6px; }
  .table--tiered td.cell-name { flex: 1 1 100%; }
  .table--tiered td.key-fig { flex: 1 1 21%; min-width: 0; }
  .table--tiered td.cell-actions { flex: 1 1 100%; }
  .table--tiered td.cell-actions form { width: 100%; }
  .table--tiered td.cell-actions .btn { width: 100%; }
  /* Kartu terbuka menampilkan pasangan per-Lokasi -- data-label-nya membawa
     umur Snapshot, dan di HP tidak ada kepala kolom yang membawanya. */
  .table--tiered td.col-loc { display: inline-flex; }
  /* Sisanya di balik satu ketukan. Tanpa JavaScript tidak ada tr.folded,
     jadi kartunya tampil penuh alih-alih terkunci tertutup. */
  .table--tiered tr.folded td:not(.cell-name):not(.key-fig):not(.cell-actions) {
    display: none;
  }
  .card-toggle {
    display: inline-block; min-height: 44px; padding: 6px 0;
    background: none; border: none; color: var(--accent);
    font-size: 12.5px; font-weight: 600; cursor: pointer;
  }
  .card-toggle[hidden] { display: none; }
}

/* --- Sasaran sentuh (K14) ---
   Terikat jenis penunjuk, bukan lebar layar. Aturan lama -- min-height: 40px di
   max-width: 1023px -- membesarkan kontrol saat jendela desktop disempitkan,
   sesuatu yang tidak pernah dibutuhkan mouse, dan tidak membesarkannya sama
   sekali di tablet yang lebih lebar dari 1024px, yang justru dipakai dengan jari.
   44px adalah sasaran terkecil yang sama-sama dipakai iOS dan Android.

   font-size: 16px ikut pindah ke sini karena alasan aslinya juga soal sentuh,
   bukan lebar: Safari iOS memperbesar halaman sendiri begitu sebuah field lebih
   kecil dari itu. Sekarang berlaku juga di iPad melintang, yang dulu terlewat. */
@media (pointer: coarse) {
  .btn, .seg > *, .nav__link { min-height: 44px; min-width: 44px; }
  .field, input, select, textarea { min-height: 44px; font-size: 16px; }

  /* Tautan di dalam sel tabel. Satu baris teks setinggi 19px sulit dikenai jari,
     jadi yang diperbesar kotak singgungnya, bukan hurufnya. */
  td a { display: inline-block; min-height: 44px; padding-block: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }

  /* Varian A, dan ini syarat K27 nomor 2 — bukan tambahan. Bidangnya tetap
     ada, hanya berhenti: menghapusnya akan menghilangkan justru yang membuat
     kaca terbaca sebagai kaca. transform dinolkan supaya keduanya berdiri di
     tempat yang dirancang, bukan di titik mana pun keyframe kebetulan
     berhenti. */
  .backdrop span { transform: none; }
}


/* ---------------------------------------------------------------------------
   7. CETAK
   ---------------------------------------------------------------------------
   Selalu terang, selalu A4 melintang, apa pun tema dan perangkatnya.
   Latar kuning baris ter-flag diganti penanda supaya tidak menghabiskan
   tinta dan tetap terbaca di fotokopi hitam-putih.
   --------------------------------------------------------------------------- */

@page { size: A4 landscape; margin: 12mm; }

/* ── Halaman masuk ───────────────────────────────────────────────────────────
   Hidup di app.css dan bukan di dalam <style> login.html karena tokennya sama
   dan temanya sama; halaman masuk memang tidak mewarisi base.html, tapi ia
   memakai lembar gaya yang sama persis.

   SEMUANYA di-scope ke `.login`. Kolom di halaman ini setinggi 44px dengan
   sudut 5px, sementara `.field` di seluruh aplikasi 34px dengan sudut 3px --
   tabel dua puluh satu kolom tidak muat kalau tiap kotaknya 44px. Tanpa scope
   itu, merapikan satu halaman menggeser dua puluh halaman lain. */

.login {
  /* 100vh lebih dulu sebagai CADANGAN, lalu 100dvh menimpanya di peramban yang
     mengerti. `100vh` sendirian meluber di Safari iOS: ia menghitung tinggi
     TANPA bilah browsernya, jadi kartu yang persis setinggi layar terdorong ke
     bawah bilah itu dan halamannya bisa digulir walau isinya muat. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  margin: 0;
}

/* 100% - 32px, bukan 100%: di 375px kartunya berhenti 16px dari tiap tepi
   alih-alih menempel, dan padding 24px pada <body> tidak bisa menjamin itu
   sendirian karena kartu yang lebih lebar dari sisanya akan meluber. */
.login__kartu {
  width: min(380px, 100% - 32px);
  border-radius: 6px;
}
.login__kartu > .card__body { padding: 32px; }

/* Kedua kolom SAMA PERSIS. Tinggi ditulis eksplisit, bukan diserahkan ke
   padding: sebuah kolom yang tingginya hasil penjumlahan padding dan
   line-height akan bergeser satu piksel begitu ukuran fontnya berubah, dan
   dua kolom yang berselisih satu piksel terbaca sebagai kelalaian. */
.login .field {
  display: block;
  width: 100%;
  height: 44px;
  min-height: 44px;
  padding: 0 12px;
  border-radius: 5px;
  font-size: 13px;
}

/* `display: block`, dan itu bukan kerapian: sebuah `<input>` adalah kotak
   SEBARIS, jadi ia duduk di dalam kotak baris anonim yang menambahkan
   setengah-leading di atas dan di bawahnya. Jarak yang terukur karenanya bukan
   margin yang ditulis -- dan irama yang diklaim 20px terukur 22px, dengan dua
   piksel yang tidak dimiliki aturan mana pun. */

/* Lebih spesifik daripada `.login .field` di atas, dan harus: `padding: 0 12px`
   di sana adalah shorthand, jadi ia MENIMPA padding-right yang ditulis di
   `.sandi__isian` yang selektornya satu kelas. Terukur: teks sandi berhenti
   12px dari tepi kanan, tepat di bawah tombolnya. */
.login .sandi__isian { padding-right: 48px; }

/* Irama tegak, satu skala. Angkanya di sini dan hanya di sini -- sampai
   3 September 2026 separuhnya ditulis sebagai style sebaris di templatenya,
   jadi tidak ada satu tempat pun yang bisa dibaca untuk mengetahui jaraknya. */
.login__judul { font-size: 26px; margin: 0 0 6px; }
.login__sub   { margin: 0 0 28px; }
.login .label { display: block; margin-bottom: 6px; }
.login__email { margin-bottom: 18px; }

.sandi {
  position: relative;
  display: block;
  margin-bottom: 16px;
}

/* padding-right menyisakan tempat untuk tombolnya: 4px inset + 36px tombol +
   8px napas. Tanpa ini, sandi yang panjang berjalan di BAWAH ikonnya dan huruf
   terakhirnya tidak terbaca -- dan yang paling sering panjang justru sandi yang
   benar. Diuji dengan sandi 40 karakter, bukan dengan melihat. */
.sandi__isian { padding-right: 48px; }

/* DI DALAM kolomnya, bukan di sebelahnya: absolut terhadap `.sandi`, jadi ia
   tidak menyumbang apa pun ke tinggi baris. Sebuah tombol yang ikut mengalir
   membuat kolom sandi lebih tinggi daripada kolom email, dan selisih itu yang
   paling dulu terlihat orang di halaman sesederhana ini. */
.sandi__mata {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  border-radius: 5px;
}

.sandi__mata:hover { color: var(--text); }

/* Fokus keyboard harus terlihat: tombol ini dijangkau Tab di antara kotak sandi
   dan tombol Masuk, dan sebuah tombol yang tidak menunjukkan fokusnya membuat
   urutan Tab jadi tebakan. `:focus-visible` dan bukan `:focus`, supaya klik
   tetikus tidak meninggalkan cincin yang tidak diminta siapa pun. */
.sandi__mata:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  color: var(--text);
}

.sandi__ikon { display: block; }

/* SATU ikon pada satu waktu, dan aturan ini yang menegakkannya.
   `[hidden] { display: none }` di lembar gaya bawaan peramban ditulis untuk
   elemen HTML; sebuah `<svg>` membawa atribut `hidden` TIDAK tersembunyi
   olehnya. Terukur 3 September 2026: kedua ikon dirender bersamaan di dalam
   tombol 36px, berdampingan, sejak hari halaman ini lahir -- dan tidak ada satu
   test pun yang melihatnya karena semuanya membaca atribut, bukan piksel. */
.sandi__ikon[hidden] { display: none; }

/* K14: sasaran sentuh 44px, lewat pseudo-elemen yang MEMPERLUAS AREA KLIK
   tanpa menyentuh tata letak. Yang tampak tetap 36px.

   Sampai 3 September 2026 ini dikerjakan dengan melebarkan tombolnya sendiri
   jadi 44px, dan ongkosnya nyata: kotak sandi harus ikut menambah
   padding-right-nya, dan tombol yang lebih lebar dari tinggi terlihat seperti
   bingkai. Pseudo-elemen absolut tidak menyumbang ukuran ke induknya, jadi
   jarinya mengenai 44px sementara matanya melihat 36px. */
@media (pointer: coarse) {
  .sandi__mata::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 44px;
    height: 44px;
  }
}

/* Seluruh baris bisa ditekan karena <label> membungkus kotak DAN teksnya --
   sasaran 16px yang harus dikenai tepat adalah sasaran yang meleset. */
.ingat {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
  font-size: 13px;
  color: var(--muted);
  cursor: pointer;
}

.ingat input {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  margin: 0;
  accent-color: var(--accent);
}

/* Kotaknya tetap 16px; yang tumbuh tinggi BARISNYA, dan barisnya seluruhnya
   bisa ditekan. Sebuah kotak centang 20px terlihat lebih besar daripada
   tetangganya di halaman lain tanpa jadi lebih mudah dikenai. */
@media (pointer: coarse) {
  .ingat { min-height: 44px; }
}

/* `flex` dan bukan `inline-flex` yang diwarisi `.btn`: alasannya sama dengan
   `display: block` pada kolom di atas -- kotak sebaris membawa setengah-leading
   yang tidak dimiliki aturan mana pun, dan iramanya jadi meleset dua piksel. */
.login__masuk {
  display: flex;
  width: 100%;
  height: 44px;
}

.login__versi {
  margin: 18px 0 0;
  text-align: center;
  font-size: 11px;
}

@media print {
  :root, :root[data-theme="dark"] {
    --bg: #fff; --surface: #fff; --surface-sunk: #eaeaea; --surface-quiet: #fff;
    --ink: #000; --text: #000; --muted: #333; --disabled: #666;
    --line: #999; --line-row: #bbb; --line-control: #999;
    --amber-soft: #fff; --danger-soft: #fff; --accent-soft: #fff;
    color-scheme: light;
  }

  .nav, .tabbar, .drawer, .panel, .searchbox, .backdrop, .page__actions,
  .pagination, .btn, .seg, .filter-chips, .no-print { display: none !important; }

  /* Hitungan hasil tetap dicetak: lembar yang dibawa ke rapat harus
     mengatakan bahwa ia sebagian, dan berapa bagian. Chip-nya yang tidak ikut
     -- ia kontrol, dan kontrol di atas kertas tidak menuju ke mana pun. */
  .filter-state { display: block; margin-bottom: 8px; }
  .filter-state__count { color: #000; }

  body { background: #fff; font-size: 12px; }
  .page { padding: 0; }
  .table-wrap { border: none; max-height: none; overflow: visible; }

  th, td { border: 0.5px solid #999; padding: 3px 4px; white-space: nowrap; }
  thead th { position: static; background: #eaeaea; }

  /* Semua kolom kembali, apa pun lebar layarnya saat mencetak */
  .col-loc { display: table-cell !important; }
  .col-loc-sum { display: none !important; }

  /* Bentuk tiered dan lipatan kartu tidak ikut ke kertas: mencetak dari
     jendela sempit tetap menghasilkan tabel penuh. */
  .table--tiered thead { position: static; left: auto; }
  .table--tiered tbody tr { display: table-row; }
  .table--tiered tbody td { display: table-cell !important; }
  .table--tiered tbody td[data-label]::before { content: none; }

  tr.flagged td { background: #fff; }
  tr.flagged td:first-child { box-shadow: none; }
  tr.flagged td:first-child::before { content: "■ "; font-weight: 700; }

  tr { page-break-inside: avoid; }
  thead { display: table-header-group; }

  /* Kolom kosong untuk coretan tangan — meniru cara kerja yang sudah berjalan */
  .print-only { display: table-cell !important; }
}

.print-only { display: none; }
