/*
 * TAMBAHAN DI ATAS CSS MOCKUP (BAGIAN A §A.1.1) — hanya diisi lewat keputusan
 * perancang; BUKAN tempat menambal selisih paritas.
 *
 * 1) Konsekuensi port yang DISENGAJA: butir nav & tombol yang di mockup berupa
 *    <button> (penukar layar) menjadi <a> tautan asli di aplikasi (BAGIAN A §A.3,
 *    normalisasi `navaksi`). <a> membawa gaya bawaan peramban — garis bawah +
 *    warna tautan — yang tidak pernah ada di mockup, jadi dinolkan di sini supaya
 *    rupa kontrolnya persis mockup.
 */
a.nav-item, a.bnav-item, a.akunitem, a.btn, a.tbtn { text-decoration: none; }
a.nav-item, a.bnav-item, a.akunitem { color: inherit; }

/* ⛔ TEMUAN CEO 10 Nov: *"tombol navigasi kenapa kenali ada garis bawah?"* — dan
   diukur, ia **TUJUH KELAS**, bukan satu tombol. Daftar di atas lahir untuk bilah
   KANTOR dan tidak pernah menyebut kelas permukaan Kerja, jadi semuanya memakai rupa
   tautan bawaan peramban.
   ⚠ Yang paling terlihat memang "Kenali": ia satu-satunya butir bilah yang labelnya
   duduk di bawah lingkaran penuh warna, jadi garisnya terbaca jelas — butir lain
   tergaris juga, hanya lebih samar. Satu cacat, bukan satu tombol.
   ⚠ Menolkannya MENJALANKAN aturan yang sudah tertulis di kepala berkas ini
   (*"warna tautan … dinolkan di sini supaya rupa kontrolnya persis mockup"*), bukan
   keputusan rupa yang baru. Dijaga `K385` supaya kelas tautan berikutnya tidak lahir
   tergaris lagi. */
a.kbil-item, a.kbil-utama, a.kerja-aksi, a.kerja-merek,
a.aset-muka, a.att, a.cariitem, a.chip, a.small { text-decoration: none; }

/*
 * 2) D-244 — CETAK SEMUA SLIP (keputusan CEO 28 Sep, pilihan "lengkapi jadi
 *    benar-benar semua"). Mockup tidak punya aturan cetak sama sekali, jadi
 *    ini BUKAN port yang disusun ulang dengan mata — ia bentuk baru yang
 *    lahir dari keputusan, dan itulah satu-satunya isi sah berkas ini.
 *
 *    Blok slip diteleport ke <body>, sehingga satu aturan `body > *` membuang
 *    seluruh cangkang (sidebar · topbar · nav bawah · kartu layar) tanpa
 *    mendaftar kelasnya — daftar kelas akan basi tiap kali cangkang berubah.
 */
.cetak-semua-slip { display: none; }

@media print {
  /*
   * ⛔ DIKURUNG `body:has(…)` DENGAN SENGAJA. Tanpa kurungan itu aturan ini berlaku di
   * SELURUH layar, dan di layar yang tidak punya blok cetak ia menyembunyikan segalanya —
   * Ctrl+P di Laporan Pesanan mencetak halaman KOSONG. Ditemukan dengan menanyakan sumbu
   * KEBALIKAN ("apa yang terjadi di layar LAIN?"), bukan dengan mencetak layar slipnya:
   * di /profil `body` punya 6 anak dan tak satu pun bernama blok cetak.
   * Aturan yang membuang segalanya bukan aturan cetak, ia pemadam.
   */
  body:has(.cetak-semua-slip) > *:not(.cetak-semua-slip) { display: none !important; }
  .cetak-semua-slip { display: block !important; }
  .slip-cetak { break-after: page; page-break-after: always; padding: 0 0 1.5rem; }
  .slip-cetak:last-child { break-after: auto; page-break-after: auto; }
  .slip-cetak h2 { font-size: 1.15rem; margin: 0 0 .15rem; }
  .slip-cetak-nama { font-weight: 700; margin-bottom: .1rem; }
  .slip-cetak-asal,
  .slip-cetak-kaki { font-size: .78rem; color: #555; }
  .slip-cetak-kaki { margin-top: .8rem; border-top: 1px solid #bbb; padding-top: .3rem; }
  .slip-cetak-catatan { font-size: .78rem; border: 1px solid #999; padding: .35rem .5rem; margin-top: .6rem; }
  .slip-cetak table { width: 100%; border-collapse: collapse; margin-top: .6rem; }
  .slip-cetak th,
  .slip-cetak td { border-bottom: 1px solid #ccc; padding: .22rem .3rem; text-align: left; font-size: .85rem; }
  .slip-cetak .num { text-align: right; }
  @page { margin: 14mm; }
}

/*
 * 3) D-248 lanjutan — LENCANA NAV BAWAH DIGESER KE SUDUT BUTIR (keputusan CEO
 *    29 Sep, pilihan "geser pil ke sudut butir").
 *
 *    Mockup menaruhnya `right:22%` di atas area ikon, dan geometri itu dirancang
 *    untuk lencana SATU digit (mockup memakai `7`). Dengan `99+` yang selebar
 *    32px di atas ikon 15px, pil menutupi 67% ikonnya — butir Tindakan jadi
 *    satu-satunya butir nav yang ikonnya nyaris tak terbaca di 390px.
 *
 *    Yang digeser HANYA jangkarnya; bentuk, warna, dan ukuran pil tetap milik
 *    mockup. Ini penyimpangan yang DISENGAJA terhadap satu nilai CSS, dicatat
 *    di sini supaya ia terbaca sebagai keputusan, bukan sebagai selisih port.
 */
@media (max-width: 820px) {
  .bnav-item .lencana { right: .3rem; top: .05rem; }
}

/* ====================================================================
 * LEBAR ISI MENGIKUTI PERANGKAT — keputusan CEO 13 Okt (D-307).
 *
 * ⛔ Dan keputusan yang lebih besar daripada satu aturan CSS: di bagian
 * TATA LETAK, kemiripan dengan mockup BUKAN lagi yang menentukan.
 * CEO: *"yang saya setujui adalah layout penuh dan responsif. mockup yang
 * membuat bug namun tidak diperbaiki hingga saat ini. jadi aturan wajib
 * mirip bug di bagian layout ini gugur dan lebih mementingkan responsif."*
 *
 * Jadi `.content{max-width:1180px}` di mockup bukan spesifikasi yang
 * aplikasi langgar — ia CACAT yang ikut tersalin. Membelanya dengan
 * "mockup sudah menjawab" adalah cara saya kemarin, dan itu keliru:
 * yang disahkan konsepnya (penuh & responsif), bukan angkanya.
 *
 * Terukur di layar CEO sebelum dicabut: jendela 1920 · isi 1180 ·
 * **508px menganggur**, sementara tabel Katalog (10 kolom) berdesakan
 * di 1136px. Sesudah: isi 1673 · sisa 15.
 *
 * ⛔ Dicabut DI SINI, bukan dengan menyunting `mockup.css`: salinan itu
 * wajib identik byte-demi-byte dengan mockup (CangkangParitasTest), dan
 * menyuntingnya akan membuat SETIAP perbandingan port sesudahnya
 * berbohong. Penyimpangan tata letak bertempat di berkas ini supaya ia
 * terbaca sebagai keputusan, bukan tersembunyi di dalam salinan.
 *
 * ⚠ TANPA batas pengganti apa pun — termasuk batas baca per kalimat yang
 * sempat saya pasang lalu dicabut CEO di hari yang sama: "penuh" berarti
 * penuh. Bila kelak kalimat terasa melelahkan di layar sangat lebar,
 * obatnya per-elemen dan atas perintah, bukan batas baru di cangkang.
 * ==================================================================== */
.content { max-width: none; }

/* ====================================================================
 * SOROTAN PANDUAN — redupan dinaikkan .34 → .55 (keputusan CEO 21 Okt,
 * ADR-265 §2; D-370).
 *
 * Teguran yang melahirkannya: *"sorotannya tidak terlihat jelas"*.
 * Sebab utamanya ternyata BUKAN angka ini — sorotannya memang tidak
 * pernah terpasang (hook Livewire + kandidat tak-terlihat, D-370) — dan
 * itu sudah diperbaiki lebih dulu. Angka ini diperiksa SESUDAHNYA, dengan
 * mengadu dua keadaan pada daerah layar yang sama: redupan .34 di atas
 * tema yang sudah gelap memang BEKERJA, tetapi bedanya nyaris tidak
 * terasa. Jadi yang dinaikkan hanya ini, bukan garis sorotnya.
 *
 * ⛔ Dicabut DI SINI, bukan dengan menyunting `mockup.css`: salinan itu
 * wajib identik byte-demi-byte dengan mockup (CangkangParitasTest).
 * ==================================================================== */
.disorot { box-shadow: 0 0 0 9999px rgba(10, 18, 28, .55); }

/* BILAH "SEDANG MELIHAT SEBAGAI" — D-374 (keputusan CEO 22 Okt 2026).
   Rumahnya di sini, bukan di salinan mockup: mockup adalah potret yang disahkan dan
   tidak disunting (pola ADR-262). Warnanya memakai token merek yang sudah ada, bukan
   warna karangan; sengaja memakai --warn supaya terbaca sebagai KEADAAN SEMENTARA,
   bukan sebagai bagian tetap aplikasi. */
.bilah-menyamar {
  position: sticky; top: 0; z-index: 70;
  display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
  padding: .55rem 1rem;
  background: var(--warn-soft, #FFF4D6);
  color: var(--text, #182633);
  border-bottom: 2px solid var(--warn, #C98A00);
  font-size: .92rem;
}
.bilah-menyamar form { margin-left: auto; }
@media (max-width: 480px) {
  .bilah-menyamar { font-size: .86rem; padding: .5rem .7rem; }
  .bilah-menyamar form { margin-left: 0; }
}

/* ====================================================================
 * KARTU .aq MEMENUHI LEBAR WADAHNYA — D-383 (teguran CEO 22 Okt 2026:
 * *"tampilan pengaturan di hp sangat jelek"*).
 *
 * ⛔ Sebabnya bukan layar Pengaturan, melainkan KELAS `.aq`, dan sebabnya halus:
 * `.aq` adalah <button>, dan kontrol formulir memakai lebar SEPANJANG ISINYA walau
 * `display`-nya grid — aturan yang tidak berlaku pada <div>. Selama ini ia tidak
 * terlihat karena pemakai aslinya duduk di dalam `.antrean`, sebuah grid yang
 * meregangkan anaknya. Di luar grid itu — indeks Pengaturan, Papan Tugas, Profil —
 * tiap kartu selebar teksnya sendiri, jadi tepi kanannya bergerigi seperti tangga
 * dan menyisakan ruang kosong besar di sebelahnya.
 *
 * ⚠ Diperbaiki pada KELASNYA, bukan pada layar yang dilaporkan (Prinsip 6). Di dalam
 * `.antrean` ia tidak mengubah apa pun: anak grid memang sudah selebar petaknya.
 *
 * ⚠ Dan ini pengukuran yang saya lewatkan: 390px sudah pernah diperiksa dan dinyatakan
 * bersih — tetapi yang diukur LUBER MENDATAR dan TINGGI SENTUH, bukan apakah kartunya
 * memenuhi lebar. Kartu yang terlalu SEMPIT tidak meluber, jadi sumbunya benar dan
 * diam sekaligus. Yang menemukannya CEO, dengan melihatnya.
 * ==================================================================== */
.aq { width: 100%; }

/* ====================================================================
 * ISIAN SANDI: TOMBOL PERLIHATKAN/SEMBUNYIKAN — D-383 (permintaan CEO).
 * Bidang sentuhnya 40px penuh (ADR-032); isiannya diberi ruang kanan supaya
 * teks sandi tidak pernah berjalan ke bawah tombolnya.
 * ==================================================================== */
.sandi-bungkus { position: relative; display: block; }
.sandi-bungkus .inp { width: 100%; padding-right: 2.9rem; }
.sandi-mata {
  position: absolute; right: .25rem; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  min-width: 40px; min-height: 40px;
  border: 0; background: transparent; cursor: pointer;
  font-size: 1rem; line-height: 1; opacity: .6; border-radius: 8px;
}
.sandi-mata:hover, .sandi-mata:focus-visible { opacity: 1; }
.sandi-mata.on { opacity: 1; background: var(--surface-2); }

/* ====================================================================
 * PAPAN TUGAS RAMPING & SATU DIVISI DI LAYAR SEMPIT — Seri S TAHAP S5
 * (pilihan CEO 23 Okt: "di ponsel satu divisi, bisa diganti").
 *
 * .aksi-kartu: aturan .aq menjadikan tiap anak non-baku SATU BARIS GRID penuh,
 * jadi tiga tombol = tiga baris — itulah kartu 8 baris yang CEO sebut
 * "menghabiskan layar". Dibungkus satu baris-aksi, tombolnya mengalir.
 *
 * .pilih-divisi hidup HANYA <=900px: desktop tetap kolom berdampingan, jadi
 * transparansi antar divisi (PERINCIAN-PAPAN-TUGAS 128) tidak berubah.
 * ==================================================================== */
.aksi-kartu { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .4rem; }
/* Seri T: kartu MILIK si pembuka disorot & didahulukan — jawaban teguran CEO 24 Okt
   (tugas untuk yang bersangkutan di-highlight / jadi prioritas). */
.aq.milikku { border-left: 3px solid var(--brand); }
/* isian tanggal (Seri T): input date bergaya isian aplikasi — menggantikan ketikan bebas */
input.inp.tgl { appearance: none; font: inherit; }
.aksi-kartu:empty { display: none; }
/* ⛔ `.pilih-divisi` (Seri S) DICABUT Seri U: pemilih divisi di ponsel digantikan
   tombol *Lihat tugas divisi lain* yang berlaku di SEMUA lebar layar — keputusan CEO
   24 Okt (*"yang tampil ditampilan utama tetap tugasnya sendiri"*). Satu mekanisme
   untuk satu maksud; dua akan berselisih di lebar 900px. */
.pilih-divisi { display: none; }

/* ===== Seri U — papan proyek: tiap divisi WADAHNYA SENDIRI (pola Trello) =====
   Baris kolom menggulir mendatar; tiap kolom punya kepala & badan sendiri sehingga
   terbaca terpisah, bukan satu kartu besar berisi tiga lajur. */
.papan-baris { display: flex; gap: .8rem; align-items: flex-start; overflow-x: auto; padding-bottom: .4rem; }
.papan-baris > .kol-divisi { flex: 0 0 clamp(248px, 25vw, 340px); margin: 0; }
/* ⛔ `outline-offset: -2px` BUKAN selera (D-454, laporan CEO 31 Okt: *"highlight
   outline kiri dan atas tidak terlihat"*). `outline` digambar DI LUAR kotak elemen,
   sedangkan `.papan-baris` wadah bergulir (`overflow-x:auto` — yang memaksa sumbu
   lainnya ikut memotong). Kolom pertama duduk rapat di tepi kiri-atas wadah, jadi
   2px cincinnya jatuh DI LUAR area gulir dan hilang di dua sisi itu saja — sisi
   kanan & bawah selamat, dan justru itu yang membuatnya terbaca seperti cacat
   gambar, bukan cacat aturan. Offset negatif menarik cincinnya ke DALAM tepi
   kartu: tidak ada yang bisa memotongnya, dan tata letaknya tidak bergeser 1px pun. */
.papan-baris > .kol-divisi.kol-saya { outline: 2px solid var(--brand); outline-offset: -2px; }
.kol-divisi .card-h { gap: .4rem; }
.kol-isi { display: flex; flex-direction: column; gap: .5rem; max-height: 62vh; overflow-y: auto; }
/* ⛔ KARTU TIDAK BOLEH DIMAMPATKAN OLEH KOLOMNYA (laporan CEO 3 Nov: *"kok papan
   tugas masih berantakan?"*, dengan tangkapan layar yang menunjukkan baris chip
   sebuah kartu tertimpa kartu di bawahnya).

   Sebabnya BUKAN kartunya, melainkan baris di atas ini: `.kol-isi` adalah flex
   KOLOM bertinggi terbatas. Begitu isinya lebih tinggi daripada ruangnya, flex
   MEMAMPATKAN setiap anaknya (`flex-shrink` bawaan = 1) sebelum kolomnya
   memutuskan bergulir. Kartu yang terdesak tetap menggambar isinya — isi itulah
   yang tumpah ke luar kotak dan menimpa kartu berikutnya.

   Terukur di peramban 1440px dengan 8 kartu (kondisi tangkapan layar CEO):
     sebelum  kartu 62px sementara isinya 93px · baris grid tengah 4,5px untuk
              teks 24px · `.kartu-kaki` keluar +32px · 8 dari 8 kartu rusak;
     sesudah  kartu 130px = tinggi isinya · baris tengah 28,7px · 0 dari 8 rusak,
              dan kolomnya TETAP bergulir (yang diperbaiki pemampatannya, bukan
              kemampuan menggulungnya).

   ⚠ Ia hanya menggigit DI ATAS 900px: di bawah itu `max-height` dicabut (lihat
   aturan media D-409), kolomnya setinggi isinya, dan tidak ada yang dimampatkan.
   Itu sebabnya pengukuran 390px saya berkata BERSIH dan tetap jujur — ia mengukur
   luber MENDATAR pada lebar yang memang tidak terkena. Pagar yang menjawab
   pertanyaan di sebelahnya akan hijau selamanya tanpa berbohong sekali pun.

   ⚠ `.carikotak` TIDAK ikut diberi aturan ini walau bentuknya mirip (flex kolom,
   `max-height:70vh`): ia punya anak yang memang bertugas bergulir (`.carilist`),
   jadi yang menyusut daftarnya — bukan kepalanya. Yang rusak hanya wadah yang
   menggulir DIRINYA SENDIRI sementara seluruh anaknya isi. */
.kol-isi > * { flex-shrink: 0; }

/* ============ BARIS TOMBOL DI LAYAR SEMPIT (D-480, pilihan CEO 3 Nov) ============
   Terukur di 390px, 23 layar: 17 punya minimal satu baris tombol yang PECAH jadi
   dua baris. Dua wadah saja yang jadi sebabnya — `.head-actions` dan `.tabs` —
   jadi satu perbaikan kelas menutup ke-38 layar tanpa menyentuh Blade layar.

   Bentuknya DIPILIH CEO: tombol kepala jadi *aksi utama + menu …*, bilah tab jadi
   *gulir mendatar*. Susunan …-nya dibangun skrip di cangkang (Livewire tidak
   menjalankan skrip yang ia render sendiri); yang di sini rupanya saja. */
@media (max-width: 900px) {
  /* satu baris, tanpa membungkus — isinya sudah dijamin muat oleh menu … */
  .head-actions { flex-wrap: nowrap; }
  /* ⛔ Tab: gulir, BUKAN membungkus. `scrollbar-width:none` menyembunyikan
     bilahnya saja — menggulirnya tetap bisa, dan tab aktif digeser ke tengah
     oleh skrip supaya tidak ada yang mengira tab aktifnya hilang. */
  .tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs > .tab { flex: 0 0 auto; }
}
/* Menu …: pemicunya setara tombol lain, panelnya menggantung di bawahnya. */
/* ⛔ Panelnya berjangkar pada BARISNYA, bukan pada tombol …. Versi pertama
   menjangkarkannya ke `.ha-menu` (`position:relative`) dan panel 200px itu
   KELUAR LAYAR di 390px, sebab … berdiri di kiri aksi utama, bukan di tepi
   kanan. Terukur sebelum diperbaiki: tepi kanan panel melewati 390px. */
[class].ha-menu { position: static; display: inline-flex; flex: 0 0 auto; }
[class].ha-baris { position: relative; }
[class].ha-menu-pemicu { min-width: 40px; }
[class].ha-menu-isi { position: absolute; z-index: 70; top: calc(100% + .3rem); right: 0;
  min-width: 200px; max-width: calc(100vw - 32px);
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  padding: .4rem; box-shadow: 0 12px 34px rgba(0,0,0,.3);
  display: flex; flex-direction: column; gap: .3rem; }
[class].ha-menu-isi > * { flex-shrink: 0; width: 100%; justify-content: flex-start; }
[class].ha-menu-isi[hidden] { display: none; }
.kol-isi.jatuh-sini { background: var(--brand-soft); border-radius: .4rem; }
.kol-kosong { padding: .6rem 0; text-align: center; }
.papan-alih { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .7rem; }
/* `touch-action:none` WAJIB di gagangnya: tanpa itu peramban memiliki gerakan
   jarinya sebagai gulir dan pointermove berhenti tiba-tiba di tengah seretan. */
.genggam { position: relative; cursor: grab; color: var(--muted); touch-action: none; padding: 0 .15rem; }
/* ⛔ SASARAN SENTUHNYA DIPERBESAR TANPA MENGGESER TATA LETAK (Seri Y · D-418 —
   laporan CEO 28 Okt: *"papan tugas saya tidak bisa drag and drop"*).
   Diukur di peramban hari itu: gagangnya **14 × 16 px**, sementara ADR-032 menuntut
   ≥±40 px. Di sentuh gagang ini SATU-SATUNYA pintu seretan — badan kartu sengaja
   tidak memulainya supaya papan tetap bisa digulir dengan jari — jadi yang terasa
   sebagai "seretannya mati" sebenarnya "sasarannya tidak bisa dikenai".
   ⚠ Pseudo-elemen, bukan padding: uji ketuk menisbatkan sentuhan pada ::after ke
   elemen induknya, jadi `closest('.genggam')` tetap menemukannya, sementara kotak
   tata letak gagangnya tidak bergeser sepiksel pun dan judul kartu tidak terdorong. */
.genggam::after { content: ''; position: absolute; left: 50%; top: 50%; width: 44px; height: 44px; transform: translate(-50%, -50%); }
.kartu-tugas.seret .genggam { cursor: grabbing; }
/* Tekan-tahan di badan kartu (pilihan CEO 28 Okt) — begitu tahannya matang, gerakan
   jari milik seretan, bukan milik gulir papan. `-webkit-touch-callout` mematikan
   menu salin bawaan yang lahir dari tahan panjang; tanpa itu menunya muncul dan
   membatalkan gerakan yang baru saja kita ambil alih. */
.kartu-tugas { -webkit-touch-callout: none; }
.kartu-tugas.siap-seret { touch-action: none; box-shadow: 0 0 0 2px var(--brand); }
/* bayangan tempat kartu akan jatuh — tanpa ini seretan tidak menjanjikan apa pun */
.bayangan-jatuh { border: 2px dashed var(--brand); border-radius: 10px; }
/* ⛔ TUMPANG TINDIH (teguran CEO 24 Okt, terlihat di kartu papan): `.aq` menaruh
   `.aql` (judul) dan `.aqm` (nomor · tenggat) pada BARIS GRID YANG SAMA — kolom 2
   dan kolom 3. Begitu judulnya memanjang jadi dua baris, meta di sebelahnya
   tetap di tengah dan keduanya saling menimpa. Di kartu papan keduanya berdiri
   sendiri-sendiri: judul satu baris penuh, meta baris berikutnya. */
.kartu-tugas > .aql,
.kartu-tugas > .aqm { grid-column: 1 / -1; }
/* ⛔ "terlalu mepet" (teguran CEO 24 Okt): `.aq` memberi jarak antarbaris hanya
   .1rem — cukup untuk kartu satu baris, sesak begitu kartunya berisi judul dua
   baris + meta + chip + dua baris tombol. Kartu papan memakai jarak & tinggi
   barisnya sendiri; nilainya DIUKUR di peramban, bukan dikira-kira. */
.kartu-tugas { gap: .3rem .7rem; padding: .7rem .8rem; }
.kartu-tugas > .aql { font-weight: 600; line-height: 1.4; }
.kartu-tugas > .aqm { margin-top: .3rem; line-height: 1.7; }
.kartu-tugas > .aqm .chip { margin-left: .25rem; }
/* ⛔ MUKA KARTU BISA DIKETUK (Seri V): seluruh kartu membuka dirinya, seperti
   kartu di papan pembanding. `user-select:none` supaya seretan tidak menyorot
   teks judulnya di tengah jalan. */
.kartu-tugas[role="button"] { cursor: pointer; user-select: none; }
.kartu-tugas[role="button"]:hover { border-color: var(--brand); }
.kartu-tugas[role="button"]:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* kaki kartu: lencana · status · avatar · ▲▼ — satu baris yang boleh membungkus */
.kartu-kaki { grid-column: 1 / -1; display: flex; align-items: center; gap: .35rem;
  flex-wrap: wrap; margin-top: .45rem; }
.kartu-kaki .kartu-orang { margin-left: auto; display: inline-flex; align-items: center; gap: .3rem; }
/* lencana = PENUNJUK isi, bukan isinya (pola ≡ di papan pembanding) */
.lencana { display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; font-size: .78rem; color: var(--muted); cursor: help; }
.lencana.bad { color: var(--bad); }
/* ⚠ `.kartu-tugas .aksi-kartu` DICABUT (Seri V): baris aksi tidak lagi berada di
   dalam kartu papan, jadi aturan itu tidak bisa menyentuh apa pun. Aturan mati
   dibiarkan hidup adalah cacat yang baru saja menghabiskan sehari di berkas ini
   (`gap` pada `display:block`): ia berbunyi seolah mengerjakan sesuatu. */
/* ⛔ JEDA IKON & TEKS (teguran CEO 24 Okt: *"kenapa tombolnya ikon dan teks tidak
   ada jeda"*, pembandingnya Trello). Sebabnya terukur, bukan selera: `.btn` bawaan
   menghitung jadi `display:block`, dan `gap` TIDAK BERLAKU pada kotak blok — jadi
   aturan jeda yang sudah tertulis di sini sejak kemarin memang tidak pernah
   mengerjakan apa pun. Inline-flex membuatnya berlaku sekaligus merapikan
   perataan vertikal glif terhadap hurufnya. */
.aksi-kartu .btn { display: inline-flex; align-items: center; justify-content: center; gap: .4rem; }
.aksi-kartu .glif { line-height: 1; }
/* ⛳ IKON DAN LABELNYA DIBERI JARAK (D-435 — laporan CEO 30 Okt: *"ikon dan teks tidak
   berspasi"*). Markupnya `<span class="glif">…</span><span class="lbl">…</span>` ditulis
   BERDEMPETAN tanpa spasi, jadi tombolnya terbaca "⭱Tambah aset". Terukur di peramban:
   `textContent` memang `"⭱Tambah aset"`.
   ⚠ Diperbaiki sebagai KELAS, bukan dengan menyisipkan spasi di tiap Blade: pola ini
   hidup di 22 tempat di 4 berkas tampilan (diukur: `</span><span class="lbl"` —
   22 kecocokan, NOL yang sudah berspasi); menambal satu per satu meninggalkan yang
   berikutnya untuk ditemukan CEO lagi.
   ⛔ Angka di baris ini pernah "6 berkas dan 28 tempat" — ditulis tanpa dihitung, kelas
   yang sama dengan larangan angka ketik tangan (ADR-056). Yang berdiri sekarang hasil
   grep, dan dijaga K285 supaya ia ikut memerah bila populasinya berubah. */
.glif + .lbl { margin-left: .4rem; }
/* ⛳ `.note.ok` DAN `.note.warn` TIDAK PERNAH PUNYA ATURAN (D-436 — ketahuan dengan
   MEMAKAI layarnya, bukan dari uji). `.note` polos berwarna PERINGATAN
   (`--warn-soft`/`--warn`), dan hanya `.info` serta `.bad` yang pernah menimpanya. Jadi
   selama ini **25 tempat** menulis `class="note ok"` (5×) atau `class="note warn"` (20×)
   lalu mendapat warna yang sama persis — pesan berhasil tampil oranye seperti
   peringatan, dan peringatan tidak bisa dibedakan dari keberhasilan.
   ⚠ `.warn` ditulis eksplisit walau nilainya sama dengan `.note` polos: tanpa barisnya,
   pembaca berikutnya tidak punya cara tahu apakah `warn` memang bawaannya atau sekadar
   kelas yang terlupa lagi. Dijaga K288 — tiap varian `note` yang dipakai layar wajib
   punya aturannya. */
/* ⛳ STRIP FAKTA DI KEPALA HALAMAN PRODUK (D-446 — permintaan CEO 30 Okt: atribut
   penting *"muncul mendampingi produk dan dapat dilihat setelah produk dibuka"*, dengan
   contoh yang ia sebut sendiri: MyAnimeList & IMDb.
   ⚠ Bentuknya DUA KOLOM label→nilai, bukan deretan keping: keping bagus untuk penanda
   satu kata (status, merek), tetapi fakta berpasangan — "Berat 85 g" tanpa kata "Berat"
   berhenti bisa dibaca. Kolom labelnya `auto`, jadi ia selebar label terpanjang dan
   nilai-nilainya sejajar; itulah yang membuat daftar seperti ini bisa DIPINDAI mata,
   bukan dibaca satu per satu. */
.fakta { display: grid; grid-template-columns: auto 1fr; gap: .2rem .7rem;
  margin: .6rem 0 0; font-size: .78rem; align-items: baseline; }
.fakta dt { color: var(--muted); white-space: nowrap; }
.fakta dd { margin: 0; color: var(--text); min-width: 0; }
.fakta dd .sku { margin-right: .3rem; }
/* ⚠ "Kompatibel" bisa sepanjang satu paragraf (daftar tipe TV). Dipangkas dua baris di
   sini; yang utuh tetap ada di tab Atribut — ringkasan yang memakan setengah layar
   berhenti jadi ringkasan. */
.fakta-panjang { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }
/* ⛳ EMPAT TOMBOL JADI SATU BARIS, dan tidak lagi menempel pada fakta di atasnya
   (laporan CEO: *"terlalu mepet dengan informasi produk diatasnya"*). Jaraknya hanya
   dipasang pada tata letak sempit, sebab di layar lebar kolom aksi berdiri di SEBELAH
   teksnya — di sana tidak ada yang berdempetan. */
@media (max-width: 1100px) {
  .page-head > .head-actions { margin-top: .75rem; }
}
.aksi-lain .pmenu button { text-align: left; }
/* ⛔ BILAH 3px "SEDANG BEKERJA" (D-444) **DICABUT 1 Nov 2026 — keputusan CEO D-464**:
   *"Jangan pakai bar atas, kurang jelas dan tidak cocok, ini data bukan video"* dan
   *"Belum diterapkan keseluruh halaman dan tab, atau popup/modal."* Penggantinya BALOK
   ABU di kedua tempat: berpindah layar (D-463) dan memperbarui isi di dalam layar
   (`.km-lapis`, D-464). Selektor `.sibuk` & `.sibuk-isi` sengaja dihapus, bukan
   disembunyikan — gaya mati yang ditinggal akan terbaca sesi berikutnya sebagai pilihan
   yang masih hidup.
   ⚠ Yang TETAP: kalimat untuk pembaca layar. Baloknya bicara pada mata lewat bentuk,
   jadi tanpa baris ini perpindahan & pembaruan menjadi senyap bagi mereka. Tidak memakai
   `display:none`, sebab itu menyembunyikannya dari pembaca layar juga. */
.sibuk-teks { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; }
/* ⛳ PENANDA "SEDANG MENGHITUNG" YANG BERGERAK (D-443 — laporan CEO 30 Okt:
   *"loading lama seperti lag namun tidak ada animasinya"*).
   ⛔ Diamnya itulah cacatnya, bukan hanya lamanya: menunggu TANPA tanda terbaca sebagai
   aplikasi macet, lalu orangnya mengetuk berulang kali dan tiap ketukan menambah
   pekerjaan yang membuatnya makin lambat. Titik berdenyut memakai gerak, sebab mata
   menangkap gerak jauh sebelum membaca kata.
   ⚠ `prefers-reduced-motion` dihormati: yang dimatikan GERAKNYA, bukan penandanya —
   kalimatnya tetap berdiri supaya yang mematikan animasi tidak ikut kehilangan
   kabarnya. */
/* ⛳ PANEL TAB MASUK DENGAN LEMBUT (D-443 — permintaan CEO: *"agar saat buka riwayat
   harian bisa smooth"*). `.screen.on` sudah punya animasi masuk sejak awal; `.tabpane.on`
   tidak pernah punya, jadi isinya muncul mendadak dan perpindahan tab terasa menyentak.
   ⚠ Memakai keyframe `fade` YANG SUDAH ADA dan pagar `prefers-reduced-motion` yang sama
   — bukan mekanisme kedua yang harus diingat orang. Berlaku untuk SELURUH tab di
   aplikasi, sebab menyentaknya bukan sifat layar Scan Resi melainkan sifat kelasnya. */
@media (prefers-reduced-motion: no-preference) {
  .tabpane.on { animation: fade .18s ease; }
}
.memuat::after { content: "●●●"; letter-spacing: .12em; margin-left: .35rem;
  animation: memuat-denyut 1.1s ease-in-out infinite; }
@keyframes memuat-denyut { 0%, 100% { opacity: .25 } 50% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) {
  .memuat::after { animation: none; opacity: .7; }
}
.note.ok { background: var(--ok-soft); color: var(--ok); }
.note.warn { background: var(--warn-soft); color: var(--warn); }
/* ⛳ PERBESAR FOTO (D-441 — permintaan CEO 30 Okt: *"agar orang yang kurang jelas
   melihat fotonya bisa klik fotonya lalu diperbesar"*).
   ⚠ Penanda bahwa fotonya BISA diketuk hanya dipasang saat penandanya ada, jadi produk
      tanpa foto tidak memakai kursor kaca pembesar yang menjanjikan sesuatu yang tidak
      terjadi. Kotaknya sendiri 140×140 (96 di layar sempit) — jauh di atas 40px ADR-032,
      jadi tidak perlu sasaran tambahan. */
.prod-photo[data-zoom-foto] { cursor: zoom-in; }
.prod-photo[data-zoom-foto]:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
/* Lapisannya memakai `.modal-veil` yang sudah ada (Esc · ketukan latar · `.vclose`
   berlaku tanpa baris baru); yang ditambahkan di sini hanya cara gambarnya mengisi
   layar. `min()` dipakai supaya di ponsel ia dibatasi layar, bukan oleh angka tetap. */
/* ⛔ LATARNYA LEBIH PEKAT daripada dialog biasa, dan itu temuan dari MELIHATNYA di
   390px, bukan dari angka: `.modal-veil` memakai `rgba(10,16,23,.55)` — cukup untuk
   dialog berkartu buram, tetapi di sini isi halaman menembus keterangan fotonya
   sehingga dua teks bertumpuk dan keduanya sulit dibaca. Ia juga ditengahkan tegak
   (bawaannya menempel ke atas), supaya keterangan & tombol Tutup tidak mendarat di
   tengah isi halaman yang masih terlihat. */
.modal-veil.zoomfoto { background: rgba(10, 16, 23, .93); align-items: center; padding: 1rem; }
.zoomfoto-isi { margin: 0; display: flex; flex-direction: column; gap: .6rem;
  align-items: center; max-width: min(96vw, 1400px); }
.zoomfoto-isi img { max-width: min(96vw, 1400px); max-height: 82vh;
  width: auto; height: auto; object-fit: contain; border-radius: 10px;
  background: var(--surface); }
.zoomfoto-isi figcaption { display: flex; gap: .8rem; align-items: center;
  flex-wrap: wrap; justify-content: center; color: #fff; font-size: .82rem; }
/* ⚠ Nama produk di sini bisa SANGAT panjang — terukur 104 aksara pada produk pertama
   yang dicoba, yang di 390px memakan tiga baris dan mendorong tombol Tutup turun.
   Dipangkas dua baris; namanya utuh tetap terbaca di judul halaman di belakangnya,
   jadi tidak ada fakta yang hilang. */
.zoomfoto-isi figcaption > span { display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden; max-width: min(86vw, 900px); }
/* Tombol tutupnya tetap ≥40px di sentuh — di layar kecil ia satu-satunya jalan keluar
   yang terlihat, sebab "ketuk latar" tidak pernah terbaca sebagai tombol oleh siapa pun. */
.zoomfoto-isi .vclose { min-height: 40px; }
/* ⛳ RIWAYAT HARIAN SCAN RESI DI LAYAR SEMPIT — tiap nilai menyebut namanya sendiri
   (D-436, terukur di 390px dengan memakai layarnya).
   `table.tring` berubah jadi kartu di bawah 900px, tetapi labelnya hanya muncul pada
   sel `c-num`/`c-num2` — dua sel, sementara tabel ini punya TIGA kolom nilai. Tanpa
   baris di bawah, kartunya berbunyi "2026-10-30 / 0 / 10:27 · Kuncoro / impor belum
   mencakup hari ini / —": angka 0 dan tanda — berdiri tanpa ada yang menyebut mereka
   angka apa. ⚠ Tidak dipaksakan ke pola `c-num` yang ada sebab pola itu menaruh
   nilainya di kolom kanan baris 1 & 2 — muat untuk DUA angka, dan yang ketiga akan
   tetap telanjang. */
@media (max-width: 900px) {
  #tbRiwayatHari td::before { content: attr(data-l); display: block;
    font-size: .68rem; color: var(--muted); font-weight: 400; margin-bottom: .1rem; }
  #tbRiwayatHari td:first-child::before { content: none; }
}
/* ⛳ ANGKA TEBAL DI DALAM KEPING & TOMBOL DIBERI JARAK SENDIRI (D-435 — laporan CEO:
   *"tombol lihat semua33berkas angkanya juga tidak berspasi"*).
   ⚠ Saya TIDAK bisa mereproduksinya: markup server maupun tulisan-ulang JS keduanya
   berspasi, dan di peramban saya terbaca `"Lihat semua 52 berkas"`. Jadi alih-alih
   mengaku menemukan sebabnya, jaraknya dibuat TIDAK BERGANTUNG pada simpul spasi —
   apa pun yang memakannya di perangkat sana (penerjemah halaman, normalisasi teks),
   jaraknya tetap ada. */
.aset-lagi-baris b, .aset-saring .chip b { margin: 0 .22rem; }
/* ⛳ SELECT MENGIKUTI TEMA (D-435 — laporan CEO: *"pada halaman papan tugas sortir
   semua orang tidak menggunakan style yang serupa"*). `.inp` menyetel latar & bingkai
   tetapi TIDAK menyetel `color`, dan pada `<select>` gaya bawaan sistem menang — di
   Android ia tampil kotak PUTIH di tengah layar gelap. Ditetapkan di sini, berikut
   `option`-nya: daftar yang terbuka ikut gelap, bukan putih menyilaukan. */
select.inp, select.inp option { color: var(--text); background-color: var(--surface); }
/* ⚠ Bingkainya ditetapkan terpisah: `.inp` sudah menulis `border:1px solid var(--border)`,
   tetapi pada `<select>` terukur tetap abu-abu bawaan sistem **rgb(118,118,118)** —
   warna latar ikut, warna bingkai tidak. Ditulis eksplisit supaya ia sekeluarga dengan
   kotak cari di sebelahnya. */
select.inp { border: 1px solid var(--border); }
/* ▲▼ berdampingan (permintaan CEO 24 Okt): sepasang tombol rapat yang tidak
   pernah terpisah baris saat baris aksinya membungkus. */
.geser-pasangan { display: inline-flex; }
.geser-pasangan .btn.geser { padding-left: .42rem; padding-right: .42rem; }
.geser-pasangan .btn:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.geser-pasangan .btn:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; }
/* ⛔ ATURAN PENYUSUTAN LABEL DICABUT (Seri V) — dan pencabutannya penting untuk
   dicatat, bukan sekadar dilakukan: baris aksi TIDAK LAGI berada di dalam kolom
   papan, jadi container query pada `.kol-isi` tidak pernah lagi menyentuhnya.
   Membiarkannya akan menyisakan aturan yang berbunyi seolah mengerjakan sesuatu
   padahal tidak — persis kelas cacat yang baru saja ketahuan pada `gap` di
   `display:block`, yang tertulis rapi selama sehari penuh tanpa pernah berlaku. */
/* avatar sebagai pengganti NAMA (perintah CEO 24 Okt: hemat tempat). Bawaannya
   lingkaran berinisial — tidak ada berkas bawaan yang perlu disalin per akun. */
.avatar-kecil { width: 22px; height: 22px; font-size: .62rem; display: inline-flex;
  vertical-align: middle; background-size: cover; background-position: center; }
.avatar-kecil[data-foto] { color: transparent; }
.avatar-mini { width: 18px; height: 18px; font-size: .55rem; }
.kartu-tugas.seret { opacity: .45; }
/* di layar sempit kolom memenuhi lebar layar — satu kolom per pandangan, digulir.
   ⛔ D-409 (temuan CEO sesudah u40: *"tombol tambah kartu melayang tidak mepet"*,
   kolom Operasional & Gudang lebih sempit daripada Marketing): aturan lama
   `flex: 0 0 min(300px, 86vw)` ditulis untuk wadah arah BARIS, padahal di bawah
   901px wadahnya arah KOLOM (`#kolomDivisi { flex-direction: column }`). Di arah
   itu basis flex berlaku pada TINGGI — kolom berisi sedikit dipaksa setinggi
   300px (terukur: isi 298px → kotak 300px, tombol tambah menggantung di atas
   ruang kosong) — sementara lebarnya jatuh ke `fit-content` karena wadahnya
   `align-items: flex-start` (terukur: 147px saat isinya disembunyikan, 363px saat
   penuh), jadi lebar kolom berayun mengikuti panjang judul kartunya.
   `width: 100%` memaku lebarnya; `flex: 0 0 auto` mengembalikan tinggi ke isinya. */
@media (max-width: 900px) {
  .papan-baris > .kol-divisi { flex: 0 0 auto; width: 100%; }
  .kol-isi { max-height: none; }
}
/* display kolom TIDAK boleh ditulis sebaris di markup — gaya sebaris mengalahkan
   aturan media di bawah, dan itulah cacat yang tertangkap mata 23 Okt: kedua kolom
   tetap tampil di 390px. Aturan medianya menang karena ditulis BELAKANGAN pada
   spesifisitas yang sama. Dijaga K233 pada markupnya. */
#kolomDivisi [data-divisi] { display: flex; flex-direction: column; gap: .5rem; }
@media (max-width: 900px) {
  .pilih-divisi { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .6rem; }
  #kolomDivisi { flex-direction: column; }
  #kolomDivisi > [data-divisi]:not(.kol-divisi) { display: none; }
  #kolomDivisi > [data-divisi].aktif:not(.kol-divisi) { display: flex; }
}

/* ===== Seri V — DI DALAM KARTU =====
   Keterangan pekerjaan ditulis & dibaca dengan barisnya UTUH: ia berisi langkah
   dan tautan, dan merapatkannya jadi satu paragraf membuang justru strukturnya. */
.teks-panjang { white-space: pre-wrap; line-height: 1.6; }
.inp.teks-isian { min-height: 5.2rem; white-space: pre-wrap; text-align: left;
  overflow-y: auto; max-height: 14rem; }
.kartu-rincian { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  font-size: .88rem; color: var(--muted); }
.kartu-rincian b { color: var(--text); }

/* ===== W1 — CEKLIS DI DALAM KARTU (§14.3) =====
   Bentuknya mengikuti papan proyek yang CEO pakai sendiri: nama ceklis, cacah a/b,
   bilah persen, butir bercoretan saat selesai. Butirnya boleh berisi TAUTAN panjang,
   jadi barisnya membungkus — bukan dipotong dengan ellipsis yang menyembunyikan
   bagian yang justru dibutuhkan (alamat listing). */
.ceklis { border: 1px solid var(--border); border-radius: 10px; padding: .6rem .7rem; }
.ceklis + .ceklis { margin-top: .6rem; }
.ceklis-kepala { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; margin-bottom: .45rem; }
.ceklis-bilah { flex: 1 1 90px; min-width: 70px; height: 6px; border-radius: 99px;
  background: var(--border); overflow: hidden; }
.ceklis-bilah > i { display: block; height: 100%; background: var(--brand); transition: width .2s ease; }
.ceklis-bilah > i.penuh { background: var(--ok); }
.ceklis-butir { display: flex; align-items: flex-start; gap: .5rem; padding: .3rem .1rem;
  min-height: 40px; cursor: pointer; }
.ceklis-butir input[type="checkbox"] { margin-top: .25rem; width: 17px; height: 17px; flex: none; }
.ceklis-butir.sudah > .teks-panjang { text-decoration: line-through; color: var(--muted); }
.ceklis.sembunyi-selesai .ceklis-butir.sudah { display: none; }
.ceklis-butir .butir-buang { margin-left: auto; opacity: .45; }
.ceklis-butir:hover .butir-buang { opacity: 1; }
.ceklis-tambah { display: flex; gap: .4rem; align-items: center; margin-top: .4rem; }
.ceklis-tambah > .inp { flex: 1; min-width: 0; }

/* ===== W2 — KOMENTAR & LINIMASA (§14.4) ===== */
.komentar-tulis { display: flex; gap: .4rem; align-items: flex-start; margin-bottom: .7rem; }
.komentar-tulis > .inp { flex: 1; min-width: 0; min-height: 2.6rem; }
.linimasa-baris { display: flex; gap: .5rem; align-items: flex-start; padding: .45rem 0; }
.linimasa-baris + .linimasa-baris { border-top: 1px solid var(--border); }
.linimasa-baris > div { min-width: 0; }

/* ===== W3–W6 — LAMPIRAN · LABEL · SAMPUL · SARINGAN ===== */
.papan-saring { gap: .45rem; }
.papan-saring .search { flex: 1 1 200px; min-width: 150px; }

/* Label: palet TETAP delapan warna (§14.6) — bukan pilih-warna-bebas, supaya dua
   label tidak pernah lahir hampir sewarna sehingga tak bisa dibedakan sekilas. */
.lb { display: inline-block; width: 26px; height: 6px; border-radius: 99px; }
.lb-merah { background: #e5484d; } .lb-jingga { background: #f76b15; }
.lb-kuning { background: #e2b203; } .lb-hijau { background: #46a758; }
.lb-toska { background: #12a594; } .lb-biru { background: #3b82f6; }
.lb-ungu { background: #8e4ec6; } .lb-kelabu { background: #8b8d98; }
.label-bilah { grid-column: 1 / -1; display: flex; gap: .25rem; margin-bottom: .2rem; }
.chip.lb-chip { color: #fff; border: 0; }
.chip.lb-chip.lb-kuning { color: #332b00; }
.lb-lepas, .lb-buang { background: none; border: 0; color: inherit; cursor: pointer; padding: 0 .15rem; }
.lb-pilih { position: relative; display: inline-block; }
.lb-pilih > summary { list-style: none; cursor: pointer; }
.lb-pilih > summary::-webkit-details-marker { display: none; }
.lb-panel { position: absolute; z-index: 40; top: calc(100% + .3rem); left: 0; min-width: 230px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: .35rem;
  box-shadow: 0 10px 30px rgba(0,0,0,.25); }
.lb-baris { display: flex; align-items: center; gap: .45rem; width: 100%; min-height: 40px;
  background: none; border: 0; color: inherit; cursor: pointer; padding: .3rem .35rem; border-radius: 8px; text-align: left; }
.lb-baris:hover { background: var(--brand-soft); }
.lb-baru { display: flex; gap: .3rem; align-items: center; margin-top: .35rem; border-top: 1px solid var(--border); padding-top: .35rem; }
.lb-baru > .inp { flex: 1; min-width: 0; }

/* Sampul kartu (W6): strip pendek supaya papan tetap ramping — hanya kartu yang
   memang diberi sampul yang bertambah tinggi. */
.kartu-sampul { grid-column: 1 / -1; display: block; height: 72px; border-radius: 8px;
  background-size: cover; background-position: center; margin-bottom: .35rem; }

/* Lampiran */
.lampiran-baris { display: flex; align-items: center; gap: .5rem; padding: .35rem 0; min-height: 44px; }
.lampiran-baris + .lampiran-baris { border-top: 1px solid var(--border); }
.lampiran-gambar { width: 44px; height: 44px; object-fit: cover; border-radius: 6px; display: block; }
.lampiran-glif { width: 44px; text-align: center; color: var(--muted); }
.lampiran-isi { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.lampiran-tambah { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; margin-top: .4rem; }
.lampiran-tambah > .inp { flex: 1 1 140px; min-width: 0; }

.anggota-chip { display: inline-flex; align-items: center; gap: .3rem; }
.kartu-kaki .kartu-orang > .avatar-kecil + .avatar-kecil { margin-left: -5px; }

/* ===== SERI X — DI DALAM KARTU: POPOVER & LEMBAR PENUH (§15.3–§15.5) =====
   Teguran CEO sesudah memakai u37 di ponsel: *"tampilan di hp sangat tidak
   responsif"*. Sebabnya bukan CSS melainkan MODEL — semua kemampuan dirender sebagai
   bagian permanen, jadi kartu kosong memikul tujuh blok formulir. Markup-nya kini
   kondisional; yang di sini menyelesaikan sisanya: wadah popover dan lembar penuh. */

.pop-bungkus { position: relative; display: inline-flex; }
.pop-panel { position: absolute; z-index: 60; top: calc(100% + .3rem); left: 0;
  min-width: 240px; max-width: calc(100vw - 32px);
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  padding: .45rem; box-shadow: 0 12px 34px rgba(0,0,0,.3);
  display: flex; flex-direction: column; gap: .35rem; }
/* ⛔ Anaknya kebal dimampatkan — KELAS yang sama dengan `.kol-isi` (K330), dan di
   sini justru menggigit di PONSEL: pada ≤900px panel ini jadi lembar bawah
   ber-`max-height:70dvh; overflow:auto` (Y-3/D-406) sementara ia tetap flex kolom
   dan seluruh anaknya ISI — tidak ada anak yang bertugas menggulir. Popup yang
   isinya melebihi 70dvh akan memampatkan judul, keterangan, dan baris tombolnya
   lebih dulu, dan isi yang terdesak tumpah keluar panel. Ditemukan penjaga K330,
   bukan oleh mata — wadah kedua yang bentuknya sama persis. */
.pop-panel > * { flex-shrink: 0; }
/* ⚠ Dipepetkan ke tepi KANAN saat wadahnya berada di separuh kanan dialog —
   popover yang keluar layar di 390px adalah popover yang tidak bisa dipakai. */
.kartu-rincian > .pop-bungkus:last-child .pop-panel,
.pop-bungkus[style*="margin-left:auto"] .pop-panel { left: auto; right: 0; }
.pop-judul { font-weight: 600; font-size: .86rem; padding: .1rem .2rem .2rem; }
.pop-pisah { border-top: 1px solid var(--border); margin: .2rem 0; }
.pop-aksi { display: flex; gap: .4rem; flex-wrap: wrap; }
.pop-baris { display: flex; align-items: center; gap: .5rem; width: 100%;
  min-height: 42px; background: none; border: 0; color: inherit; cursor: pointer;
  padding: .35rem .4rem; border-radius: 8px; text-align: left; }
.pop-baris:hover { background: var(--brand-soft); }
.pop-baris > span { display: flex; flex-direction: column; min-width: 0; }
.pop-baris i { font-style: normal; font-size: .78rem; color: var(--muted); }
.pop-panel .field { display: flex; flex-direction: column; gap: .2rem; font-size: .84rem; }

/* palet label: warna dipilih dengan MATA, bukan diterjemahkan dari nama di dropdown */
.lb-palet { display: flex; flex-wrap: wrap; gap: .3rem; }
.lb-warna { width: 34px; height: 24px; border-radius: 6px; border: 2px solid transparent; cursor: pointer; }
.lb-warna.pilih { border-color: var(--text); }

/* baris tombol "tambahkan" di dalam kartu */
.kartu-tambah-baris { display: flex; flex-wrap: wrap; gap: .35rem; }
.kartu-tambah-baris .btn { display: inline-flex; align-items: center; gap: .35rem; }

/* keterangan kosong = ajakan menulis yang bisa diketuk, bukan blok formulir */
.ket-kosong { display: block; width: 100%; text-align: left; min-height: 44px;
  background: none; border: 1px dashed var(--border); border-radius: 8px;
  color: var(--muted); padding: .6rem .7rem; cursor: pointer; }
.ket-kosong:hover { border-color: var(--brand); color: var(--text); }
.ceklis-buka-butir { margin-top: .35rem; }

/* ⛔ SUNTING DI TEMPAT (Seri Y · D-419 — keputusan CEO 28 Okt: judul & keterangan
   ditulis DI DALAM popup kartu, bukan lewat dialog kedua).
   ⚠ `.ket-isian` MENGEMBANG di tempat teksnya dan tidak memakai `.pop-panel`:
   keterangan kartu bisa belasan baris, dan panel melayang selebar 260px yang menutupi
   kartunya sendiri justru bentuk yang dikeluhkan. */
/* ⛔ UBIN VARIAN BISA DIKETUK DAN MENAMAI DIRINYA (D-423 — laporan CEO 28 Okt:
   *"pada foto produk ada 3 file, tapi tidak bisa diklik"*).
   ⚠ Cacat lamanya berlapis dua: ubinnya `<div>` polos **yang sudah ber-`cursor:pointer`**
   — jadi ia MENGAKU bisa diketuk sementara tidak melakukan apa pun, kelas "tombol yang
   berbohong" (ADR-202 · D-241) — dan barisnya terbaca sebagai "3 berkas foto produk ini"
   padahal tiap ubin adalah sampul SKU BERSAUDARA. Label SKU di bawah ubin menutup salah
   baca itu pada sebabnya, bukan pada gejalanya. */
/* ⛳ FOTO DI KEPALA HALAMAN KEMBALI 1:1 (D-433 — permintaan CEO 30 Okt: *"foto produk
   di atas itu rasionya 1:1 agar foto produk terlihat jelas"*).
   ⛔ Sebabnya bukan ukuran yang salah ditulis, melainkan aturan TETANGGA:
   `.page-head > div:first-child{flex:1 1 240px}` dimaksudkan untuk kolom teks, tetapi
   `.prod-photo` yang berdiri pertama di DOM — jadi ia yang melar. Terukur sebelum
   diperbaiki: **610 × 84 px, rasio 7,26**, padahal `.prod-photo` sendiri menulis
   110×110. Yang diperbaiki pemenangnya, bukan angkanya. */
/* ⚠ Pemilihnya `div.prod-photo:first-child` (0-3-1), bukan `.prod-photo` (0-2-0):
   aturan lama `.page-head > div:first-child` berbobot 0-2-1 dan MENANG atas pemilih
   yang lebih pendek. Terukur pada percobaan pertama saya: rasionya memang jadi 1,00
   tetapi ukurannya **610 × 610** — lebarnya tetap melar, dan tingginya ikut. */
.page-head > div.prod-photo:first-child { flex: 0 0 auto; width: 140px; height: 140px; aspect-ratio: 1 / 1; }
/* ⛳ DI PONSEL, ISINYA BERDAMPINGAN DENGAN FOTO — bukan bertumpuk (D-434, usul CEO
   30 Okt: *"untuk tampilan di mobile kenapa tombol, judul, dan seluruh informasinya
   dipindah ke samping foto seperti di desktop agar tidak memakan tempat?"*).
   ⛔ Terukur sebelum diubah pada 375px: kepala halaman **408 px — 50% tinggi layar** —
   karena ketiga anaknya membungkus jadi tiga baris penuh (foto 104 · judul 186 ·
   tombol 88), padahal di samping foto masih tersisa 255 px yang menganggur.
   ⚠ Dibuat dengan GRID, bukan dengan mencabut `flex-wrap`: ketiga anaknya bersaudara
   (foto · judul · aksi), jadi tanpa grid tombolnya akan jadi kolom KETIGA yang sempit.
   Di sini foto memegang kolom 1 sepanjang semua baris, sisanya menumpuk di kolom 2. */
/* ⛔ AMBANGNYA 1100px, DAN ANGKA ITU DIUKUR — bukan dipilih, bukan disalin dari ADR-014.
   Tinggi kepala halaman pada SKU berjudul panjang (`KT-LG-733`), tata letak lebar:
   900px → **315** · 1000px → **300** · 1150px → **191** · 1350px → **191**. Jadi tata
   letak lebar baru benar-benar berhenti bertumpuk di atas ±1.100px; di bawah itu ia
   membungkus dan memakan tinggi, berapa pun perangkatnya.
   ⚠ DUA tebakan saya sebelumnya keliru di sini, dan keduanya ketahuan dari tangan CEO
   atau dari pengukuran, bukan dari uji: pertama **520px** (angka yang saya karang) —
   *"di hp dan tablet belum berubah"*, sebab perangkatnya lebih lebar dari itu; lalu
   **820px** (ambang sidebar milik aplikasi) — terdengar berdasar, tetapi 900px pun masih
   bertumpuk. Satu lebar yang diuji bukan bukti untuk seluruh layar kecil.
   ⚠ FLOAT, bukan grid: dengan grid foto memegang satu kolom penuh sehingga ruang di
   BAWAH foto menganggur. Keduanya DIUKUR pada halaman yang sama (375px, KP-MTX-PVR):
   bertumpuk **408** → grid **316** → float **301** px. Selisih grid→float hanya 15 px,
   jauh lebih kecil daripada dugaan saya sebelum mengukur. */
@media (max-width: 1100px) {
  .page-head { display: block; }
  .page-head > div.prod-photo:first-child { float: left; width: 120px; height: 120px; margin: 0 .8rem .5rem 0; }
  .page-head > .head-actions { clear: none; }
  .page-head::after { content: ""; display: block; clear: both; }
}
@media (max-width: 520px) {
  .page-head > div.prod-photo:first-child { width: 96px; height: 96px; margin-right: .7rem; }
}
.thumbrow { align-items: flex-start; flex-wrap: wrap; }
.thumbrow .thumb { width: auto; height: auto; flex-direction: column; gap: .25rem;
  border: none; background: none; padding: 0; text-decoration: none; color: inherit; }
.thumb-muka { width: 56px; height: 56px; border-radius: 8px; overflow: hidden;
  border: 2px solid var(--border); background: linear-gradient(150deg, var(--brand-soft), var(--surface-2));
  display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }
.thumbrow .thumb.on .thumb-muka { border-color: var(--brand); }
.thumbrow .thumb:hover .thumb-muka { border-color: var(--brand); }
.thumbrow .thumb:focus-visible .thumb-muka { outline: 2px solid var(--brand); outline-offset: 2px; }
.thumb-muka img { width: 100%; height: 100%; object-fit: cover; }
/* nama SKU dipotong ber-ellipsis — terpotong SENYAP adalah cacat tersendiri */
.thumb-sku { max-width: 72px; font-size: .68rem; line-height: 1.2; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center; }
.thumbrow .thumb.on .thumb-sku { color: var(--text); font-weight: 600; }

/* ⛔ GALERI ASET PRODUK (Seri AC · D-422). Kisi yang memilih sendiri jumlah kolomnya,
   jadi satu kolom di ponsel dan beberapa di layar lebar tanpa lebar yang dipatok. */
/* ⛳ DIPERKECIL D-428 (keluhan CEO 29 Okt: *"galeri terlalu panjang ke bawah, apakah bisa
   dibuat lebih ringkas agar tidak memakan ruang"*). 150px → 104px. ⚠ 104px adalah batas
   bawah yang masih menyisakan sasaran sentuh ≥40px untuk tiga tombol aksinya (ADR-032);
   lebih kecil lagi dan ubinnya berhenti bisa dipakai di ponsel. */
.aset-kisi { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: .5rem; }
/* ⛔ `display:none`, BUKAN `visibility`/`opacity`: hanya yang pertama membuat peramban
   tidak mengunduh gambarnya sama sekali. Itu separuh dari jawaban "tidak memberatkan";
   separuh lainnya ubin memakai `lampiran-kecil`, bukan gambar aslinya. */
.aset-kisi .aset-ubin.aset-lebih { display: none; }
.aset-kisi.aset-semua .aset-ubin.aset-lebih { display: flex; }
.aset-ubin.aset-tersaring { display: none !important; }
/* ⛳ Aksi per berkas hanya saat "Kelola berkas" ditekan (D-428). Terukur: barisnya
   membungkus jadi 86px di ubin sempit, dan itulah yang membuat galeri panjang —
   bukan fotonya. Saat tampil, tombolnya tetap ≥40px (ADR-032). */
.aset-ubin .aset-aksi { display: none; }
.aset-kisi.aset-kelola .aset-ubin .aset-aksi { display: flex; }
.aset-saring { display: flex; gap: .3rem; flex-wrap: wrap; margin-left: .6rem; }
/* ⚠ Keping saringan adalah kontrol yang DITEKAN, jadi ia tunduk pada ADR-032 — terukur
   29px pada rancangan pertama saya dan dinaikkan ke 40px. Keping berlabel di layar lain
   hanya penanda, bukan tombol; itu sebabnya aturan ini disempitkan ke `.aset-saring`. */
/* ⛳ Warna DITETAPKAN, tidak diwariskan (D-429 — laporan CEO 29 Okt: *"sortir semua,
   hasil dan bahan warna fontnya terlalu gelap"*). `.chip` dasar sengaja tidak menyetel
   `color`, jadi keping di kepala kartu mewarisi warna teks REDUP di sekitarnya dan
   terbaca seperti tulisan mati — padahal ia tombol. */
.aset-saring .chip { cursor: pointer; font: inherit; color: var(--text);
  background: var(--surface-2); border-color: var(--border);
  min-height: 40px; display: inline-flex; align-items: center; }
.aset-saring .chip:hover { border-color: var(--brand); }
/* ⚠ `--brand`, BUKAN `--pri`: yang kedua tidak pernah didefinisikan di tema ini, dan
   nilai cadangan di dalam `var()` TIDAK menyelamatkannya — ia menyamarkan token hantu
   sehingga keping terpilih kehilangan warna temanya diam-diam. Ditangkap sapuan
   paritas (kelas temuan CEO 25 Okt), bukan oleh mata saya. */
/* ⚠ `#fff`, BUKAN `--brand-text`: yang kedua adalah teks BERWARNA MEREK untuk latar
   netral, bukan teks DI ATAS merek. Terukur saat dicoba — keping terpilih memulangkan
   kontras **1,62** (biru muda di atas biru), nyaris tak terbaca, padahal keping lainnya
   12,49. Nilainya disamakan dengan `.btn.pri` yang memang sudah `color:#fff` di atas
   `--brand`, supaya tombol utama dan keping terpilih tidak pelan-pelan berbeda. */
.aset-saring .chip.on { background: var(--brand-soft); border-color: var(--brand); color: var(--brand-text); }
.aset-lagi-baris { display: flex; justify-content: center; margin-top: .6rem; }
/* ⛳ Di layar sempit `auto-fill` hanya memuat DUA kolom (terukur 375px: 12 ubin = 963 px,
   lebih panjang daripada di layar lebar). Dipatok tiga kolom supaya galeri di ponsel
   ikut memendek — di situlah CEO memeriksanya. */
@media (max-width: 520px) { .aset-kisi { grid-template-columns: repeat(3, 1fr); } }
.aset-ubin { margin: 0; display: flex; flex-direction: column; gap: .4rem;
  border: 1px solid var(--border); border-radius: 10px; padding: .5rem; }
/* rasio tetap: ubin yang tingginya mengikuti gambar membuat kisinya bergerigi */
.aset-muka { display: block; aspect-ratio: 1 / 1; border-radius: 8px; overflow: hidden;
  background: var(--surface-2, rgba(127, 127, 127, .12)); display: grid; place-items: center; }
.aset-muka img { width: 100%; height: 100%; object-fit: cover; }
.aset-video { font-size: 1.8rem; color: var(--muted); }
/* nama berkas dipotong dengan ellipsis — terpotong SENYAP adalah cacat tersendiri */
.aset-ubin figcaption { display: flex; flex-direction: column; min-width: 0; }
.aset-nama { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .82rem; }
.aset-aksi { display: flex; gap: .3rem; flex-wrap: wrap; }
/* sasaran sentuh ≥40px (ADR-032) — pelajaran D-418: kontrol kecil di dalam kartu
   adalah persis yang luput dari sumbu responsif 390px */
.aset-aksi .btn { min-height: 40px; min-width: 40px; justify-content: center; }

/* ⛔ KARTU SUMBER MASUK (Seri Z · D-420). Kisi yang memilih sendiri jumlah kolomnya —
   satu kolom di ponsel, dua sampai tiga di layar lebar — jadi tidak ada lebar yang
   dipatok dan tidak ada gulir mendatar (ADR-032 · ADR-262). */
.sumber-kisi { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: .7rem; }
.sumber-kartu { display: flex; flex-direction: column; gap: .55rem;
  border: 1px solid var(--border); border-radius: 10px; padding: .7rem .8rem; }
.sumber-kartu.warn { border-color: var(--warn); }
.sumber-kartu.bad { border-color: var(--bad); }
.sumber-kepala { display: flex; align-items: flex-start; gap: .6rem; }
/* glif sekeluarga dengan ikon aplikasi — monokrom, mengikuti warna teks redup */
.sumber-glif { font-size: 1.35rem; line-height: 1.1; color: var(--muted); flex: 0 0 auto; }
.sumber-nama { min-width: 0; }
.sumber-vonis { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
/* ⚠ tiap angka MEMBAWA namanya sendiri: itu justru yang hilang saat tabel runtuh */
.sumber-fakta { display: flex; flex-direction: column; gap: .25rem; margin: 0;
  border-top: 1px solid var(--border); padding-top: .5rem; }
.sumber-fakta > div { display: flex; align-items: baseline; justify-content: space-between; gap: .6rem; }
.sumber-fakta dt { font-size: .78rem; color: var(--muted); margin: 0; }
.sumber-fakta dd { margin: 0; font-size: .85rem; text-align: right; }
.sumber-aksi { display: flex; justify-content: flex-end; }

.ket-isian { display: flex; flex-direction: column; gap: .5rem; }
.ket-isian .inp.teks-isian { min-height: 7rem; }
.kartu-judul-isian { display: flex; flex-direction: column; gap: .5rem; }
/* judul yang bisa diketuk MENGAKU bisa diketuk — sasarannya pun ≥40px (ADR-032) */
.kd-sunting { cursor: pointer; border-radius: 6px; padding: .2rem .35rem; margin: -.2rem -.35rem;
  min-height: 40px; display: inline-flex; align-items: center; }
.kd-sunting:hover { background: var(--brand-soft); }
.kd-sunting:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ⛔ LEMBAR PENUH DI PONSEL (§15.5). Dialog kartu di 390px dulu jadi gulungan
   ±2.000px di dalam modal yang masih bertepi — dua lapis gulir sekaligus. Di bawah
   641px ia mengambil seluruh layar dengan kepala yang LENGKET, jadi judul & tombol
   tutup tidak pernah hilang entah sejauh apa isinya digulir. */
.kartu-lembar { max-width: 640px; }
/* D-409: gulir yang mentok di ujung lembar tidak diteruskan ke halaman di
   belakangnya (rantai gulir). Berlaku di semua lebar, dan tidak menggeser apa pun. */
.modal-veil { overscroll-behavior: contain; }
.kartu-isi { display: flex; flex-direction: column; gap: .9rem; }
@media (max-width: 640px) {
  /* ⛔ `align-items: stretch` DICABUT (D-408, temuan CEO 25 Okt sesudah u39): ia
     memaksa tinggi lembar = tinggi LAYAR (844px), sementara isinya 1156px — 312px
     terakhir (komentar bawah + aksi status) berada DI LUAR kotak berlatar dan
     menembus ke tirai semi-transparan, sehingga papan di belakang terlihat
     tumpang tindih. `flex-start` membuat tingginya mengikuti ISI; `min-height`
     di bawah tetap menjamin satu layar penuh saat isinya pendek. */
  .modal-veil:has(.kartu-lembar) { padding: 0; align-items: flex-start; }
  .kartu-lembar { max-width: 100%; width: 100%; margin: 0; border-radius: 0;
    min-height: 100dvh; border-left: 0; border-right: 0; }
  .kartu-lembar-kepala { position: sticky; top: 0; z-index: 70; background: var(--surface); }
  .kartu-isi { gap: .55rem; }
  .kartu-isi .kartu-rincian { gap: .35rem; font-size: .84rem; }
  /* ⛔ D-409 — LATAR TIDAK IKUT BERGULIR (temuan CEO sesudah u40: *"kartu sudah
     mentok tapi masih bisa discroll sampai bawah padahal yang discroll halaman
     papan tugasnya… kartu saat dibuka full screen kesannya masuk ke halaman
     baru"*). Lembar penuh MENJANJIKAN halaman, jadi yang di belakangnya wajib
     diam — kalau tidak, jari yang menggulir melewati ujung lembar diam-diam
     memindahkan papan, dan orangnya kembali ke tempat yang bukan tempatnya.
     ⚠ Hanya di lebar ini: pada desktop dialog TIDAK menutupi layar, dan mengunci
     `overflow` di sana menghilangkan bilah gulir sehingga seluruh tata letak
     bergeser ±15px tiap dialog dibuka — menyembuhkan yang tidak sakit. */
  body:has(.modal-veil:not([hidden])) { overflow: hidden; }
  /* Y-3 (D-406) — popover jadi LEMBAR BAWAH: panel yang menempel tombolnya
     meluber keluar tepi 390px dan tidak bisa dipakai (temuan CEO 25 Okt #2/#5). */
  .pop-panel { position: fixed; left: 12px; right: 12px; top: auto;
    bottom: calc(12px + env(safe-area-inset-bottom)); width: auto; min-width: 0;
    max-height: 70dvh; overflow: auto; z-index: 130; }
}

/* X4 — komposer tambah kartu di kaki kolom */
.kolom-tambah { margin-top: .5rem; display: flex; flex-direction: column; gap: .35rem; }
.kolom-tambah .inp { width: 100%; }
.kolom-tambah-aksi { display: flex; gap: .35rem; align-items: center; }

/* Y-2 (D-406) — batas yang TERLIHAT (temuan CEO 25 Okt #1): pemisah bagian yang
   SAMA untuk semua bagian kartu, dan kotak isian komposer yang kelihatan kotak
   (gaya .inp tema hanya hidup di dalam .pfield). */
.kartu-bagian { border-top: 1px solid var(--border); padding-top: .7rem; }
.komentar-tulis > .inp, .ceklis-tambah > .inp, .kolom-tambah .inp {
  border: 1px solid var(--border); border-radius: 8px; padding: .42rem .6rem;
  background: var(--surface); }
/* Y-6 — aksi kecil pada baris komentar milik sendiri; sentuh >=40px (ADR-032) */
.linimasa-aksi { display: inline-flex; gap: .15rem; margin-left: .3rem; vertical-align: middle; }
.linimasa-aksi .btn { min-height: 40px; min-width: 40px; justify-content: center; }

/* Z-2 (D-411) — dua pola alamat per kanal berdiri sendiri-sendiri; di ponsel
   keduanya menumpuk, dan labelnya yang membedakan — bukan urutannya. */
.pola-isian { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-bottom: .35rem; }
.pola-isian > .small { min-width: 7.5rem; }

/* ⛳ AKSI SEKUNDER JADI TAUTAN TEKS (D-448 — pilihan CEO 30 Okt sesudah melihat bentuk
   tombol berjajar: *"sudah sesuai, tapi kurang pas rasanya, ajukan bentuk lain"*).
   ⛔ Satu aksi utama menonjol, sisanya direndahkan jadi tautan — pola yang dipakai
   rujukan yang CEO sebut sendiri (IMDb · MyAnimeList), supaya pusat perhatian kepala
   halaman jatuh pada FAKTA produknya, bukan pada deretan kotak.
   ⚠ Tinggi sentuh tetap ≥40px walau rupanya teks: yang dikecilkan tampilannya, bukan
   sasarannya. Tautan setinggi satu baris teks di ponsel adalah tautan yang meleset
   diketuk, dan ADR-032 tidak mengenal pengecualian untuk "ini cuma tautan". */
.aksi-tautan { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;
  margin-top: .55rem; }
.aksi-tautan > button, .aksi-tautan .aksi-lain > button {
  background: none; border: 0; padding: 0; color: var(--brand-text);
  font: inherit; font-size: .82rem; cursor: pointer;
  min-height: 40px; display: inline-flex; align-items: center; }
.aksi-tautan > button:hover, .aksi-tautan .aksi-lain > button:hover { text-decoration: underline; }
.aksi-tautan > button:focus-visible, .aksi-tautan .aksi-lain > button:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px; }

/* ⛳ AKSI UTAMA BERSEBELAHAN DENGAN "← Katalog" (D-448 — usul CEO 30 Okt).
   ⚠ Barisnya sudah ada dan selama ini setengah kosong, jadi aksi utama menumpang di
   sana tanpa menambah satu baris pun. Tombol utama didorong ke kanan supaya di layar
   lebar ia tetap berada di tepi — tempat mata mencarinya — sementara di ponsel ia
   berdampingan rapat dengan tombol kembali. */
.prod-atas { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.prod-atas .btn.pri { margin-left: auto; }
@media (max-width: 520px) { .prod-atas .btn.pri { margin-left: 0; } }

/* D-449 — REKAP HARIAN & TIM HARIAN.
   ⚠ Rumahnya berkas ini, bukan salinan mockup (ADR-262): tiap penyimpangan tampilan
   dicatat sebagai keputusan yang terbaca di diff. */

/* Deret lencana formasi: pembungkus yang membolehkan baris patah sendiri, supaya
   delapan nama tidak memaksa halaman bergulir menyamping di ponsel. */
.chips { display: flex; flex-wrap: wrap; gap: .35rem; }

/* Kotak angka jumlah paket di tabel Tim Harian — sempit dengan sengaja (isinya paling
   banyak tiga angka), tetapi tinggi sentuhnya TIDAK ikut dikecilkan: ADR-032 menuntut
   ≥40px, dan sasaran setinggi baris teks adalah sasaran yang meleset diketuk. */
.inp.sm.jmlpaket { min-width: 4.2rem; max-width: 5.5rem; min-height: 40px; display: inline-flex;
  align-items: center; text-align: right; }

/* Matriks marketplace × ekspedisi bisa punya belasan kolom. Angkanya dirapatkan ke
   kanan supaya satuan, puluhan dan ratusan berbaris — tabel angka yang rata kiri
   memaksa mata membandingkan panjang teks, bukan besarnya nilai. */
#tbMatriksHari td:not(:first-child), #tbRekapBulan td:not(:first-child) { text-align: right; }
#tbMatriksHari th:not(:first-child), #tbRekapBulan th:not(:first-child) { text-align: right; }
@media (max-width: 768px) {
  /* pada lebar ponsel tabel berubah jadi kartu berlabel (pola `data-l` aplikasi),
     dan rata kanan di sana justru memisahkan label dari nilainya */
  #tbMatriksHari td, #tbRekapBulan td { text-align: left; }
}

/* ⛳ BALOK ABU BERKILAU SAAT BERPINDAH LAYAR (D-463 — keputusan CEO 1 Nov 2026:
   *"Saya suka model loadingnya facebook, seluruh teks/gambar sudah diposisinya dalam
   bentuk balok abu lalu ada animasi seperti berjalan. … Jangan pakai bar atas, kurang
   jelas dan tidak cocok, ini data bukan video. Kalau progress bar untuk webiste video
   memang cocok seperti bar control video player"*).

   ⛔ Kenapa bilah atas tetap ada dan ini BUKAN penggantinya di semua tempat — dasarnya
   panduan primer, bukan selera: balok abu cocok untuk HALAMAN PENUH yang sedang dimuat,
   sedangkan untuk SATU MODUL di dalam layar yang sudah terbuka, penanda berjalan lebih
   tepat. Jadi pembagiannya: berpindah layar → balok abu; memperbarui isi di dalam satu
   layar (Livewire) → bilah 3px D-444. Panduan yang sama juga menolak bilah progres untuk
   halaman dan menyediakannya untuk unduhan/unggahan — persis pembedaan yang CEO sebut.

   ⚠ Bentuk balok WAJIB menyerupai isi yang datang. Panduan itu menyebut ketidaksamaan
   bentuk sebagai sebab layar tersentak saat isinya masuk, dan ongkosnya di sini murah:
   terukur 37 dari 39 layar sebentuk sama (judul · sub-judul · tombol aksi · kartu
   bertabel), hanya Papan Tugas berkolom dan Panduan tanpa tabel.

   ⚠ Dan satu bukti yang berlawanan dicatat supaya tidak hilang: satu studi terkontrol
   136 orang menemukan balok abu KALAH dari penanda berputar maupun layar kosong pada
   dugaan lama menunggu (2,82 vs 2,41 vs 2,29 detik). Penjelasan penulisnya sendiri yang
   membuatnya tetap tepat di sini: balok abu bekerja lebih baik pada antarmuka yang SUDAH
   DIKENAL — dan aplikasi ini dipakai sembilan orang setiap hari. */
:root { --km-blok: #E4EBF2; --km-kilau: #F6F9FC; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --km-blok: #1F2E3C; --km-kilau: #2B3D4F; }
}
:root[data-theme="dark"] { --km-blok: #1F2E3C; --km-kilau: #2B3D4F; }

/* Wadahnya meniru `.content`: kolom bercelah sama, supaya balok berdiri di posisi yang
   akan ditempati isi sebenarnya. */
.kerangka-muat { display: flex; flex-direction: column; gap: 1.1rem; }
.kerangka-muat .km-kepala { display: flex; align-items: flex-start;
  justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.kerangka-muat .km-aksi { display: flex; gap: .5rem; flex-wrap: wrap; }
.kerangka-muat .km-kartu { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; overflow: hidden; }
.kerangka-muat .km-kartu-h { padding: .8rem 1rem; border-bottom: 1px solid var(--border); }
.kerangka-muat .km-kartu-b { padding: 1rem; display: flex; flex-direction: column; gap: .55rem; }
.kerangka-muat .km-kolom { display: flex; gap: .8rem; }
.kerangka-muat .km-kolom > .km-kartu { flex: 1; min-width: 0; }

/* ⛔ Baloknya sendiri. `background-size` 400% supaya kilaunya menyapu melewati ujung
   balok, bukan berhenti di tengahnya — gradien selebar baloknya membuat kilau tampak
   berkedip di tempat alih-alih berjalan. Siklus 1,5 dtk mengikuti kebiasaan yang sudah
   dikenal orang dari aplikasi lain. */
.km-blok { background: var(--km-blok); border-radius: 6px; height: .8rem; }
.km-blok.b-judul { height: 1.3rem; width: 38%; border-radius: 7px; }
.km-blok.b-sub { height: .78rem; width: 56%; margin-top: .45rem; }
.km-blok.b-tombol { height: 2rem; width: 7.5rem; border-radius: 8px; }
.km-blok.b-tombol.pendek { width: 5.5rem; }
.km-blok.b-h2 { height: 1rem; width: 12rem; }
.km-blok.b-baris { height: .82rem; }
.km-blok.b-baris.r2 { width: 92%; }
.km-blok.b-baris.r3 { width: 78%; }
.km-blok.b-ubin { height: 3.4rem; border-radius: 8px; }
.kerangka-muat .km-blok { animation: km-kilau 1.5s ease-in-out infinite;
  background-image: linear-gradient(90deg, var(--km-blok) 25%, var(--km-kilau) 37%, var(--km-blok) 63%);
  background-size: 400% 100%; }
@keyframes km-kilau { 0% { background-position: 100% 0 } 100% { background-position: 0 0 } }

/* ⛔ Gerak dimatikan bagi yang memintanya, TETAPI baloknya tetap berdiri: yang dicabut
   kilauannya, bukan kabar bahwa isinya sedang datang — sama seperti pagar `.sibuk-isi`. */
@media (prefers-reduced-motion: reduce) {
  .kerangka-muat .km-blok { animation: none; background-image: none; }
}

/* Pada layar kecil tombol aksi turun ke bawah judul, sama seperti `.page-head` nyata
   (lihat pagar 1100px di atas) — balok yang tetap sebaris akan menjanjikan tata letak
   yang tidak akan datang. */
@media (max-width: 1100px) {
  .kerangka-muat .km-kepala { display: block; }
  .kerangka-muat .km-aksi { margin-top: .75rem; }
}
@media (max-width: 760px) {
  .kerangka-muat .km-kolom { flex-direction: column; }
}
/* ⛔ Isi nyata disembunyikan selama balok terlapis — bukan dibuang. Kekhususan pemilih
   ini (dua kelas + satu :not) mengalahkan `.screen.on` sehingga layar aktif pun ikut
   tersembunyi tanpa perlu `!important`. */
main.content.sedang-muat > *:not(.kerangka-muat) { display: none; }
/* ⛔ Pembungkus judul WAJIB `flex:1`. Tanpa itu ia menyusut ke lebar isinya, dan isinya
   balok berlebar PERSEN — persen dari nol tetap nol, sehingga judul & sub-judul tidak
   tergambar sama sekali. Tidak tertangkap uji markup mana pun (baloknya ADA di DOM,
   hanya berukuran nol); ketahuan dari tangkapan layar. */
.kerangka-muat .km-kepala > div:first-child { flex: 1; min-width: 0; }
/* Kepala tabel: sedikit lebih tinggi & lebih redup daripada barisnya, meniru `thead`
   yang memang berbeda dari isinya. */
.km-blok.b-kepala-tabel { height: 1rem; opacity: .72; margin-bottom: .2rem; }

/* ⛳ BALOK ABU UNTUK PEMBARUAN DI DALAM LAYAR (D-464 — keputusan CEO 1 Nov 2026:
   *"Belum diterapkan keseluruh halaman dan tab, atau popup/modal. Saya masih menemukan
   progress bar diatas saat pindah tab karyawan ke shift."*).

   ⛔ Sejak sini **bilah 3px D-444 PENSIUN dari layar pengguna**: satu bahasa pemuatan
   untuk seluruh aplikasi — berpindah layar (D-463) dan memperbarui isi di dalam layar
   (di sini) sama-sama balok abu berkilau.

   ⚠ Baloknya DILAPISKAN DI LUAR komponen, bukan di dalamnya, dan itu menentukan:
   Livewire me-morph isi komponen saat jawaban datang: simpul yang saya sisipkan di
   dalamnya ikut dibongkar di tengah jalan. Lapisan ini anak `<body>`, ditempatkan di
   atas kotak wilayahnya dengan koordinat halaman — morph tidak pernah melihatnya.

   ⚠ Ia MENGHALANGI ketukan (berbeda dari bilah lama yang `pointer-events:none`):
   wilayah yang sedang diperbarui memang tidak boleh ditekan dua kali, dan tombol yang
   tersembunyi di balik balok tetap bisa tertekan bila lapisannya tembus. */
.km-lapis { position: absolute; z-index: 60; background: var(--surface);
  border: 1px solid var(--border); border-radius: 12px; padding: 1rem;
  display: flex; flex-direction: column; gap: .55rem; overflow: hidden; }
/* Di atas lapisan dialog (`.modal-veil` z-index 110) saat yang diperbarui isi dialog. */
.km-lapis[data-di-dialog] { z-index: 120; }
.km-lapis .km-blok { animation: km-kilau 1.5s ease-in-out infinite;
  background-image: linear-gradient(90deg, var(--km-blok) 25%, var(--km-kilau) 37%, var(--km-blok) 63%);
  background-size: 400% 100%; }
@media (prefers-reduced-motion: reduce) {
  .km-lapis .km-blok { animation: none; background-image: none; }
}

/* ⛳ KOTAK YANG SEDANG DIKETIK BERKILAU, TIDAK DITUTUPI (D-473 — laporan CEO 2 Nov:
   *"saat mencari sku itu otomatis layar loading muncul padahal saya masih mengetik"*).

   ⛔ Garis bawah, bukan selimut — dan pembedanya bukan selera: balok abu menjawab
      *"isinya belum ada"*, sedangkan di sini isinya justru ADA di tangan orangnya;
      yang belum datang hasilnya. Menutupi huruf yang sedang diketik membuat orang
      berhenti mengetik — persis kebalikan dari gunanya.
   ⚠ Warna, irama, dan siklus 1,5 dtk sama persis dengan baloknya, jadi ia tetap satu
      bahasa pemuatan; yang berbeda hanya tebalnya: 2px di tepi bawah kotak.
   ⚠ Keyframes-nya TERPISAH dan itu wajib: `km-kilau` menggerakkan `background-position`
      ke `0`, yang pada garis ini menarik kilaunya dari tepi bawah ke tepi atas kotak
      dan membuatnya terbaca seperti coretan, bukan kilau. */
.km-ketik { background-image: linear-gradient(90deg, var(--km-blok) 25%, var(--km-kilau) 37%, var(--km-blok) 63%);
  background-size: 400% 2px; background-repeat: no-repeat; background-position: 0 100%;
  animation: km-kilau-garis 1.5s ease-in-out infinite; }
@keyframes km-kilau-garis { 0% { background-position: 100% 100% } 100% { background-position: 0 100% } }
/* Geraknya dicabut bagi yang memintanya, garisnya tetap berdiri — sama seperti balok. */
@media (prefers-reduced-motion: reduce) {
  .km-ketik { animation: none; background-image: linear-gradient(var(--km-blok), var(--km-blok)); }
}

/* ⛳ KARTU VERIFIKASI PENGGAJIAN (D-466 — teguran CEO 1 Nov: *"verifikasi penggajian …
   sangat tidak user friendly dan memusingkan"*). Satu kartu per ORANG, bukan satu
   baris per temuan: yang membaca mengambil keputusan per orang. */
.pkartu { border: 1px solid var(--border); border-radius: 12px; padding: .9rem 1rem;
  margin-bottom: .8rem; background: var(--surface); }
.pkartu-h { display: flex; align-items: center; justify-content: space-between;
  gap: .6rem; flex-wrap: wrap; margin-bottom: .55rem; }
.phal { border-top: 1px solid var(--border); padding: .6rem 0 .2rem; }
.pkartu .phal:first-of-type { border-top: 0; padding-top: 0; }
.phal-t { margin-bottom: .2rem; }
/* Kalimat tindakan diberi pagar kiri supaya terbaca sebagai JALAN KELUAR, bukan
   keterangan tambahan yang mudah dilewati mata. */
.phal-a { border-left: 2px solid var(--brand); padding-left: .55rem; margin-top: .35rem; }
.plipat { border: 1px solid var(--border); border-radius: 12px; padding: .7rem 1rem; }
.plipat > summary { cursor: pointer; font-size: .86rem; }
.plipat[open] > summary { margin-bottom: .4rem; }

/* ===== Sisi foto per ubin galeri (D-369 · Ide 8) =====
   Pemilih sisi hidup DI UBINNYA, tempat fotonya terlihat — menyebut sisi tanpa melihat
   fotonya adalah menebak, dan menebak sudah diukur gagal (penyaring otomatis 45%).
   Yang BELUM disebut ditandai, bukan didiamkan: kekosongan yang tidak terlihat tidak
   akan pernah dikerjakan siapa pun, dan 8.661 aset masih menunggu disebut. */
.aset-ubin .aset-sisi {
  width: 100%;
  margin-top: .3rem;
  padding: .18rem .3rem;
  font-size: .72rem;
  min-height: 40px;            /* ADR-032: sasaran sentuh tidak lebih kecil dari ini */
}
.aset-ubin .aset-sisi.belum {
  border-color: var(--warn, #c98a00);
  color: var(--warn, #c98a00);
}
/* ═══ Penyaring Perlu Tindakan: SATU baris di layar ponsel (D-476) ═══════════
   Temuan CEO 2 Nov dari ponselnya: ketiga tombolnya patah jadi dua baris dan
   mendorong daftar pekerjaan turun. `mockup.css` memberinya `flex-wrap:wrap`,
   yang memang benar untuk daftar tombol yang panjangnya tak tentu — tetapi ini
   penyaring BERTIGA dan tetap, jadi ia pantas dibagi rata.
   ⛔ Diperbaiki DI SINI, bukan dengan menyunting salinan mockup (ADR-262): ini
   geometri tata letak, dan penyimpangannya wajib terbaca sebagai keputusan.
   ⚠ `min-width:0` bukan hiasan — tanpanya butir flex menolak menyusut di bawah
   lebar isinya dan pembungkusnya tetap pecah walau `nowrap` dipasang. */
.aqsaring{flex-wrap:nowrap}
.aqsaring>.btn{flex:1 1 0;min-width:0;white-space:nowrap;padding-left:.4rem;padding-right:.4rem}

/* ⛳ KERANGKA BERBENTUK HASIL (D-496 — koreksi CEO 4 Nov: *"abunya kurang cocok
   desainnya"*). Satu baris meniru baris hasil yang akan menggantikannya: SKU tebal
   pendek di atas, keterangan tipis panjang di bawahnya, tautan pendek di kanan.
   ⛔ Sebabnya bukan selera: kerangka yang bentuknya BERBEDA dari hasilnya membuat
   layar melompat saat hasil datang, dan lompatan itulah yang terbaca sebagai
   "tidak cocok". Warnanya sendiri sudah diukur wajar (#1F2E3C di atas #151F2A). */
.kerangka-muat .km-hasil { display: flex; align-items: center; gap: .75rem;
  padding: .5rem 0; }
.kerangka-muat .km-hasil > span { flex: 1; min-width: 0; display: flex;
  flex-direction: column; gap: .35rem; }
.km-blok.b-sku { height: .86rem; width: 8.5rem; }
.km-blok.b-ket { height: .68rem; width: 13.5rem; opacity: .72; }
.km-blok.b-buka { height: .68rem; width: 3.2rem; flex: 0 0 auto; opacity: .72; }
/* Lebar yang berbeda-beda per baris: tiga baris identik terbaca sebagai pola, bukan
   sebagai daftar — dan pola yang terlalu rapi justru terlihat seperti layar beku. */
.kerangka-muat .km-hasil:nth-child(3) .b-sku { width: 10.5rem; }
.kerangka-muat .km-hasil:nth-child(3) .b-ket { width: 11rem; }
.kerangka-muat .km-hasil:nth-child(4) .b-sku { width: 7rem; }
.kerangka-muat .km-hasil:nth-child(4) .b-ket { width: 14.5rem; }
@media (max-width: 480px) { .km-blok.b-ket { width: 9.5rem; } }

/* ===== D-497 — PANEL ANAK DIKURUNG DI PITA KARTUNYA, DAN DITENGAHKAN ===========
   ⛳ Koreksi CEO 6 Nov saat menyusuri UAT §14.30 butir 1: *"saat tombol diklik malah
   muncul popup dialog baru … melewati popup kartu yang sudah muncul sebelumnya …
   seharusnya kalau itu dibuat popup bagian dari kartu dia harus berada didalam kartu
   bukan melebihi kartu"*, lalu bentuknya ia tetapkan: *"dikurung didalam kartu atau
   kontainer pemilik tombol itu dan muncul ditengahnya agar secara tampilan juga
   proporsional bukan dibawa atau mepet kiri atau kanan seperti yang sekarang"*.
   ⛔ Sebabnya: `.pop-panel` MENGGANTUNG di bawah tombolnya (`top:calc(100% + .3rem)`)
   sementara Tugaskan · Serah terima · Pindah divisi · Batalkan duduk di baris aksi
   PALING BAWAH kartu — panelnya tumpah melewati tepi kartu dan terbaca sebagai popup
   KEDUA yang berdiri sendiri.
   ⚠ DITENGAHKAN PADA LAYAR, BUKAN PADA KARTU, dan itu keputusan yang disebut: yang
   bergulir adalah TIRAInya (`.modal-veil{overflow:auto}`), kartunya tidak — jadi kartu
   panjang bisa punya titik-tengah DI LUAR layar. Menengahkan ke kartu akan membuat
   panel lahir di tempat yang tidak terlihat, yaitu persis cacat yang perbaikan ini
   tutup. Lebarnya tetap dikurung di dalam pita kartu (21rem < 640px), jadi secara
   tampilan ia tetap "di dalam kartu" dan tidak pernah mepet tepi.
   ⚠ HANYA ≥901px: pada ≤900px panel ini sudah jadi LEMBAR BAWAH (Y-3 · D-406, temuan
   CEO 25 Okt) dan itu sudah benar. Media query dipakai — bukan sekadar urutan baris —
   sebab selektor di sini lebih spesifik daripada `.pop-panel` di blok ponsel dan akan
   menang tanpa pagar ini. */
@media (min-width: 901px) {
  .kartu-lembar .pop-panel {
    position: fixed; top: 50%; left: 50%; right: auto;
    transform: translate(-50%, -50%);
    width: min(86vw, 21rem); max-width: none;
    max-height: 76vh; overflow: auto; z-index: 130;
  }
}

/* D-508 — petunjuk mode kelola: hadir hanya saat modenya MATI.
   ⛳ Temuan CEO 6 Nov: ia mengunggah foto percobaan lalu menyimpulkan foto tidak bisa
   dihapus. Tombolnya memang ada, tetapi `.aset-ubin .aset-aksi{display:none}`
   menyembunyikannya sampai ⚙ Kelola berkas ditekan — dan tidak ada apa pun yang
   memberi tahu bahwa mode itu ADA. ⚠ Kalimatnya lenyap begitu modenya menyala;
   petunjuk yang bertahan sesudah dikerjakan berubah jadi kebisingan. */
.aset-kisi.aset-kelola ~ .aset-petunjuk { display: none; }

/* ===== PERMUKAAN KERJA (D-500 · ADR-273 K-1…K-4) ==============================
   ⛳ Takarannya DISALIN dari arsip mockup *DJG Kerja* (keputusan CEO 7 Nov):
   grid 3 kolom · ubin ≥94px berikon kotak-bulat 40px · bilah bawah 5 slot dengan
   tombol tengah lingkaran 56px yang naik −26px menembus bilahnya.
   ⚠ Hanya berlaku di dalam `body.kerja` — cangkang Kantor tidak tersentuh sehuruf
   pun, sebab menyatukan dua postur di satu berkas adalah bentuk yang keluhan CEO
   6 Nov justru tolak. */
body.kerja { background: var(--bg); margin: 0; min-height: 100dvh;
  display: flex; flex-direction: column; }

.kerja-kepala { position: sticky; top: 0; z-index: 5; display: flex; align-items: center;
  gap: .6rem; padding: .6rem .85rem; background: var(--surface);
  border-bottom: 1px solid var(--border); padding-top: max(.6rem, env(safe-area-inset-top)); }
.kerja-kepala-teks { flex: 1; min-width: 0; }
/* MEREK di kepala (koreksi CEO 7 Nov): logo, bukan sapaan; sapaan pindah ke isi
   halaman. Sasaran sentuhnya tetap >=40px walau logonya lebih kecil. */
.kerja-merek { flex: 0 0 auto; display: grid; place-items: center;
  min-width: 40px; min-height: 40px; }
.kerja-merek img { height: 26px; width: auto; display: block; }
/* .kerja-judul disetel di blok KEPALA PONSEL di bawah (CEO 8 Nov 2026). */


/* ═══ IKON SVG SATU KELUARGA (keputusan CEO 8 Nov 2026) ═══
   Menggantikan glif teks pada MENU (navigasi · ubin beranda · bilah bawah · kepala).
   ⛔ Terukur sebelum pindah: 83 slot ikon memakai 45 lambang, 21 lambang memikul DUA
   arti (Pemasok & Panduan memakai lambang yang sama, begitu pula Presensi & Profil),
   dan 28 slot memakai kotak-berarsir yang pada 390px tidak bisa dibedakan.
   ⚠ Tebal garis, bentuk ujung, dan sudutnya ditentukan DI SINI — satu rumah. Itulah
   yang membuat 89 ikon terbaca sebagai satu keluarga, dan itu yang glif teks tidak
   pernah bisa jamin: bentuknya milik font tiap ponsel (D-435 mencatat dua glif SAH
   tergambar kotak kosong di ponsel CEO). */
.simbol { width: 1.15em; height: 1.15em; flex: 0 0 auto; display: inline-block;
  vertical-align: -0.18em; fill: none; stroke: currentColor; stroke-width: 1.75;
  stroke-linecap: round; stroke-linejoin: round; }
/* Ukuran per wadah — wadahnya sudah punya kotaknya sendiri, ikon mengisi proporsional. */
.nav-item .ic .simbol, .bnav-item .ic .simbol { width: 1.15rem; height: 1.15rem; }
.kerja-ubin-ikon .simbol { width: 1.4rem; height: 1.4rem; }
.kbil-item .ic .simbol { width: 1.25rem; height: 1.25rem; }
.kbil-utama .ic .simbol { width: 1.6rem; height: 1.6rem; stroke-width: 2; }
/* PUSAT INFORMASI di kepala (perintah CEO 8 Nov 2026: "header aplikasi pwa tidak ada
   fungsinya"). Sasaran sentuh 40px penuh walau glifnya kecil — ADR-032.
   ⚠ `margin-left:auto` dibutuhkan khusus di BERANDA: di sana blok judul ber-flex:1
   tidak dirender, jadi tanpa baris ini ikonnya menempel ke logo. */
.kerja-pusat { flex: 0 0 auto; position: relative;
  display: grid; place-items: center; min-width: 40px; min-height: 40px;
  border-radius: 999px; color: var(--text); text-decoration: none;
  font-size: 1.15rem; }
.kerja-pusat .lencana { position: absolute; top: 1px; right: 0; min-width: 1.05rem;
  padding: 0 .22rem; border-radius: 999px; background: var(--bad); color: #fff;
  font-size: .62rem; font-weight: 700; line-height: 1.05rem; text-align: center; }

/* Kartu pusat informasi: "keadaan + jumlah + kata kerjanya".
   ⛔ Kalimatnya WAJIB boleh turun baris. Di `.kerja-baris` judul dipotong elipsis satu
   baris — benar untuk daftar SKU, salah di sini: kalimat itulah isi kartunya, dan
   kartu yang memotongnya berhenti menyerahkan pekerjaan dan kembali sekadar memberi
   tahu. Itu persis beda antara kartu *Peringatan* BigSeller yang berguna dan lonceng
   yang diabaikan. */
.pusat-item { display: flex; align-items: flex-start; gap: .6rem;
  padding: .7rem .15rem; border-bottom: 1px solid var(--border);
  color: inherit; text-decoration: none; }
.pusat-item:last-child { border-bottom: 0; }
.pusat-item .kiri { flex: 1; min-width: 0; }
.pusat-item .jd { font-size: .86rem; font-weight: 600; }
.pusat-item .kal { font-size: .76rem; color: var(--muted); margin-top: .15rem; }
.pusat-item .n { font-family: var(--f-mono); font-size: 1.05rem; font-weight: 700;
  line-height: 1.2; }

/* ⛔ Ruang bawah = tinggi bilah + batang beranda ponsel. Tanpa itu ubin terakhir
   tertimpa bilah, dan itu persis kelas "isi terpotong senyap" sumbu ke-8. */
.kerja-isi { flex: 1 1 auto; padding: .8rem .75rem;
  padding-bottom: calc(76px + env(safe-area-inset-bottom) + .8rem);
  display: flex; flex-direction: column; gap: .9rem; }

.kerja-kelompok { display: flex; flex-direction: column; gap: .45rem; }
.kerja-kelompok-judul { font-size: var(--huruf-sub); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); margin: 0 .15rem; }

/* ⛔ DUA KELAS YANG LAHIR DARI PENGUKURAN, bukan dari selera (8 Nov 2026).
   Terukur di permukaan ponsel: 307 deklarasi gaya diketik di markup, dan TIGA
   teratas menutup 55% — `margin:.9rem 0 .35rem` (61×), `color:var(--muted)` (57×),
   `padding:.5rem .15rem` (51×). Ketiganya bukan keputusan per layar; ia jarak
   judul kelompok dan baris "belum ada isinya", ditulis ulang berpuluh kali karena
   tidak ada kelasnya. Gaya yang diketik ulang adalah gaya yang akan berbeda
   sendiri pada hari salah satunya disunting. */
.kerja-kelompok-judul { margin: var(--ruang-6) 0 var(--ruang-2); }
.kerja-kelompok-judul.pertama { margin-top: 0; }
.kerja-kosong { padding: var(--ruang-3) .15rem; color: var(--muted);
  font-size: var(--huruf-sub); }
.kerja-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: .5rem; }

.kerja-ubin { position: relative; min-height: 94px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 12px; display: flex;
  flex-direction: column; align-items: center; justify-content: center; gap: .35rem;
  padding: .5rem .3rem; text-align: center; color: var(--text); text-decoration: none; }
.kerja-ubin:active { background: var(--surface-2); }
.kerja-ubin-ikon { width: 40px; height: 40px; border-radius: 11px; display: grid;
  place-items: center; font-size: 1.25rem; background: var(--brand-soft); color: var(--brand-text); }
.kerja-ubin-kata { font-size: var(--huruf-kecil); font-weight: 600; line-height: 1.2; }
.kerja-ubin-lencana { position: absolute; top: 6px; right: 8px; min-width: 18px; height: 18px;
  border-radius: 999px; background: var(--bad); color: #fff; font-size: .62rem; font-weight: 700;
  display: grid; place-items: center; padding: 0 .25rem; }
.kerja-catatan { text-align: center; padding: .6rem; background: var(--surface-2);
  border-radius: 9px; }

/* ⛔ BILAH BAWAH 5 SLOT. `overflow: visible` wajib — tombol tengah naik menembus
   tepi atas bilah, dan satu `overflow:hidden` di sini memotongnya jadi setengah
   lingkaran tanpa galat apa pun. */
.kerja-bilah { position: fixed; left: 0; right: 0; bottom: 0; z-index: 10;
  display: flex; align-items: stretch; overflow: visible;
  background: var(--surface); border-top: 1px solid var(--border);
  padding-bottom: env(safe-area-inset-bottom); }
.kbil-item { flex: 1; min-height: 56px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px; padding: .2rem;
  color: var(--muted); text-decoration: none; font-size: .66rem; font-weight: 600;
  position: relative; }
.kbil-item .ic { font-size: 1.1rem; line-height: 1; }
.kbil-item.on { color: var(--brand-text); }
.kbil-lencana { position: absolute; top: 4px; right: 50%; transform: translateX(16px);
  min-width: 16px; height: 16px; border-radius: 999px; background: var(--bad); color: #fff;
  font-size: .58rem; font-weight: 700; display: grid; place-items: center; padding: 0 .2rem; }

.kbil-utama { flex: 0 0 88px; border: 0; background: transparent; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  gap: 2px; padding-bottom: .35rem; color: var(--brand-text);
  font: inherit; font-size: .66rem; font-weight: 700; }
.kbil-utama .ic { width: 56px; height: 56px; border-radius: 50%; background: var(--brand);
  color: #fff; display: grid; place-items: center; font-size: 1.5rem; margin-top: -26px;
  box-shadow: var(--shadow); border: 4px solid var(--surface); }
.kbil-utama:active .ic { background: var(--brand-strong); }

/* Kartu keadaan hari ini — puncak beranda Kerja (D-500). */
.kerja-hari { background: linear-gradient(180deg, var(--brand-soft), var(--surface));
  border: 1px solid var(--border); border-radius: 12px; padding: .75rem; }
.kerja-hari-atas { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.kerja-hari-utama { display: flex; align-items: center; gap: .7rem; margin: .5rem 0 .25rem; }
.kerja-hari-jam { font-family: var(--f-mono); font-size: 1.9rem; font-weight: 600; line-height: 1; }

/* Bilah tawaran "ada tampilan ponsel" — permukaan Kantor di layar sempit (D-500). */
.tawar-kerja { display: flex; align-items: center; gap: .5rem; padding: .5rem .7rem;
  background: var(--brand-soft); border-bottom: 1px solid var(--border);
  font-size: .82rem; color: var(--brand-text); }
.tawar-kerja > span { flex: 1; min-width: 0; }
.tawar-kerja .btn { min-height: 40px; }

/* ===================================================================
   KOSAKATA LAYAR PERMUKAAN KERJA (D-500) — dipakai 40 layar, bukan satu.
   ⛔ Ditulis SEKALI di sini, bukan per layar: tiap layar Kerja menyusun
      kartu · baris · pil · aksi · medan dari kelas yang sama, sehingga
      satu perbaikan rupa berlaku ke semuanya. Bentuknya diambil dari
      mockup DJG Kerja (ADR-268: mockup = ARSIP yang sah dibaca sebagai
      bahan merancang); angkanya TETAP dari mesin (Prinsip 4).
   ⚠ Berawalan `kerja-` walau nama mockup-nya polos (`.kartu`, `.baris`),
      sebab berkas CSS ini juga melayani permukaan Kantor — nama polos
      akan menabrak layar yang sudah hidup. =================== */

.kerja-kartu { background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: .75rem; }
.kerja-kartu + .kerja-kartu { margin-top: .6rem; }

.kerja-baris { display: flex; align-items: center; gap: .6rem;
  padding: .6rem .15rem; border-bottom: 1px solid var(--border); }
.kerja-baris:last-child { border-bottom: 0; }

/* ⛔ BARIS YANG JADI TOMBOL/TAUTAN MENYUSUT MENGIKUTI ISINYA — temuan CEO 10 Nov
   ("menu pemasok berantakan"), dan akarnya satu baris CSS yang TIDAK ADA.
   `.kerja-baris` di atas hanya menyatakan `display:flex`. Sebuah `<div>` memang
   memenuhi lebar induknya, tetapi `<button>` dan `<a>` berlebar `auto` — jadi tiap
   baris selebar isinya sendiri, dan daftar yang namanya panjang-pendek tergambar
   bertingkat-tingkat seperti tangga. Itu persis yang CEO lihat di layar Pemasok.
   ⚠ Ia menyentuh ENAM layar sekaligus (`import` · `katalog` · `klaim` · `papan` ·
   `pengajuan` · `ref`/Pengaturan) plus `pemasok` — dan CEO memang menyebut Pemasok,
   Pengaturan, dan Klaim sebagai tiga keluhan terpisah. Satu akar, tiga laporan.
   ⚠ Chrome bawaan tombol ikut dicabut: tanpa `font:inherit` barisnya memakai font
   sistem, tanpa `text-align:left` isinya terdorong ke tengah, dan tanpa
   `text-decoration:none` baris-sebagai-TAUTAN tergaris bawah — keluhan CEO yang
   terpisah lagi ("katalog ... semuanya digaris bawahi"). */
button.kerja-baris, a.kerja-baris {
  width: 100%; text-align: left;
  font: inherit; color: inherit;
  background: none; border: 0; border-bottom: 1px solid var(--border);
  text-decoration: none; cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
button.kerja-baris:last-child, a.kerja-baris:last-child { border-bottom: 0; }
.kerja-baris .kiri { flex: 1; min-width: 0; }
.kerja-baris .nm { font-size: var(--huruf-isi); font-weight: 600; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.kerja-baris .sub { font-size: var(--huruf-kecil); color: var(--muted); }
.kerja-baris .kanan { font-family: var(--f-mono);
  font-size: .9rem; font-weight: 600; text-align: right; }

.kerja-pil { display: inline-flex; align-items: center; gap: .25rem;
  padding: .12rem .5rem; border-radius: 999px; font-size: .7rem; font-weight: 600;
  background: var(--surface-2); color: var(--muted); }
.kerja-pil.ok { background: var(--ok-soft); color: var(--ok); }
.kerja-pil.warn { background: var(--warn-soft); color: var(--warn); }
.kerja-pil.bad { background: var(--bad-soft); color: var(--bad); }

.kerja-aksi { width: 100%; min-height: 52px; border-radius: 12px; border: 0;
  background: var(--brand); color: #fff; font: inherit; font-size: .95rem;
  font-weight: 700; cursor: pointer; display: flex; align-items: center;
  justify-content: center; gap: .4rem; margin-top: .6rem; }
.kerja-aksi.kedua { background: var(--surface); color: var(--brand-text);
  border: 1px solid var(--border-strong); }
.kerja-aksi.bahaya { background: var(--bad); }
.kerja-aksi[disabled] { opacity: .5; cursor: not-allowed; }
.kerja-aksi:focus-visible { outline: 2px solid var(--brand-strong, var(--brand));
  outline-offset: 2px; }

.kerja-ket { font-size: var(--huruf-kecil); color: var(--muted); text-align: center;
  padding: .5rem; background: var(--surface-2); border-radius: 9px;
  margin-top: .6rem; }
.kerja-ket.sejajar { text-align: left; }

.kerja-medan { display: flex; flex-direction: column; gap: .3rem; }
.kerja-medan label { font-size: var(--huruf-sub); font-weight: 600; color: var(--muted); }
.kerja-medan input, .kerja-medan textarea, .kerja-medan select {
  width: 100%; min-height: 46px; border: 1px solid var(--border-strong);
  border-radius: 10px; background: var(--surface); color: var(--text);
  font: inherit; font-size: .9rem; padding: .55rem .7rem; }
.kerja-medan textarea { min-height: 84px; resize: vertical; }

.kerja-pilihan { display: flex; gap: .4rem; flex-wrap: wrap; }
.kerja-pilihan button { flex: 1 1 30%; min-height: 46px; border-radius: 10px;
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text); font: inherit; font-size: .82rem; font-weight: 600;
  cursor: pointer; }
.kerja-pilihan button[aria-pressed="true"] { background: var(--brand-soft);
  border-color: var(--brand); color: var(--brand-text); }

.kerja-angka-baris { display: flex; gap: .5rem; margin-top: .6rem; }
.kerja-angka { flex: 1; background: var(--surface-2); border-radius: 9px;
  padding: .45rem .5rem; text-align: center; }
.kerja-angka b { display: block; font-family: var(--f-mono);
  font-size: 1.15rem; line-height: 1.2; }
.kerja-angka span { font-size: var(--huruf-kecil); color: var(--muted); }

.kerja-medan-tumpuk { display: flex; flex-direction: column; gap: .8rem; }

/* Kartu "Hari ini" — dua potong yang terlewat saat beranda ditulis, dan sekarang
   dipakai dua layar (beranda + Presensi), jadi ia memang milik kosakata bersama. */
.kerja-hari-tgl { font-size: var(--huruf-kecil); color: var(--muted); }
.kerja-hari-ket { font-size: var(--huruf-sub); color: var(--muted); }

/* Jalan pulang di kepala layar dalam permukaan Kerja (D-500). Sasaran sentuh
   dibuat ≥40px lewat padding, bukan lewat tinggi huruf (ADR-032). */
/* ⛳ KEPALA PONSEL — bentuk pilihan CEO 8 Nov 2026 (panah · judul · dua aksi).
   ⛔ Yang lama: logo + tautan teks "‹ Beranda" + judul, bertumpuk di kiri pada TIAP
   layar. Tiga hal yang bersaing memperebutkan sisi yang sama, dan logo yang tidak
   memberi tahu apa pun kepada orang yang sudah ada di dalam aplikasi.
   ⚠ Panah dan aksi sama-sama 40px penuh (ADR-032) tanpa menggeser judulnya. */
.kerja-kembali { flex: 0 0 auto; display: grid; place-items: center;
  min-width: 40px; min-height: 40px; margin-left: -.35rem; padding: 0;
  border: 0; background: none; color: var(--text); border-radius: 999px;
  cursor: pointer; }
.kerja-judul { flex: 1; min-width: 0; font-size: var(--huruf-judul); font-weight: 700; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kerja-kepala-aksi { flex: 0 0 auto; position: relative; display: grid;
  place-items: center; min-width: 40px; min-height: 40px; border-radius: 999px;
  color: var(--text); text-decoration: none; }
/* ⛔ Grup, bukan `:first-of-type` pada aksinya. Terukur 8 Nov dari tangkapan
   beranda: di sana `<a class="kerja-merek">` adalah `<a>` PERTAMA, jadi pemilih
   tipe itu tidak pernah kena dan kedua ikon menempel ke logo di KIRI. Pembungkus
   menjawab pertanyaan yang benar: "dorong KELOMPOK aksi ke kanan". */
.kerja-kepala-aksi-grup { margin-left: auto; display: flex; align-items: center; gap: .1rem; }
/* Panah "buka" di ujung baris — memakai gambar yang SAMA dengan panah kembali,
   diputar. Satu gambar, dua arah: sprite tidak perlu memuat dua berkas untuk satu
   bentuk, dan keduanya pasti sekeluarga sebab keduanya memang satu. */
.simbol-maju { transform: rotate(180deg); color: var(--muted); align-self: center; }

/* KEPALA RINCIAN PRODUK di ponsel (teguran CEO 8 Nov 2026: katalog "hanya porting
   desktop"). Foto besar dulu, baru nama — di lapangan pertanyaannya "ini barang yang
   mana?", dan jawaban tercepat untuk itu gambarnya, bukan kodenya. */
.produk-kepala { display: flex; align-items: center; gap: .8rem; }
.produk-foto { flex: 0 0 auto; width: 84px; height: 84px; border-radius: 14px;
  overflow: hidden; background: var(--surface-2); display: grid; place-items: center; }
.produk-foto img { width: 100%; height: 100%; object-fit: cover; }
.produk-judul { flex: 1; min-width: 0; }
.produk-judul .nm { font-size: .95rem; font-weight: 700; word-break: break-all; }
.produk-judul .sub { font-size: .78rem; color: var(--muted); margin-top: .15rem; }

/* ===================================================================
   ISI KANTOR DI DALAM CANGKANG KERJA (D-500) — berlaku sampai tiap layar
   punya kembaran Kerja-nya sendiri. Hanya PENYESUAIAN, bukan tampilan baru:
   yang dirapikan di sini cuma hal yang jadi ganda karena cangkangnya berganti. */

/* Judul layar sudah disebut kepala cangkang; `.page-head h1` menyebutnya untuk
   kedua kalinya, dua baris di bawahnya. Sub-judulnya TETAP — ia menerangkan,
   bukan mengulang. */
body.kerja .page-head h1 { display: none; }
body.kerja .page-head { margin-bottom: .5rem; }
body.kerja .screen { padding: 0; }

/* Galeri produk permukaan Kerja (D-500) — tiga kolom di 390px, foto persegi. */
/* ⛔ minmax(0,1fr), BUKAN 1fr: bawaan grid adalah `min-width:auto`, jadi SKU
   panjang seperti `3D-CABLECLIP-ORG(HITAM)+3PCS` MELEBARKAN kolomnya sendiri dan
   menindih tetangganya — terlihat 7 Nov di layar 390px. Dan ia tidak tertangkap
   ukuran "luber mendatar": halamannya memang tidak bergulir ke samping, yang luber
   isi di dalam petaknya. Pengukur yang salah pertanyaan hijau selamanya. */
.kerja-galeri { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; }
.kerja-ubin-foto { display: flex; flex-direction: column; gap: .2rem; text-decoration: none;
  color: var(--text); min-height: 44px; min-width: 0; }
.kerja-foto { aspect-ratio: 1; border-radius: 10px; overflow: hidden; background: var(--surface-2);
  display: grid; place-items: center; }
.kerja-foto img { width: 100%; height: 100%; object-fit: cover; }
.kerja-foto-kosong { font-size: var(--huruf-besar); color: var(--muted); }
/* Isinya kini ikon SVG, bukan glif teks (D-531 #4): ukurannya ditentukan di sini,
   bukan oleh font ponselnya. */
.kerja-foto-kosong .simbol { width: 28px; height: 28px; opacity: .5; }
.kerja-foto-sku { font-size: var(--huruf-kecil); font-weight: 600; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kerja-foto-harga { font-size: var(--huruf-kecil); color: var(--muted);
  font-family: var(--f-mono); }

/* ⛔ `--f-mono` DIDEFINISIKAN, bukan dipakai dengan cadangan (D-500).
   Pagar `VariabelCssTest` menyebutnya HANTU, dan ia benar: variabel yang hanya hidup
   lewat nilai cadangan di tiap pemakaian membuat pemakai berikutnya menebak
   cadangannya sendiri — lalu kolom angka di dua layar memakai dua font berbeda tanpa
   ada yang menyadarinya. Nilainya SAMA dengan `.mono` di mockup.css, satu rumah untuk
   satu keputusan.
   ⚠ Dan contoh pemakaiannya tidak boleh ditulis sebagai kode di komentar ini: pagarnya
   membaca seluruh berkas, jadi nama variabel khayalan di komentar pun terbaca sebagai
   hantu — terbukti sekali di sini. */
:root { --f-mono: "IBM Plex Mono", "Consolas", monospace; }

/* ⛳ KENALI PRODUK SEBAGAI HALAMAN — `.caripage` (V2.3 · D-514).
   ⛔ Keluhan CEO 7 Nov: *"Tampilan di mobile masih ada popup panel, itu aneh di pwa"*.
   Markupnya SAMA dengan tirai desktop (satu mesin, dua penempatan); yang berbeda hanya
   lapisan ini: bukan `position:fixed` di atas layar, melainkan isi biasa yang mengalir
   di dalam halaman — tanpa latar gelap, tanpa batas tinggi 70vh, tanpa bayangan kotak
   melayang. Kartunya melebar penuh sebab di 390px kartu-di-tengah hanya menyisakan
   gutter yang tidak ada gunanya.
   ⚠ `max-height` dilepas DENGAN SENGAJA: daftar hasil di halaman digulir bersama
   halamannya, bukan di dalam kotak yang punya gulirnya sendiri — dua gulir bersarang
   di layar sentuh adalah cara tercepat membuat orang kehilangan tempatnya. */
.caripage { display: block; }
.caripage .carikotak { width: 100%; max-height: none; border: 0; border-radius: 0;
  box-shadow: none; background: transparent; }
.caripage .carihead { padding: .6rem 0; }
.caripage .carihead:first-child { border-bottom: 0; }
.caripage .carilist { overflow: visible; padding: .35rem 0; }

/* ⛳ INDIKATOR MUAT ANTAR HALAMAN — tampilan ponsel (V2.6 · D-514).
   ⛳ Keluhan CEO dari APK: *"setiap loading juga tidak memuat loading halaman sendiri
   seperti aplikasi mobile pada umumnya."* Batang tipis di tepi atas, menyala saat
   halaman diambil dan hilang begitu tiba — jawaban SEKETIKA atas ketukan, yang
   membedakan "aplikasi" dari "halaman yang sedang menunggu".
   ⚠ Warnanya dari token tema, bukan nilai ketik: satu keputusan, satu rumah.
   ⚠ Dibungkus prefers-reduced-motion di bawah — gerak yang tidak bisa dimatikan adalah
   gerak yang memaksa. */
.kerja-muat { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 50;
  background: transparent; pointer-events: none; }
.kerja-muat::before { content: ''; display: block; height: 100%; width: 0;
  background: var(--brand); transition: width .18s ease-out, opacity .2s linear;
  opacity: 0; }
body.sedang-muat .kerja-muat::before { width: 70%; opacity: 1;
  transition: width 1.1s cubic-bezier(.1, .7, .2, 1), opacity .1s linear; }

@media (prefers-reduced-motion: reduce) {
  .kerja-muat::before,
  body.sedang-muat .kerja-muat::before { transition: none; }
}

/* ===================================================================
   HALAMAN MASUK VERSI PONSEL (V2.8 · D-514) — KESAN PERTAMA APK.

   ⛳ Keluhan CEO 7 Nov dari memasang APK-nya: *"Saya akses apk, halaman awalnya saja
   masih seperti desktop."* Terukur di 375px sebelum blok ini: kartu 339px melayang di
   tengah layar (idiom desktop), isian 289×40 ber-font **13,5px**, tombol 41px.
   ⛔ Font di bawah 16px bukan soal selera: iOS MEMPERBESAR halaman sendiri saat orang
   menyentuh isian ber-font lebih kecil, lalu tata letaknya melompat dan separuh layar
   keluar bidang. Satu angka itu yang membedakan "aplikasi" dari "situs di ponsel".

   ⚠ Dipilih dari LEBAR LAYAR, bukan dari ruang `/m`: halaman masuk ada SEBELUM orangnya
   dikenali, jadi ia tidak punya permukaan — dan menambah logika permukaan ke halaman
   tanpa auth berarti menebak, kelas cacat yang D-512 habisi. CSS cukup di sini.
   ⚠ Alur POST, pesan galat, dan "Ingat perangkat ini" TIDAK disentuh.
   =================================================================== */
@media (max-width: 540px) {
  .login-veil { align-items: flex-start; padding: 0; }
  .login-card { max-width: none; width: 100%; border: 0; border-radius: 0;
    box-shadow: none; background: var(--bg);
    padding: max(2.2rem, env(safe-area-inset-top)) 1.15rem 1.4rem;
    gap: 1.1rem; min-height: 100dvh; }
  /* Merek lebih besar: di ponsel ia satu-satunya yang memberi tahu orangnya bahwa ia
     membuka aplikasi yang benar — tidak ada bilah alamat di APK. */
  .login-brand { gap: .7rem; padding-bottom: .6rem; }
  .login-brand .logo-mark { width: 72px; height: 72px; border-radius: 18px; }
  /* Isian & tombol sebesar sasaran sentuh sungguhan, dan font 16px supaya iOS tidak
     memperbesar halamannya. */
  body .login-card .inp { min-height: 52px; font-size: 16px; border-radius: 12px;
    padding: .7rem .85rem; }
  .login-card .field { gap: .35rem; }
  .login-card #loginMasuk { min-height: 52px; font-size: 1rem; border-radius: 12px;
    padding: .8rem; }
  .login-card .login-row { min-height: 44px; }
}

/* ===================================================================
   V.3 — BAHASA VISUAL TAMPILAN PONSEL (D-514). Mandat CEO 7 Nov 2026:
   *"tampilan yang saya mau adalah modern dan fresh diaplikasi. Jadi jika tampilan itu
   masih terasa seperti porting dari web itu adalah salah"* · *"Dia harus bersikap
   seperti native mobile, bukan website yang diporting atau webview saja."*

   ⛔ **Yang membuat sebuah layar terasa "web yang diperkecil", dan ketiganya terukur
   bukan selera:** (a) kartu & kontrol bergaris 1px seperti tabel — aplikasi Android
   modern memisahkan dengan PERMUKAAN bertingkat, bukan dengan garis; (b) radius kecil
   dan ruang rapat yang disusun untuk tetikus, bukan untuk jempol; (c) ketukan yang
   tidak menjawab apa pun — tanpa keadaan `:active`, tiap sentuhan terasa seperti
   sentuhan yang tidak terdaftar, dan itu kesan tunggal paling kuat yang membedakan
   aplikasi dari halaman.

   ⚠ Hanya di bawah `body.kerja`. Tampilan DESKTOP tidak disentuh sehuruf pun — mandat
   CEO menyebutnya sendiri: *"Tidak harus sama seperti desktop."*
   ⚠ Ditulis sebagai TOKEN lalu diturunkan, bukan angka yang diulang per komponen:
   komponen berikutnya mewarisi keputusan yang sama tanpa ada yang mengingatnya.
   ⚠ Glif tetap satu keluarga monokrom (K231) — blok ini tidak menyentuh satu lambang.
   =================================================================== */
body.kerja {
  /* lekuk: kartu lebih bulat daripada kontrol — kartu adalah permukaan, kontrol adalah
     benda yang ditekan, dan mata membedakan keduanya dari lekuknya */
  --k-lekuk-kartu: 16px;
  --k-lekuk-kontrol: 14px;
  --k-lekuk-pil: 999px;
  /* ruang: satu tangga 4·8·12·16·24 — bukan belasan nilai pecahan rem yang
     masing-masing dipilih sendiri */
  --k-r1: 4px;  --k-r2: 8px;  --k-r3: 12px; --k-r4: 16px; --k-r5: 24px;
  /* huruf: empat tingkat, cukup untuk daftar · judul baris · judul layar · angka besar */
  --k-h1: 13px; --k-h2: 15px; --k-h3: 17px; --k-h4: 20px;
  /* bayangan SATU tingkat: dua tingkat membuat mata mencari kedalaman yang tidak ada */
  --k-bayang: 0 1px 2px rgba(0, 0, 0, .18), 0 2px 10px rgba(0, 0, 0, .10);
}

/* ── PERMUKAAN TONAL, BUKAN GARIS ───────────────────────────────────────────── */
body.kerja .kerja-kartu { border: 0; border-radius: var(--k-lekuk-kartu);
  padding: var(--k-r3); box-shadow: var(--k-bayang); }
body.kerja .kerja-kartu + .kerja-kartu { margin-top: var(--k-r2); }
/* Pemisah di DALAM daftar tetap ada, tetapi setipis mungkin: di dalam satu kartu, mata
   butuh tahu di mana satu baris berakhir — yang tidak dibutuhkan adalah kotak di
   sekeliling kartunya. */
body.kerja .kerja-baris { border-bottom-color: color-mix(in srgb, var(--border) 55%, transparent); }

/* ── APP-BAR 56px ───────────────────────────────────────────────────────────── */
body.kerja .kerja-kepala { min-height: 56px; border-bottom: 0;
  box-shadow: 0 1px 0 color-mix(in srgb, var(--border) 60%, transparent);
  padding: var(--k-r2) var(--k-r3); padding-top: max(var(--k-r2), env(safe-area-inset-top));
  gap: var(--k-r3); }
body.kerja .kerja-judul { font-size: var(--k-h3); }

/* ── BARIS DAFTAR: setinggi jempol, ekornya terbaca ─────────────────────────── */
/* ⛔ KERAPATAN DAFTAR — temuan CEO 10 Nov (D-531 #9): *"tampilan seluruh daftar terlalu
   boros, 1 lembar hanya muat berapa daftar sku saja. coba optimalkan lagi lalu terapkan
   keseluruh fitur serupa. agar layout tampilannya seragam walaupun beda halaman."*
   ⮕ Diukur sebelum disentuh: `min-height:56px`, padding 2×10,4px, kartu 13,6px.
   Sesudah: 48px · 2×7,2px · 11,5px. ⚠ `box-sizing:border-box` berlaku global, jadi
   keduanya menguasai BARIS YANG BERBEDA dan harus bergerak bersama: baris SEBARIS
   ditentukan `min-height` (56→48), baris DUA BARIS ditentukan padding di dalam
   kotaknya (20,8→14,4). Menurunkan satu saja menyisakan separuh daftar tetap boros.
   ⚠ BATAS BAWAHNYA BUKAN SELERA — ADR-032 menuntut sasaran
   sentuh ≥40px, dan baris daftar ini DAPAT DIKETUK di tujuh layar. 48px menyisakan
   margin di atas ambang itu; di bawahnya daftar jadi rapat sekaligus sulit ditekan,
   dan yang kedua tidak pernah terlihat di tangkapan layar. Dijaga `K387` dua arah.
   ⚠ Kerapatan LAPANG (`data-kerapatan="lapang"`) TIDAK disentuh: layar lapangan
   sengaja longgar sebab dipakai berdiri, satu tangan, kadang di bawah matahari
   (ADR-275 A3). Mengecilkannya akan menukar satu keluhan dengan keluhan lain. */
body.kerja .kerja-baris { min-height: 48px; padding: 7px var(--k-r1); }
body.kerja .kerja-baris .nm { font-size: var(--k-h2); }
body.kerja .kerja-baris .sub { font-size: var(--k-h1); }

/* ── KONTROL: primer penuh, sekunder TONAL (bukan outline) ──────────────────── */
body.kerja .kerja-aksi { border-radius: var(--k-lekuk-kontrol); font-size: var(--k-h2); }
/* ⛔ TOMBOL KEDUA TENGGELAM — temuan CEO 10 Nov (D-531 #8): *"transfer stok tombolnya
   tidak jelas, warnanya membuat orang tidak terlalu sadar."* Ia berlatar `surface-2`
   TANPA batas, jadi di atas kartu yang juga terang ia tidak terbaca sebagai tombol
   sama sekali — hanya teks berwarna merek. Batas tipis mengembalikan bentuknya tanpa
   menjadikannya tombol utama kedua, sebab A2 melarang dua tombol berebut jempol. */
body.kerja .kerja-aksi.kedua { background: var(--surface-2); border: 1px solid var(--border-strong); }
body.kerja .kerja-aksi + .kerja-aksi { margin-top: var(--k-r2); }
/* Isian: 52px & >=16px — di bawah 16px iOS memperbesar halamannya sendiri */
body.kerja .inp, body.kerja input[type="text"], body.kerja input[type="search"],
body.kerja input[type="number"], body.kerja input[type="date"], body.kerja select,
body.kerja textarea { min-height: 52px; font-size: 16px;
  border-radius: var(--k-lekuk-kontrol); }
/* ⚠ Kolom pindai memakai `.q` di dalam `.search`, yang bukan `.inp` */
body.kerja .search .q { min-height: 48px; font-size: 16px; }

/* ── UBIN BERANDA: ikon dalam kotak tonal ───────────────────────────────────── */
body.kerja .kerja-ubin { border: 0; border-radius: var(--k-lekuk-kartu);
  box-shadow: var(--k-bayang); gap: var(--k-r2); }
body.kerja .kerja-ubin-ikon { width: 44px; height: 44px; border-radius: var(--k-r3);
  background: var(--surface-2); }
body.kerja .kerja-ubin-kata { font-size: var(--k-h1); }

/* ── PIL STATUS: warna = MAKNA ──────────────────────────────────────────────── */
body.kerja .kerja-pil { border-radius: var(--k-lekuk-pil); font-size: var(--k-h1);
  padding: var(--k-r1) var(--k-r2); }

/* ── SENTUH & GERAK — bagian yang paling menentukan "rasa aplikasi" ─────────── */
body.kerja { -webkit-tap-highlight-color: transparent; }
body.kerja .kerja-ubin, body.kerja .kerja-ubin-foto, body.kerja .kerja-aksi,
body.kerja .kbil-item, body.kerja .kbil-utama, body.kerja .kerja-kembali,
body.kerja .kerja-merek, body.kerja .kerja-pilihan button, body.kerja .cariitem,
  body.kerja .kerja-pusat, body.kerja .pusat-item {
  touch-action: manipulation;
  transition: transform .09s ease-out, background-color .12s ease-out;
}
body.kerja .kerja-ubin:active, body.kerja .kerja-ubin-foto:active,
body.kerja .kerja-aksi:active, body.kerja .kbil-item:active,
body.kerja .kerja-pilihan button:active, body.kerja .cariitem:active,
 body.kerja .kerja-pusat:active, body.kerja .pusat-item:active {
  transform: scale(.98); background: var(--surface-2);
}
body.kerja .kbil-utama:active { transform: scale(.96); }
body.kerja .kerja-kembali:active, body.kerja .kerja-merek:active { transform: scale(.94); }
/* Bilah & kepala tidak ikut tersorot saat jempol menahan — ISI halaman tetap bisa
   disalin, dan itu disengaja: nomor dus & resi memang perlu disalin. */
body.kerja .kerja-bilah, body.kerja .kerja-kepala { user-select: none; -webkit-user-select: none; }

/* Transisi antar halaman untuk muat dokumen-silang (mis. tautan yang tidak
   ber-wire:navigate, dan pemuatan pertama dari APK). */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: reduce) {
  body.kerja .kerja-ubin, body.kerja .kerja-ubin-foto, body.kerja .kerja-aksi,
  body.kerja .kbil-item, body.kerja .kbil-utama, body.kerja .kerja-kembali,
  body.kerja .kerja-merek, body.kerja .kerja-pilihan button, body.kerja .cariitem,
  body.kerja .kerja-pusat, body.kerja .pusat-item {
    transition: none;
  }
  body.kerja .kerja-ubin:active, body.kerja .kerja-ubin-foto:active,
  body.kerja .kerja-aksi:active, body.kerja .kbil-item:active,
  body.kerja .kbil-utama:active, body.kerja .kerja-kembali:active,
  body.kerja .kerja-merek:active, body.kerja .kerja-pilihan button:active,
  body.kerja .cariitem:active { transform: none; }
}

/* ⛳ LABEL YANG BERPERAN SEBAGAI TOMBOL (V.4 · D-514) — tombol kamera presensi.
   ⛔ `.kerja-medan label` bergaya sebagai KETERANGAN medan (kecil, abu). Sebuah
   `<label>` yang membungkus isian berkas tersembunyi berperan sebagai TOMBOL, dan tanpa
   baris ini ia mewarisi gaya keterangan — tombolnya tergambar seperti tulisan kecil abu
   yang tak seorang pun tahu bisa ditekan.
   ⚠ `.kerja-medan-label` menggantikan `<label for>` untuk teksnya, sebab satu medan kini
   punya dua hal: keterangan DAN tombol. */
body.kerja .kerja-medan-label { font-size: var(--huruf-sub); font-weight: 600; color: var(--muted); }
body.kerja .kerja-medan label.kerja-aksi { font-size: var(--k-h2); font-weight: 600;
  color: var(--brand-text); display: flex; align-items: center; justify-content: center; }

/* ⛳ NAMA SKU DI GALERI: DUA BARIS (V.4 · D-514).
   ⛔ Satu baris 113px hanya memuat ±14 huruf pada ukuran ini, sementara SKU katalog ini
   panjang DAN pembedanya di belakang (`(HITAM)` vs `(PUTIH)`, `+3PCS`). Pemotongan
   tengah sudah mengembalikan pembedanya; dua baris mengembalikan sisanya. */
body.kerja .kerja-foto-sku { white-space: normal; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }

/* ── KONTAK YANG BISA DIKETUK ────────────────────────────────────────────────── */
/* ⛳ Temuan CEO 10 Nov (D-531 #15): tabel menampilkan nama, dan kontaknya tidak
   mengantar ke mana pun. Di ponsel, nomor yang hanya TERGAMBAR menuntut orangnya
   mengetik ulang 12 angka ke aplikasi lain — lebih lambat daripada alur manual,
   dan Prinsip 11 melarang fitur yang begitu.
   ⚠ Ukurannya BERBEDA per permukaan, dan itu disengaja: Kantor dipakai dengan
   TETIKUS (28px cukup, dan baris tabel tidak membengkak), permukaan Kerja dengan
   JEMPOL — di sana ia 40px, ambang ADR-032. Satu ukuran untuk keduanya berarti
   salah satunya: tabel desktop yang melar, atau sasaran sentuh yang meleset. */
.kontak { display: inline-flex; align-items: center; gap: .25rem; flex-wrap: wrap; }
.kontak-nomor { margin-right: .15rem; }
.kontak-aksi { display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 8px; flex: none;
  color: var(--brand-text); text-decoration: none;
  background: color-mix(in srgb, var(--brand) 10%, transparent); }
.kontak-aksi:hover { background: color-mix(in srgb, var(--brand) 20%, transparent); }
.kontak-aksi .simbol { width: 15px; height: 15px; }
body.kerja .kontak-aksi { width: 40px; height: 40px; }
body.kerja .kontak-aksi .simbol { width: 18px; height: 18px; }
body.kerja .kontak-aksi:active { transform: scale(.94); }
/* Baris pusat-cari: jangkar baris + tombol kontak berdampingan. Tombolnya WAJIB di
   luar jangkarnya (markup `<a>` di dalam `<a>` tidak sah), jadi pembatas barisnya
   pindah ke pembungkus ini. */
.pusat-baris { display: flex; align-items: center; gap: .3rem;
  border-bottom: 1px solid var(--border); }
.pusat-baris:last-child { border-bottom: 0; }
.pusat-baris > .pusat-item { flex: 1; min-width: 0; border-bottom: 0; }
