/* ============================================================================
 * ui.css — sistem antarmuka retvalweb. Satu-satunya stylesheet aplikasi ini.
 *
 * Menggantikan CoreUI Free Admin Template v5.6.0 (337 KB CSS + 112 KB JS) dan
 * Chart.js (208 KB). Alasannya ada di DESIGN.md; ringkasnya: kerangka admin
 * bawaan itu sendiri yang membuat aplikasi ini terbaca seperti template, dan
 * repo ini tidak punya build step sehingga CSS polos JUSTRU lebih sederhana
 * daripada menimpa 705 custom property milik orang lain.
 *
 * Ditulis dengan @layer supaya urutan menang-kalah eksplisit dan tidak ada
 * satu pun !important di berkas ini.
 *
 * ATURAN YANG MENGATUR SELURUH BERKAS:
 *   Warna jenuh hanya boleh berarti STATUS. Tidak pernah dekorasi, tidak
 *   pernah identitas. Kalau sebuah elemen berwarna, pembaca harus bisa
 *   menjawab "berarti apa?" tanpa menebak.
 * ========================================================================= */

@layer dasar, tata, komponen, utilitas;

/* ── Muka huruf ─────────────────────────────────────────────────────────
 * Di-vendor ke repo: aplikasi ini di balik allowlist jaringan, dan permintaan
 * ke Google Fonts yang gagal berarti halaman jatuh ke font sistem tanpa ada
 * yang tahu kenapa. Tiap bobot berkas tersendiri yang sudah diperiksa unik --
 * satu muka yang cuma dimuat pada satu bobot tidak punya tebal sungguhan.
 */
/* ?v=1 di tiap url font, dan itu BUKAN hiasan. Jawaban /static dikirim
 * dengan Cache-Control immutable satu tahun (lihat retvalweb/aset.py), yang
 * hanya jujur kalau URL-nya berubah saat isinya berubah. URL ui.css sendiri
 * sudah membawa sidik isinya otomatis; berkas font tidak bisa, karena yang
 * menyebutnya adalah berkas CSS ini, bukan template.
 *
 * ATURANNYA: mengganti salah satu berkas .woff2 WAJIB disertai menaikkan
 * angka di baris itu. Tanpa itu, font lama akan menempel di browser orang
 * sampai setahun -- bahkan sesudah Ctrl+Shift+R. Dijaga
 * test_font_di_css_membawa_versi.
 */
/* Tiga muka dari mock Stitch (2026-09-23): Inter untuk isi, Plus Jakarta
 * Sans untuk judul dan angka besar, JetBrains Mono untuk angka tabel.
 * Ketiganya FONT VARIABEL -- satu berkas memuat seluruh rentang bobot, jadi
 * font-weight di sini rentang, bukan satu angka. Bobot yang diminta di luar
 * rentang itu akan dipalsukan browser; rentangnya diambil dari sumbu wght
 * masing-masing berkas. */
@font-face { font-family: "Inter R"; src: url("../fonts/inter-var.woff2?v=1") format("woff2");
             font-weight: 100 900; font-display: swap; }
@font-face { font-family: "Jakarta R"; src: url("../fonts/plusjakartasans-var.woff2?v=1") format("woff2");
             font-weight: 200 800; font-display: swap; }
@font-face { font-family: "JetBrains R"; src: url("../fonts/jetbrainsmono-var.woff2?v=1") format("woff2");
             font-weight: 100 800; font-display: swap; }

@layer dasar {
  /* ── Token ─────────────────────────────────────────────────────────────
   * Palet diambil dari mock Stitch project 3038690048976132313 (Material 3
   * hasil generate, seed #a33900), keputusan Juan 2026-09-23. Nilai terang
   * adalah nilai Stitch apa adanya; nilai gelap diturunkan dari keluarga
   * yang sama karena Stitch tidak menggambar tema gelap.
   *
   * Tema TERANG kini bawaan (lihat data-tema di base.html). Blok :root di
   * bawah tetap tema gelap supaya blok [data-tema="terang"] menimpa, bukan
   * sebaliknya -- susunan yang dijaga test_tema_terang_menurunkan_terang_token_sla.
   *
   * BENTURAN YANG DITERIMA SADAR. DESIGN.md 15 Sep menolak palet ini karena
   * oranye karat duduk di antara merah (lewat) dan amber (mendesak). Bentur-
   * annya masih ada; yang dilakukan untuk meredamnya:
   *   - amber digeser ke kuning zaitun (#765a00), menjauh dari oranye;
   *   - aksen dipakai sebagai BIDANG (tombol utama, nav aktif, fokus) dan
   *     teks tautan, tidak pernah untuk menandai keadaan sebuah order;
   *   - keadaan "lewat" tetap satu-satunya yang berbidang merah, dan setiap
   *     keadaan tetap membawa kata, bukan warna saja.
   */
  :root {
    color-scheme: dark;

    /* Gelap bernuansa biru, bukan hitam: satu keluarga dengan navy merek
     * #0F1C3F (bar atas & bar status), supaya oranye jadi satu-satunya
     * warna hangat di layar. Kontras di atas --surface: --fg 15,2:1,
     * --muted 8,6:1, --faint 5,6:1, --line-kuat 3,8:1. */
    --bg:        #0B1220;
    --surface:   #111827;
    --surface-2: #1a2438;
    /* Baris genap tabel antrean: di antara --surface dan --surface-2,
     * supaya hover (--surface-2) tetap terbedakan dari belangnya. */
    --zebra:     #172136;
    --line:      #243049;
    --line-kuat: #62739a;
    --fg:        #e6eeff;
    --muted:     #aab5cc;
    --faint:     #8791a8;

    /* Oranye sebagai BIDANG (tombol, nav aktif, batang grafik, fokus) dan
     * teks beraksen. Tema gelap #FF6B35, lebih terang dari #E8521A tema
     * terang supaya hidup di latar gelap: sebagai teks 6,3:1 di atas kartu,
     * dan teks navy di atas tombolnya 5,9:1 (putih cuma 2,8:1). Tema terang
     * memakai --accent-teks yang lebih gelap; di sini oranyenya sendiri lolos. */
    --accent:    #FF6B35;
    --accent-fg: #0F1C3F;
    --accent-teks: #FF6B35;
    --accent-lemah: rgb(255 107 53 / 0.14);
    /* Seri grafik kedua (batang per brand di /dashboard). Navy #0F1C3F di
     * tema terang; di sini dicerahkan karena navy hilang di kartu gelap. */
    --grafik-2:  #9fb3e6;

    /* Bar atas: bidang gelap di KEDUA tema, seperti mock (inverse-surface).
     * Token sendiri, bukan --surface: di tema terang ia satu-satunya bidang
     * gelap di layar, dan warnanya tidak boleh ikut berubah bersama kartu. */
    --bar-bg:    #0F1C3F;
    --bar-fg:    #eaf1ff;
    --bar-muted: #aab6cc;
    --bar-line:  #22315a;
    --bar-hover: #1a2a55;

    /* Semantik data. Lima keadaan klaim_status; tidak ada yang lain boleh
     * memakai warna-warna ini. */
    --st-belum:     var(--muted);
    --st-diajukan:  #f0c060;
    --st-disetujui: #7fd49a;
    --st-ditolak:   #ff8a80;
    --st-diterima:  #6fd0cf;

    /* Tenggat banding. Token sendiri, bukan menggeser --st-*, supaya
     * perbaikan kontras di sini tidak diam-diam mengubah rupa setiap lencana
     * status di seluruh aplikasi. Di tema gelap nilainya kebetulan sama. */
    --sla-lewat:    var(--st-ditolak);
    --sla-mendesak: var(--st-diajukan);

    /* Radius 4 / 8 / 12px, tombol 8px (2026-10-19). Dinaikkan dari Stitch
     * (2 / 4 / 8, tombol 6) ke arah landing `/` (kartu 12, tombol 10), satu
     * langkah di bawahnya: kartu, input, dan KPI di sini jauh lebih rapat
     * daripada blok landing, dan 12px di kotak setinggi 34px terbaca lonjong.
     * Modal dan dialog (--r-lg) memakai 12px, sama dengan kartu landing. */
    --r-sm: 4px; --r-md: 8px; --r-lg: 12px; --r-btn: 8px;
    --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 24px; --s-6: 40px;

    /* Skala huruf (2026-10-15). Dulu 20+ ukuran ditulis langsung, termasuk
     * 10 dan 11px di lencana, kepala tabel, dan label. Lantainya kini 12px:
     * di bawah itu huruf kapital berjarak pun tidak terbaca di layar kantor.
     * Teks SVG grafik (.tren-sumbu, .donat-*) sengaja di luar skala: satuan
     * viewBox, bukan px layar. */
    --fs-xs: 0.75rem;    /* 12 — label, lencana, kepala tabel, tombol kecil */
    --fs-sm: 0.8125rem;  /* 13 — isi tabel, input, tombol */
    --fs-base: 0.875rem; /* 14 — teks badan */
    --fs-md: 1rem;       /* 16 — judul kartu, h2 */
    --fs-lg: 1.125rem;   /* 18 — judul modal, angka sekunder */
    --fs-xl: 1.25rem;    /* 20 — judul bagian Dashboard */
    --fs-2xl: 1.5rem;    /* 24 — h1, angka KPI */
    --fs-3xl: 1.75rem;   /* 28 — angka KPI beranda */
    --fs-4xl: 2.25rem;   /* 36 — angka utama laporan di telepon */
    --fs-5xl: 2.75rem;   /* 44 — angka utama laporan */

    --t-cepat: 120ms; --t-sedang: 200ms;
    --ease: cubic-bezier(0.2, 0, 0, 1);

    /* Badan TETAP Inter, walau landing `/` memakai Plus Jakarta Sans untuk
     * badannya. Dicoba 2026-10-19 di Papan Kerja dengan data nyata: di 12-13px
     * (isi tabel, lencana, label) Jakarta punya x-height lebih kecil, spasi
     * antarkata sempit ("sisa 1 hari" terbaca "sisa1hari"), dan angka 1-nya
     * mirip l. Landing membaca Jakarta di 15-19px; di sini teksnya rapat.
     * Penyatu dengan landing tetap ada di judul (--display Jakarta). */
    --sans: "Inter R", ui-sans-serif, system-ui, sans-serif;
    --mono: "JetBrains R", ui-monospace, SFMono-Regular, monospace;
    --display: "Jakarta R", "Inter R", sans-serif;

    --bar-h: 48px;

    /* Lebar rel isi. SATU angka untuk <main>, bar atas, dan bar status --
     * ketiganya harus berbagi tepi kiri yang sama, dan tepi itu tidak boleh
     * bergantung pada tiga aturan yang kebetulan sepakat. Sebelum ini cuma
     * .isi yang dibatasi 1600px dan tidak dipusatkan, jadi di monitor 2560px
     * seluruh aplikasi menempel kiri dengan ~960px ruang mati di kanan. */
    --rel: 1600px;

    /* Elevasi dua bagian: satu bayangan rapat untuk bentuk, satu lebar untuk
     * jarak. Bukan `0 4px 6px rgba(0,0,0,.1)` yang dipakai di mana-mana. */
    --naik: 0 1px 2px rgb(0 0 0 / 0.30), 0 8px 24px -8px rgb(0 0 0 / 0.45);
    /* Tombol ala Stripe (2026-10-15): cincin tipis + bayang 1px, bukan garis
     * --line-kuat. Label dan bayang yang menandai tombol, bukan garisnya. */
    --btn-garis:  rgb(255 255 255 / 0.14);
    --btn-bayang: 0 1px 1px rgb(0 0 0 / 0.40);
  }

  /* Tema terang. Netral sejak 2026-10-19 diambil dari landing `/` (abu
   * bernuansa navy, bukan biru Stitch), supaya halaman sesudah login satu
   * keluarga dengan halaman depannya. Kontras terukur di atas kartu putih:
   * --fg 16,7:1, --muted 8,1:1, --faint 5,6:1, --accent-teks 5,6:1,
   * --accent 3,7:1 (bidang & fokus, syaratnya 3:1), --line-kuat
   * 3,5:1 (batas bidang kontrol, syaratnya 3:1). Yang paling tipis,
   * --faint di atas --surface-2, 4,9:1; --line-kuat di atas --surface-2
   * 3,0:1.
   *
   * Belang tabel --zebra 1,07:1 terhadap kartu putih, sama dengan nilai
   * lama; hover --surface-2 kini 1,08:1 terhadap belang (dulu 1,03:1).
   *
   * Dua nilai digeser sedikit dari Stitch (audit axe 2026-10-15), keduanya
   * gagal tipis di 4,5:1: --faint #687088 hanya 4,47:1 di atas --bg dan
   * --surface-2 (bukan kartu putih), dan --accent-fg #0F1C3F di atas
   * --accent 4,49:1 -- tombol utama di tiap layar. Kini 4,6:1 dan 5,0:1;
   * bedanya tidak terlihat mata, hanya menyeberangkan batasnya. */
  :root[data-tema="terang"] {
    color-scheme: light;
    --bg:        #f5f7fa;
    --surface:   #ffffff;
    --surface-2: #edf0f4;
    --zebra:     #f6f7f9;
    --line:      #dde2ea;
    --line-kuat: #828a9c;
    --fg:        #0f1c3f;
    --muted:     #44506b;
    --faint:     #5d6880;
    --accent:    #E8521A;
    --accent-fg: #0B1220;
    --accent-teks: #b8400f;
    --accent-lemah: rgb(232 82 26 / 0.10);
    --grafik-2:  #0F1C3F;
    --bar-bg:    #0F1C3F;
    --bar-fg:    #eaf1ff;
    --bar-muted: #aab6cc;
    --bar-line:  #2f4058;
    --bar-hover: #2c3d54;
    --st-diajukan:  #765a00;
    --st-disetujui: #1e6b3a;
    --st-ditolak:   #ba1a1a;
    --st-diterima:  #006a6a;
    /* lewat 6,5:1, mendesak 6,5:1 di atas kartu putih. Keduanya lolos AA
     * untuk teks 11px. */
    --sla-lewat:    #ba1a1a;
    --sla-mendesak: #765a00;
    --naik: 0 1px 2px rgb(15 28 63 / 0.06), 0 8px 24px -8px rgb(15 28 63 / 0.14);
    --btn-garis:  rgb(64 68 82 / 0.20);
    --btn-bayang: 0 1px 1px rgb(0 0 0 / 0.10);
  }

  *, *::before, *::after { box-sizing: border-box; }
  /* overflow-x di sini, bukan cuma di body: <html> (bukan <body>) yang jadi
   * root scrolling element sungguhan di mode standar -- overflow-x: hidden
   * di body saja terbukti TIDAK cukup (diperiksa langsung di browser,
   * 2026-09-16). Lihat catatan lengkap di aturan body di bawah.
   *
   * clip, BUKAN hidden (2026-09-17). Keduanya sama-sama memotong luapan
   * mendatar, tapi `hidden` menjadikan elemen ini SCROLL CONTAINER -- dan
   * position: sticky selalu menempel pada leluhur yang bisa digulir, bukan
   * pada viewport. Akibatnya .bar dan .samping, yang sudah ber-position:
   * sticky sejak awal, DIAM-DIAM TIDAK PERNAH LENGKET: keduanya ikut
   * tergulir habis ke atas. Bug ini masuk bersama baris overflow-x itu
   * sendiri pada 2026-09-16 dan tidak pernah terlihat karena halaman tetap
   * tampak wajar -- yang hilang cuma perilaku, bukan rupa.
   *
   * Diukur langsung di browser 2026-09-17, viewport 1280x480, scrollY 400:
   *   overflow-x: hidden -> bar.getBoundingClientRect().top = -400 (ikut
   *                         tergulir), luapan mendatar: tidak ada
   *   overflow-x: clip   -> bar.getBoundingClientRect().top =    0 (lengket),
   *                         luapan mendatar: tidak ada
   *
   * `clip` tidak membuat scroll container sama sekali, jadi ia memotong
   * tanpa merebut sticky. Deklarasi `hidden` dibiarkan DI DEPANNYA sebagai
   * cadangan: browser yang belum mengenal `clip` mengabaikan baris kedua dan
   * mendapat perilaku lama (tidak lengket, tapi juga tidak meluber) alih-alih
   * kehilangan pemotongannya sama sekali. */
  html { -webkit-text-size-adjust: 100%; overflow-x: hidden; overflow-x: clip; }
  body {
    margin: 0; background: var(--bg); color: var(--fg);
    font-family: var(--sans); font-size: var(--fs-base); line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    /* Cadangan terakhir, bukan perbaikan utama: tabel lebar di dalam
     * .gulir (overflow-x: auto) TERBUKTI kadang tetap membuat
     * document.documentElement sendiri melaporkan scrollWidth lebih
     * besar dari viewport walau .gulir/.kartu/.konten/.kerangka semua
     * terukur pas (diperiksa langsung di browser, 2026-09-16, layar Cek
     * rusak) -- kombinasi table th sticky + ancestor overflow-x:auto
     * bersarang. overflow-x di sini memotong jalur pelarian itu tanpa
     * mengubah satu pun perilaku scroll VERTIKAL halaman.
     *
     * clip dengan cadangan hidden, alasan yang SAMA PERSIS dengan aturan
     * html di atas: `hidden` di sini pun menjadikan <body> scroll container
     * dan ikut mematikan sticky pada seluruh keturunannya. */
    overflow-x: hidden;
    overflow-x: clip;
  }
  /* Skala judul Stitch: headline-lg 24px/700, headline-sm 16px/600. */
  h1, h2, h3, h4 { margin: 0; font-family: var(--display); font-weight: 700;
                   letter-spacing: -0.015em; line-height: 1.2; }
  h1 { font-size: var(--fs-2xl); letter-spacing: -0.02em; }
  h2 { font-size: var(--fs-md); font-weight: 600; }
  h3 { font-size: var(--fs-base); }
  p  { margin: 0 0 var(--s-3); }
  a  { color: var(--accent-teks); text-decoration: none; }
  a:hover { text-decoration: underline; text-underline-offset: 3px; }
  /* Tautan di tengah kalimat bergaris bawah sejak awal (2026-10-15): di
   * antara teks --muted, warna saja hanya 1,2:1 dari teks di sekitarnya
   * (WCAG 1.4.1), jadi tautan itu tidak terlihat sebagai tautan. Tautan
   * berkelas (tombol, nav, sel tabel) punya bentuknya sendiri. */
  :where(p, .catatan) a:not([class]) {
    text-decoration: underline; text-underline-offset: 3px;
    text-decoration-color: color-mix(in oklch, currentColor 45%, transparent);
  }

  /* Satu aturan fokus untuk seluruh aplikasi. outline:none tanpa pengganti
   * adalah larangan keras -- keyboard harus selalu bisa melihat posisinya. */
  :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm);
  }

  /* Tiap angka. tabular-nums bukan kerapian: tanpa lebar digit yang sama,
   * kolom bulan tidak bisa dibandingkan dengan mata -- dan membandingkan
   * kolom bulan adalah satu-satunya alasan laporan itu ada. */
  .angka, td.angka, .nilai {
    font-family: var(--mono); font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1, "zero" 1;
  }
  .rp { color: var(--faint); font-weight: 400; margin-right: 0.15em; }
  /* Rincian Dana Return (kompensasi otomatis + penggantian klaim), satu
   * baris per bagian di bawah angkanya. */
  .dana-rinci { display: block; font-family: var(--sans); font-weight: 400;
                white-space: nowrap; }
  /* Penjelasan istilah di bawah rinciannya (komisi AMS), tertutup bawaan:
   * PIC yang sudah paham tidak perlu membacanya tiap membuka modal. */
  /* Baris rincian yang boleh turun baris: kolom Dana Return di modal sempit,
   * dan nowrap mendorong seluruh modal jadi bergulir ke samping. */
  .dana-rinci.dana-lipat { white-space: normal; }
  .dana-penjelasan { font-family: var(--sans); font-weight: 400; margin-top: var(--s-1);
                     max-width: 46ch; }
  .dana-penjelasan summary { cursor: pointer; width: max-content; color: var(--accent-teks); }
  .dana-penjelasan p { margin-top: var(--s-1); color: var(--muted); white-space: normal; }

  ::selection { background: var(--accent-lemah); }
}

@layer tata {
  /* ── Kerangka ──────────────────────────────────────────────────────────
   * Kolom tunggal: bar atas menempel di puncak, isi mengisi sisanya, bar
   * status menempel di kaki. Sebelumnya grid dua kolom dengan sidebar 224px
   * tetap; ditukar 2026-09-22 karena layar kerja utama adalah tabel 13 kolom
   * dan 224px itu justru lebar yang dibutuhkannya (retvalweb/DESIGN.md,
   * "Arsitektur layar").
   *
   * min-width: 0 pada .isi TETAP wajib, dan alasannya tidak berubah bersama
   * tata letaknya: sebagai flex item ia mengambil lebar minimum dari KONTEN
   * terlebar di dalamnya (tabel lebar / form berbaris) lalu memaksa seluruh
   * halaman bisa digulir mendatar. 0 menyerahkan penanganan "terlalu lebar"
   * ke elemen yang memang dirancang untuk itu -- overflow-x: auto pada
   * .gulir, flex-wrap pada .baris.
   */
  .kerangka { display: flex; flex-direction: column; min-height: 100vh; }

  .merek {
    font-family: var(--display); font-weight: 700; font-size: var(--fs-base);
    letter-spacing: -0.01em; line-height: 1.1; flex: none;
    display: block; color: var(--bar-fg); padding-right: var(--s-4);
    border-right: 1px solid var(--bar-line); margin-right: var(--s-2);
  }
  .merek:hover { text-decoration: none; }
  .merek small { display: block; font-family: var(--sans); font-weight: 500;
                 font-size: var(--fs-xs); color: var(--bar-muted); letter-spacing: 0.04em;
                 text-transform: uppercase; margin-top: 1px; }

  /* ── Nav mendatar ──────────────────────────────────────────────────────
   * Empat tautan harian rata di bar, tiga kelompok sisanya di <details>.
   * min-width: 0 + flex-wrap supaya bar TIDAK pernah memaksa halaman
   * melebar: kalau tautannya tidak muat (admin, 900-1200px), nav turun ke
   * baris kedua dan bar ikut meninggi.
   *
   * BUKAN overflow-x: auto, seperti sejak a16ce1a sampai 2026-09-23.
   * overflow-x selain visible memaksa overflow-y ikut auto, jadi menu
   * .nav-lipat yang menjulur di bawah bar terpotong habis: <details>
   * terbuka tapi menunya tak terlihat dan tak bisa diklik. Memindah blok
   * penampung menu ke .bar sudah dicoba -- menunya lolos dari potongan,
   * tapi tidak ikut bergeser saat nav digulir.
   */
  .nav { display: flex; flex-wrap: wrap; align-items: center; gap: 2px;
         min-width: 0; padding-block: 4px; }
  .nav > a, .nav summary {
    display: flex; align-items: center; gap: var(--s-2); white-space: nowrap;
    padding: 6px var(--s-3); border-radius: var(--r-md);
    color: var(--bar-muted); font-size: var(--fs-sm); font-weight: 500;
    cursor: pointer; user-select: none;
  }
  .nav > a:hover, .nav summary:hover {
    background: var(--bar-hover); color: var(--bar-fg); text-decoration: none;
  }
  /* Item aktif: bidang aksen penuh, seperti mock Stitch. Ini salah satu
   * tempat aksen boleh jadi bidang -- ia menandai POSISI, bukan keadaan data.
   * Kelompok yang anaknya aktif cukup diberi garis bawah: bidang penuh di
   * dua tempat membuat mata tidak tahu mana yang sebenarnya halaman ini. */
  .nav > a[aria-current="page"] {
    background: var(--accent); color: var(--accent-fg);
  }
  .nav-grup[data-aktif] > summary {
    color: var(--bar-fg); box-shadow: inset 0 -2px 0 var(--accent);
  }
  .nav svg { width: 15px; height: 15px; flex: none; opacity: 0.6; }
  .nav > a[aria-current="page"] svg, .nav > a:hover svg { opacity: 1; }

  /* <details> sebagai menu. Panahnya dibuat sendiri (ikon "turun"), jadi
   * penanda bawaan dimatikan di kedua sintaks -- ::marker untuk mesin baru,
   * ::-webkit-details-marker untuk Safari lama yang masih dipakai di lapangan. */
  .nav-grup { position: relative; flex: none; }
  .nav-grup > summary { list-style: none; }
  .nav-grup > summary::marker,
  .nav-grup > summary::-webkit-details-marker { display: none; content: ""; }
  .nav-grup > summary svg { width: 13px; height: 13px; opacity: 0.5; }
  .nav-grup[open] > summary svg { transform: rotate(180deg); }
  /* Menu lipat ikut bidang bar, bukan kartu terang: ia menjulur dari bar,
   * dan di laci mobile ia duduk DI DALAM bidang yang sama. Satu set token
   * untuk keduanya berarti tidak ada kombinasi warna yang tidak pernah
   * diukur. */
  .nav-lipat {
    position: absolute; top: calc(100% + 4px); left: 0; z-index: 30;
    min-width: 190px; padding: var(--s-1);
    display: flex; flex-direction: column; gap: 1px;
    background: var(--bar-bg); border: 1px solid var(--bar-line);
    border-radius: var(--r-md); box-shadow: var(--naik);
  }
  .nav-lipat a {
    display: flex; align-items: center; gap: var(--s-3); white-space: nowrap;
    padding: 7px var(--s-3); border-radius: var(--r-sm);
    color: var(--bar-muted); font-size: var(--fs-sm); font-weight: 500;
  }
  .nav-lipat a:hover { background: var(--bar-hover); color: var(--bar-fg);
                       text-decoration: none; }
  .nav-lipat a[aria-current="page"] {
    background: var(--bar-hover); color: var(--bar-fg);
    box-shadow: inset 2px 0 0 var(--accent);
  }

  .bar {
    position: sticky; top: 0; z-index: 20; min-height: var(--bar-h);
    display: flex; align-items: center; gap: var(--s-2);
    /* Latar SENGAJA selebar jendela -- blur dan garis bawah sticky harus
     * menyentuh kedua tepi, kalau tidak ia terlihat seperti kartu yang
     * mengambang. Yang ditarik ke rel cuma ISI-nya. max() menangani layar
     * sempit sendiri: di bawah --rel hasil calc negatif, jadi yang menang
     * --s-5, persis seperti sebelumnya. */
    padding-block: 0;
    padding-inline: max(var(--s-5), calc((100% - var(--rel)) / 2 + var(--s-5)));
    /* Bidang pejal, bukan tembus pandang: blur di atas navy gelap cuma
     * membuat isi yang tergulir di bawahnya tampak kotor. */
    background: var(--bar-bg); color: var(--bar-fg);
    border-bottom: 1px solid var(--bar-line);
  }
  .bar-kanan { margin-left: auto; display: flex; align-items: center;
               gap: var(--s-2); flex: none; padding-left: var(--s-3); }
  /* Desktop: nav rata tengah JENDELA, bukan tengah sisa ruang di antara
   * merek dan bar-kanan -- keduanya tidak sama lebar, jadi flex biasa akan
   * menaruhnya miring. Dua kolom sisi sama-sama 1fr tapi tidak pernah lebih
   * sempit dari isinya; kolom tengah minmax(0, max-content), jadi kalau
   * ruang tidak cukup (admin, 900-1200px) sisi tetap utuh dan nav yang
   * membungkus di kolomnya sendiri. Di bawah 900px bar kembali flex: nav
   * sudah jadi laci, bukan kolom. Garis pemisah merek dilepas: ia
   * memisahkan merek dari nav yang dulu menempel di sebelahnya, dan kini
   * berdiri sendirian di tengah ruang kosong. */
  @media (min-width: 900px) {
    .bar { display: grid; align-items: center;
           grid-template-columns: minmax(max-content, 1fr) minmax(0, max-content) minmax(max-content, 1fr); }
    .merek { justify-self: start; border-right: 0; margin-right: 0; }
    .bar-kanan { justify-self: end; }
  }
  .bar-siapa { font-size: var(--fs-xs); color: var(--bar-muted); }
  .bar-siapa b { color: var(--bar-fg); font-weight: 600; }
  .bar-peran { font-family: var(--mono); font-size: var(--fs-xs); color: var(--bar-muted); }
  /* Tombol sunyi di bar memakai warna bar -- --muted terang di atas navy
   * terukur di bawah 3:1. */
  .bar .btn.sunyi { color: var(--bar-muted); }
  .bar .btn.sunyi:hover { background: var(--bar-hover); color: var(--bar-fg); }
  .bar :focus-visible { outline-color: var(--bar-fg); }

  /* ── Bar status ────────────────────────────────────────────────────────
   * Menempel di kaki jendela, bukan di kaki dokumen. Paginasi antrean 50
   * baris selalu di bawah lipatan kalau ia ikut menggulir, dan kontrol yang
   * harus dicari memperlambat pekerjaan yang paling sering diulang.
   * Hanya dirender kalau layarnya benar-benar mengisi (lihat base.html).
   */
  .bar-status {
    position: sticky; bottom: 0; z-index: 20; margin-top: auto;
    display: flex; align-items: center; gap: var(--s-4); flex-wrap: wrap;
    padding-block: var(--s-2);
    padding-inline: max(var(--s-5), calc((100% - var(--rel)) / 2 + var(--s-5)));
    font-size: var(--fs-xs);
    /* Bidang gelap yang sama dengan bar atas, di kedua tema: kepala dan kaki
     * jendela satu pasang. Warna tombol & teks di dalamnya ada di lapisan
     * komponen/utilitas di bawah -- .btn dan .muted tinggal di sana. */
    background: var(--bar-bg); color: var(--bar-fg);
    border-top: 1px solid var(--bar-line);
  }
  .bar-status :focus-visible { outline-color: var(--bar-fg); }
  .bar-status .paginasi { margin: 0; }
  .bar-status-kanan { margin-left: auto; }

  /* width:100% WAJIB ikut, dan ini jebakan yang sudah sekali menggigit:
   * margin-inline:auto pada anak flex MEMATIKAN align-items:stretch, jadi
   * <main> jatuh ke shrink-to-fit dan lebarnya ikut isi halaman -- diukur
   * 1.259px di layar 2.545px, berbeda-beda per layar. width:100% mengembalikan
   * lebar penuh, max-width memotongnya di rel, auto memusatkan sisanya.
   *
   * .isi.tamu (login) menimpa max-width jadi none, jadi auto di sana tidak
   * berpengaruh -- halaman itu memusatkan isinya lewat place-items. */
  .isi { flex: 1; min-width: 0; padding: var(--s-5);
         width: 100%; max-width: var(--rel); margin-inline: auto; }

  .kepala { margin-bottom: var(--s-5); }
  .kepala p { color: var(--muted); font-size: var(--fs-sm); max-width: 68ch; margin: var(--s-1) 0 0; }

  .tirai { display: none; }

  /* ── Di bawah 900px: nav mendatar jadi laci ────────────────────────────
   * Bukan nav desktop yang dikecilkan -- ia benar-benar berubah bentuk,
   * sama seperti sidebar dulu. Kelompok <details> dibuka paksa di dalam
   * laci: ruang vertikal di sana tidak langka, dan menu-di-dalam-menu
   * adalah dua ketukan untuk sesuatu yang muat utuh.
   */
  @media (max-width: 899.98px) {
    .nav {
      position: fixed; inset: 0 auto 0 0; width: min(280px, 82vw); z-index: 40;
      flex-direction: column; flex-wrap: nowrap; align-items: stretch; overflow-y: auto;
      gap: 1px; padding: var(--s-4) var(--s-2);
      background: var(--bar-bg); border-right: 1px solid var(--bar-line);
      transform: translateX(-100%); box-shadow: var(--naik);
    }
    :root:has(#laci:checked) .nav { transform: none; }
    :root:has(#laci:checked) .tirai { opacity: 1; pointer-events: auto; }
    .tirai { display: block; position: fixed; inset: 0; z-index: 30;
             background: oklch(0 0 0 / 0.5); opacity: 0; pointer-events: none; }
    .nav-grup[data-aktif] > summary {
      box-shadow: inset 2px 0 0 var(--accent);
    }
    /* Menu jadi daftar bersarang: posisinya statis, bingkainya dilepas,
     * buka-tutupnya TETAP <details> yang sama. Sempat dicoba memaksanya
     * selalu terbuka di laci -- tidak bisa: `open` adalah atribut, dan CSS
     * tidak bisa memasangnya. Menirunya dengan display pada anak <details>
     * yang tertutup bergantung pada detail implementasi UA yang berbeda
     * antar-mesin. Satu ketukan tambahan lebih murah daripada nav yang
     * runtuh di satu browser. */
    .nav-grup { flex: 1 0 auto; }
    .nav-lipat { position: static; min-width: 0; padding: 0 0 0 var(--s-3);
                 margin-top: 1px; background: none; border: 0;
                 border-left: 1px solid var(--bar-line); box-shadow: none; }
    .isi { padding: var(--s-4); }
    .bar { padding: 0 var(--s-4); }
    .bar-status { padding: var(--s-2) var(--s-4); }
    .merek { border-right: 0; margin-right: 0; }
  }

  /* Di lebar telepon bar atas meluber 34px ke kanan -- diukur di browser pada
   * 375px, 2026-09-22. Sebabnya .merek dan .bar-kanan dua-duanya flex: none,
   * jadi tidak ada yang mengalah saat namanya panjang. Yang mengalah adalah
   * identitas: label peran hilang (ia sudah tertulis di setiap layar yang
   * memagari peran), dan nama dipotong titik-titik. Nama TIDAK ikut hilang --
   * PIC harus tetap bisa melihat ia masuk sebagai siapa sebelum menandai
   * banding atas namanya sendiri. */
  @media (max-width: 719.98px) {
    /* padding-right dilepas: ia jarak ke garis pemisah, dan garisnya sudah
     * tidak ada di lebar ini. Tanpa itu "Return Management" (2026-10-09)
     * terpotong 6px di 375px. */
    .merek { flex: 0 1 auto; min-width: 0; overflow: hidden;
             text-overflow: ellipsis; white-space: nowrap; padding-right: 0; }
    .bar-kanan { flex: 0 1 auto; min-width: 0; padding-left: var(--s-2); }
    .bar-siapa { min-width: 0; overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
    .bar-peran { display: none; }
    /* Tanpa ini bar status jadi TIGA baris setinggi 145px di layar 812px --
     * seperenam layar untuk paginasi. Diukur di browser pada 375px.
     * Sebabnya "Halaman 1 dari 50 · 4.989 baris" 223px duduk sebaris dengan
     * dua tombol 95px dan 87px, dan margin-left: auto memaksa Segarkan turun
     * lagi. Kalimatnya dinaikkan ke barisnya sendiri: dua baris, dan setiap
     * kontrol tetap terlihat -- tidak satu pun disembunyikan. */
    /* TIDAK sticky di lebar telepon. Bar ini menempel di kaki jendela supaya
     * paginasi antrean 50 baris tidak selalu di bawah lipatan -- alasan yang
     * hanya berlaku ketika tabelnya lebih tinggi dari jendelanya dan sisa
     * layarnya masih lapang. Di 375x812 ia tiga baris setinggi 127px, yaitu
     * seperenam layar yang dipakai permanen oleh kontrol yang dipakai sekali
     * per halaman. Di sini ia ikut menggulir seperti isi biasa. */
    .bar-status { position: static; gap: var(--s-2) var(--s-3); }
    .bar-status-kanan { margin-left: 0; }
    /* display: contents melarutkan kotak .paginasi sehingga tombolnya jadi
     * anak langsung .bar-status. Tanpa itu paginasi tetap satu blok yang
     * membungkus di dalam dirinya sendiri, dan Segarkan terdorong ke baris
     * ketiga. Bukan mengubah .paginasi di sembilan layar lain -- hanya
     * ketika ia duduk di dalam bar status, pada lebar telepon. */
    .bar-status .paginasi { display: contents; }
    .bar-status .posisi { flex: 1 0 100%; order: -1; }
  }
  @media (prefers-reduced-motion: no-preference) {
    .tirai { transition: opacity var(--t-sedang) var(--ease); }
    @media (max-width: 899.98px) {
      .nav { transition: transform var(--t-sedang) var(--ease); }
    }
  }
}

@layer komponen {
  /* ── Kartu ──────────────────────────────────────────────────────────── */
  .kartu { background: var(--surface); border: 1px solid var(--line);
           border-radius: var(--r-md); }
  .kartu + .kartu, .blok + .blok { margin-top: var(--s-5); }
  .kartu-kepala {
    display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap;
    padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line);
  }
  .kartu-kepala .sub { font-size: var(--fs-xs); color: var(--faint);
                       font-family: var(--mono); }
  .kartu-badan { padding: var(--s-4); }

  /* ── Kartu angka ─────────────────────────────────────────────────────
   * Label dulu, baru angka: pembaca perlu tahu angka APA yang ia lihat
   * sebelum melihat angkanya. Dulu terbalik, dan tiap kartu dicat warna
   * jenuh berbeda yang tidak membawa arti apa pun.
   */
  .kisi-stat { display: grid; gap: var(--s-3);
               grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
  .stat { background: var(--surface); border: 1px solid var(--line);
          border-radius: var(--r-md); padding: var(--s-4); }
  .stat-label { font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.06em;
                text-transform: uppercase; color: var(--faint); }
  .stat-nilai { font-family: var(--display); font-weight: 700; font-size: var(--fs-2xl);
                line-height: 1.1; font-variant-numeric: tabular-nums;
                margin: var(--s-2) 0 var(--s-1); overflow-wrap: anywhere; }
  .stat-nilai .satuan { font-family: var(--sans); font-size: var(--fs-base);
                        font-weight: 500; color: var(--muted); margin-left: 0.3em; }
  .stat-nilai.kecil { font-family: var(--mono); font-size: var(--fs-md); font-weight: 500; }
  /* Rumah tetap untuk syarat -- terlihat selalu, tidak pernah menyaingi. */
  .stat-syarat { font-size: var(--fs-xs); color: var(--muted); line-height: 1.45; }
  .stat.perhatian { border-left: 2px solid var(--st-diajukan); }

  /* ── Tabel ───────────────────────────────────────────────────────────
   * Tanpa bingkai kartu yang merembes ke baris. Kepala lengket supaya
   * nama kolom tidak hilang di tabel panjang -- laporan ini 12 kolom
   * bulan dan orang menggulir mencari satu brand.
   */
  .gulir { overflow-x: auto; }
  table.tabel { width: 100%; border-collapse: collapse; }
  .tabel th {
    position: sticky; top: 0; z-index: 1; background: var(--surface);
    font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--faint); text-align: left;
    padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--line);
    white-space: nowrap;
  }
  .tabel td { padding: 10px var(--s-3); border-bottom: 1px solid
              color-mix(in oklch, var(--line) 60%, transparent); vertical-align: top; }
  .tabel tbody tr:last-child td { border-bottom: 0; }
  .tabel tbody tr:hover td { background: var(--surface-2); }
  .tabel .kanan, .tabel th.kanan { text-align: right; }
  .tabel .rapat td { padding: 6px var(--s-3); }
  /* Kolom pertama lengket: di pivot 12 bulan, tanpa ini orang kehilangan
   * nama brand begitu menggulir ke Agustus. */
  .tabel.brand-lengket td:nth-child(2), .tabel.brand-lengket th:nth-child(2) {
    position: sticky; left: 0; background: var(--surface); z-index: 2;
    border-right: 1px solid var(--line);
  }
  .tabel.brand-lengket tbody tr:hover td:nth-child(2) { background: var(--surface-2); }

  /* Cek rusak: kolom PERTAMA (Order ID) lengket, bukan kedua -- pola sama
   * dengan .brand-lengket, target beda karena tabel ini tidak punya kolom
   * nomor urut di depan Order ID. Tanpa ini, identitas baris hilang begitu
   * digulir ke kolom aksi (Putuskan/Banding) di ujung kanan. */
  .tabel.order-lengket td:nth-child(1), .tabel.order-lengket th:nth-child(1) {
    position: sticky; left: 0; background: var(--surface); z-index: 2;
    border-right: 1px solid var(--line);
  }
  .tabel.order-lengket tbody tr:hover td:nth-child(1) { background: var(--surface-2); }

  /* ── Lencana status: satu-satunya tempat warna jenuh dibenarkan ─────── */
  .lencana {
    display: inline-block; font-size: var(--fs-xs); font-weight: 600;
    letter-spacing: 0.03em; padding: 1px 6px; border-radius: var(--r-sm);
    border: 1px solid currentColor; white-space: nowrap; color: var(--muted);
  }
  .lencana.belum     { color: var(--st-belum); }
  .lencana.diajukan  { color: var(--st-diajukan); }
  .lencana.disetujui { color: var(--st-disetujui); }
  .lencana.ditolak   { color: var(--st-ditolak); }
  .lencana.diterima  { color: var(--st-diterima); }
  /* Gugatan pembeli TikTok: hue yang sama dengan pasangan bandingnya
   * (kuning = menunggu, hijau = kita menang, merah = kita kalah), garis
   * PUTUS-PUTUS supaya tidak terbaca sebagai banding kita. Warna menjawab
   * "bagaimana hasilnya", garis menjawab "siapa yang membawanya ke platform". */
  .lencana.digugat        { color: var(--st-diajukan);  border-style: dashed; }
  .lencana.gugatan_menang { color: var(--st-disetujui); border-style: dashed; }
  .lencana.gugatan_kalah  { color: var(--st-ditolak);   border-style: dashed; }
  .lencana.netral    { color: var(--faint); border-color: var(--line-kuat); }

  /* ── Tenggat banding (SLA) ───────────────────────────────────────────
   * Sengaja BUKAN .lencana, meski sekilas mirip. Lencana menyatakan status
   * yang sudah terjadi; ini menyatakan sisa waktu yang masih berjalan, dan
   * dua hal itu berdampingan di kartu yang sama. Kalau bentuknya sama,
   * "DIAJUKAN" dan "sisa 3 hari" terbaca sebagai dua nilai dari daftar yang
   * sama padahal keduanya sumbu yang berbeda.
   *
   * Hanya "lewat" yang diberi bidang berisi. Itu satu-satunya keadaan di
   * layar ini yang tidak bisa diperbaiki dengan bekerja lebih cepat, dan
   * satu-satunya yang boleh menghentikan mata. "aman" sepenuhnya sunyi --
   * ia tidak menuntut apa pun, jadi ia tidak meminta perhatian.
   *
   * Angkanya memakai tabular-nums supaya "sisa 9 hari" dan "sisa 14 hari"
   * tetap sejajar saat baris-baris ini bertumpuk ke bawah.
   */
  .sla {
    display: inline-block; font-size: var(--fs-xs); font-weight: 600;
    letter-spacing: 0.02em; font-variant-numeric: tabular-nums;
    padding: 1px 6px; border-radius: var(--r-sm); white-space: nowrap;
    border: 1px solid transparent;
  }
  .sla.lewat {
    color: var(--sla-lewat); border-color: currentColor;
    background: color-mix(in oklch, var(--sla-lewat) 14%, transparent);
  }
  .sla.mendesak { color: var(--sla-mendesak); border-color: currentColor; }
  /* --muted, bukan --faint: 11px di atas --faint terukur 3,6:1 dan ini teks
   * yang membawa angka, bukan hiasan. Tetap sunyi, tetap terbaca. */
  .sla.aman     { color: var(--muted); padding-left: 0; padding-right: 0; }

  /* ── Tombol ──────────────────────────────────────────────────────────── */
  /* Gaya Stripe Dashboard (pilihan Juan 2026-10-15, dari lima referensi):
   * permukaan putih, cincin tipis, bayang 1px, huruf 600, sudut 6px, ikon
   * di depan label berwarna --muted. Dulu: isi --surface-2 biru pucat +
   * garis --line-kuat -- terbaca "jadul". */
  .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    font: inherit; font-weight: 600; font-size: var(--fs-sm);
    padding: 7px var(--s-3); min-height: 34px;
    border: 1px solid var(--btn-garis); border-radius: var(--r-btn);
    background: var(--surface); color: var(--fg); cursor: pointer;
    box-shadow: var(--btn-bayang);
    text-decoration: none; white-space: nowrap;
  }
  .btn:hover { background: color-mix(in oklch, var(--surface) 94%, var(--fg));
               border-color: color-mix(in oklch, var(--btn-garis), var(--fg) 15%);
               text-decoration: none; }
  .btn.utama { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
  .btn.utama:hover { background: color-mix(in oklch, var(--accent) 88%, white); }
  .btn.sunyi { background: transparent; border-color: transparent; color: var(--muted);
               box-shadow: none; font-weight: 500; }
  .btn.sunyi:hover { background: var(--surface-2); color: var(--fg); }
  .btn.kecil { min-height: 28px; padding: 3px var(--s-2); font-size: var(--fs-xs); }
  .btn[aria-disabled="true"], .btn:disabled { opacity: 0.45; pointer-events: none; }
  .btn svg { width: 14px; height: 14px; flex: none; }
  /* :where() supaya spesifisitasnya tetap .btn svg -- pewarnaan ulang di
   * tempat lain (.bar-status .btn svg) harus bisa menang tanpa !important. */
  .btn:where(:not(.utama, .sunyi)) svg { color: var(--muted); }
  .btn:where(:not(.utama, .sunyi)):hover svg { color: inherit; }
  /* Tombol laci HARUS di lapisan ini, sesudah .btn. Dulu aturannya di
   * lapisan tata, dan display: inline-flex milik .btn di lapisan yang
   * lebih akhir selalu menang: tombol laci tampil di desktop, tempat ia
   * tidak membuka apa-apa (ditemukan 2026-09-23). */
  .btn.buka-laci { display: none; }
  @media (max-width: 899.98px) { .btn.buka-laci { display: inline-flex; } }
  /* Checkbox-nya ikut: di desktop ia tidak membuka apa-apa, dan sebagai .sr
   * ia tetap menerima Tab -- fokus yang mendarat di sesuatu yang tak
   * terlihat. Di ponsel fokusnya pindah ke tombol yang terlihat
   * (2026-10-15). */
  @media (min-width: 900px) { #laci { display: none; } }
  #laci:focus-visible + .buka-laci { outline: 2px solid var(--bar-fg); outline-offset: 2px; }
  /* Tombol keputusan: warnanya BUKAN dekorasi -- ia arah tindakan, dan
   * ketiganya sengaja setara secara visual supaya tidak ada yang terpencet
   * karena paling menonjol. */
  .btn.ya:hover  { border-color: var(--st-disetujui); color: var(--st-disetujui); }
  .btn.tidak:hover { border-color: var(--st-ditolak); color: var(--st-ditolak); }

  @media (prefers-reduced-motion: no-preference) {
    .btn { transition: background-color var(--t-cepat) var(--ease),
                       border-color var(--t-cepat) var(--ease),
                       color var(--t-cepat) var(--ease); }
  }

  /* ── Kontrol ─────────────────────────────────────────────────────────── */
  .medan { display: flex; flex-direction: column; gap: 5px; }
  .medan > label { font-size: var(--fs-xs); font-weight: 500; color: var(--muted); }
  .input, select.input, textarea.input {
    font: inherit; font-size: var(--fs-sm); padding: 7px var(--s-3); min-height: 34px;
    background: var(--surface-2); color: var(--fg);
    border: 1px solid var(--line-kuat); border-radius: var(--r-md); width: 100%;
  }
  .input:hover { border-color: var(--muted); }
  .input:focus { border-color: var(--accent); }
  .input::placeholder { color: var(--faint); }
  .input[aria-invalid="true"] { border-color: var(--st-ditolak); }
  select.input { appearance: none; padding-right: 30px;
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: right 14px center, right 9px center;
    background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
  /* Tinggi kontrol satu baris dikunci, bukan min-height: input tanggal
   * Chrome membawa tinggi dalamnya sendiri dan jadi 38px di sebelah
   * input lain yang 36px (2026-10-15). Dua ukuran saja, sama dengan
   * tombol: 36px biasa, 28px .kecil. */
  :is(input, select).input { block-size: 36px; }
  /* Kotak tempel nomor pesanan tumbuh mengikuti isinya (field-sizing,
   * Chrome 123+): 300 nomor yang ditempel terlihat sampai ~15 baris, baru
   * kotaknya bergulir. Lantainya tetap setinggi rows semula. */
  textarea.input { field-sizing: content; min-block-size: calc(3lh + 16px);
                   max-block-size: min(50vh, calc(15lh + 16px)); }
  textarea.input[rows="4"] { min-block-size: calc(4lh + 16px); }
  :is(input, select).input.kecil { block-size: 28px; min-height: 0; padding: 3px var(--s-2);
                                   font-size: var(--fs-xs); }
  select.input.kecil { padding-right: 26px;
    background-position: right 12px center, right 7px center; }

  /* Pilihan bawaan yang bisa diberi gaya (Chrome 135+; ops memakai Chrome
   * dan Edge 152+). Daftar pilihan ikut tema, bukan kotak putih sistem
   * di tema gelap. Hanya <select> polos: [data-combobox] diganti kotak
   * cari oleh ui.js dan tidak boleh kehilangan pencariannya. Browser lain
   * tetap memakai panah gradien di atas. */
  @supports (appearance: base-select) {
    select.input:not([data-combobox]),
    select.input:not([data-combobox])::picker(select) { appearance: base-select; }
    select.input:not([data-combobox]) { background-image: none; display: flex;
                                        align-items: center; padding-right: var(--s-2); }
    select.input:not([data-combobox])::picker-icon {
      color: var(--muted); margin-left: auto; padding-left: var(--s-2);
    }
    select.input:not([data-combobox])::picker(select) {
      background: var(--surface); color: var(--fg);
      border: 1px solid var(--line-kuat); border-radius: var(--r-md);
      padding: var(--s-1); margin-block: var(--s-1);
      box-shadow: 0 8px 24px rgb(0 0 0 / 0.25);
    }
    select.input:not([data-combobox]) option {
      padding: 6px var(--s-2); border-radius: var(--r-sm); font-size: var(--fs-sm);
    }
    select.input:not([data-combobox]) option:hover,
    select.input:not([data-combobox]) option:focus-visible { background: var(--surface-2); outline: none; }
    select.input:not([data-combobox]) option:checked { font-weight: 600; }
    select.input:not([data-combobox]) option::checkmark { color: var(--accent); }
  }
  .galat-medan { font-size: var(--fs-xs); color: var(--st-ditolak); }

  /* Combobox saringan (ui.js, pasangCombobox). Kotaknya memakai rupa
   * select.input yang sama persis -- termasuk panahnya -- supaya layar
   * dengan dan tanpa JavaScript terlihat sama; bedanya cuma bisa diketik. */
  .combo { position: relative; width: 100%; }
  .combo-kotak { padding-right: 30px; text-overflow: ellipsis;
    background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                      linear-gradient(135deg, currentColor 50%, transparent 50%);
    background-position: right 14px center, right 9px center;
    background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
  .combo-buka { position: absolute; top: 0; right: 0; bottom: 0; width: 30px;
    padding: 0; border: 0; background: transparent; cursor: pointer; }
  .combo-daftar {
    position: absolute; z-index: 30; top: calc(100% + 4px); left: 0;
    min-width: 100%; max-width: min(320px, calc(100vw - 32px));
    max-height: 280px; overflow-y: auto; margin: 0; padding: var(--s-1) 0;
    list-style: none; background: var(--surface);
    border: 1px solid var(--line-kuat); border-radius: var(--r-md);
    box-shadow: var(--naik); font-size: var(--fs-sm);
  }
  .combo-daftar li { padding: 6px var(--s-3); cursor: pointer; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
  .combo-daftar li[aria-selected="true"] { font-weight: 600; }
  /* Satu tanda untuk "aktif", dipakai keyboard dan tetikus sekaligus: dua
   * sorotan yang berbeda di satu daftar membuat orang tidak tahu Enter
   * akan memilih yang mana. */
  .combo-daftar li[data-aktif] { background: var(--surface-2);
    box-shadow: inset 2px 0 0 var(--accent); }
  .combo-daftar .combo-kosong { color: var(--muted); cursor: default; }
  /* Pilih-banyak (2026-10-03): kotak centang di depan tiap baris -- tanda
   * bahwa daftar ini bisa dicentang lebih dari satu, sebelum orang mencoba.
   * Bobot tebal mode tunggal dilepas: di sini centangnya yang bicara. "Semua"
   * dipisah garis karena ia mengosongkan, bukan satu pilihan di antara yang
   * lain. */
  .combo-banyak li { display: flex; align-items: center; gap: var(--s-2); }
  .combo-banyak li[aria-selected="true"] { font-weight: inherit; }
  .combo-banyak li[role="option"]::before { content: ""; flex: none;
    width: 14px; height: 14px; border: 1px solid var(--line-kuat);
    border-radius: var(--r-sm); background: var(--surface); }
  .combo-banyak li[aria-selected="true"]::before {
    border-color: var(--accent);
    background: var(--accent) no-repeat center / 10px 10px
      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M1.5 5.2 4 7.5l4.5-5' fill='none' stroke='%230F1C3F' stroke-width='1.8'/%3E%3C/svg%3E"); }
  .combo-banyak .combo-semua { border-bottom: 1px solid var(--line);
    margin-bottom: var(--s-1); padding-bottom: 7px; }

  /* Pencarian nomor pesanan di Papan Kerja: cukup lebar untuk 18 digit
   * Order ID TikTok dalam huruf mono tanpa terpotong. */
  /* Pembungkus yang hanya ada untuk ui.js (wilayah tukar): tanpa kotak sendiri. */
.larut { display: contents; }
.cari-nomor .input { display: block; width: 24ch; max-width: 100%; appearance: none; }

  .saring { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s-3);
            padding: var(--s-3) var(--s-4); background: var(--surface);
            border: 1px solid var(--line); border-radius: var(--r-md);
            margin-bottom: var(--s-5); }
  .saring .medan { min-width: 150px; }
  .saring .dorong { margin-left: auto; display: flex; gap: var(--s-2); align-items: flex-end; }

  /* ── Catatan ─────────────────────────────────────────────────────────
   * Alert penuh warna menarik mata lebih keras daripada angka yang
   * dijaganya. Diturunkan jadi catatan bergaris kiri: tetap terlihat,
   * tidak lagi berteriak.
   */
  .catatan {
    display: flex; gap: var(--s-3); padding: var(--s-3) var(--s-4);
    background: var(--surface); border: 1px solid var(--line);
    border-left: 2px solid var(--line-kuat); border-radius: var(--r-md);
    font-size: var(--fs-sm); line-height: 1.55; color: var(--muted);
    margin-bottom: var(--s-5);
  }
  .catatan b, .catatan strong { color: var(--fg); font-weight: 600; }
  .catatan.perhatian { border-left-color: var(--st-diajukan); }
  .catatan.buruk     { border-left-color: var(--st-ditolak); }
  .catatan.info      { border-left-color: var(--accent); }
  .catatan > svg { width: 16px; height: 16px; flex: none; margin-top: 2px; color: var(--faint); }

  /* ── Keadaan kosong / galat ──────────────────────────────────────────
   * Tidak pernah "Tidak ada data" telanjang: tiap keadaan kosong
   * menyebutkan SEBABNYA dan langkah berikutnya.
   */
  .kosong { padding: var(--s-6) var(--s-4); text-align: center; color: var(--muted); }
  .kosong p { font-size: var(--fs-sm); max-width: 46ch; margin: 0 auto var(--s-3); }
  .kosong .kosong-judul { font-family: var(--sans); font-size: var(--fs-base); font-weight: 600;
                          color: var(--fg); margin-bottom: var(--s-2); }
  /* Halaman galat (galat.html): sempit, sedikit turun dari bar. */
  .galat { max-width: 40rem; padding-block: var(--s-6); }

  /* ── Paginasi ────────────────────────────────────────────────────────── */
  .paginasi { display: flex; align-items: center; justify-content: space-between;
              gap: var(--s-3); margin-top: var(--s-4); flex-wrap: wrap; }
  .paginasi .posisi { font-family: var(--mono); font-size: var(--fs-xs); color: var(--faint); }

  /* ── Grafik batang (SVG inline, menggantikan Chart.js) ────────────────
   * Enam brand, satu sumbu. Chart.js 208 KB tidak sepadan untuk ini, dan
   * menulisnya sendiri memberi kontrol tipografi yang sama dengan sisa
   * aplikasi -- sumbu memakai muka dan ukuran yang sama dengan tabel.
   */
  .batang { display: grid; gap: var(--s-2); }
  .batang-baris { display: grid; grid-template-columns: 140px 1fr auto;
                  align-items: center; gap: var(--s-3); }
  .batang-nama { font-size: var(--fs-sm); color: var(--muted); overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
  .batang-jalur { height: 18px; background: var(--surface-2); border-radius: var(--r-sm);
                  overflow: hidden; }
  /* display: block WAJIB: .batang-isi itu <span>, dan span inline
   * mengabaikan width/height -- tanpa ini batangnya tidak pernah tergambar
   * (sejak 9f2dcc2, di /dashboard dan /manajemen). */
  .batang-isi { display: block; height: 100%; background: var(--accent);
                border-radius: var(--r-sm); }
  .batang.kedua .batang-isi { background: var(--grafik-2); }
  .batang-nilai { font-family: var(--mono); font-size: var(--fs-xs);
                  font-variant-numeric: tabular-nums; color: var(--fg); }
  @media (prefers-reduced-motion: no-preference) {
    .batang-isi { transition: width var(--t-sedang) var(--ease); }
  }

  /* ── Penanda kondisi fisik di laporan bulanan ─────────────────────────
   * Tiga keadaan, bukan dua bilangan. "fisik —" berarti gudang belum
   * menerima apa pun, jadi "perlu cek rusak: 0" di sebelahnya BELUM berarti
   * bersih -- pembedaan yang diminta catatan di atas tabel dan yang dulu
   * harus disimpulkan sendiri oleh pembaca dari dua angka.
   */
  .cek { font-family: var(--mono); font-size: var(--fs-xs); line-height: 1.3;
         font-variant-numeric: tabular-nums; white-space: nowrap; margin-top: 2px;
         cursor: help; }
  .cek.buta   { color: var(--faint); }
  .cek.bersih { color: var(--muted); }
  .cek.perlu  { color: var(--st-diajukan); }

  /* ── Rincian terbuka (dialog ringan tanpa JS) ─────────────────────────── */
  details.lipat > summary { cursor: pointer; list-style: none; color: var(--muted);
                            font-size: var(--fs-xs); padding: var(--s-1) 0; }
  details.lipat > summary::-webkit-details-marker { display: none; }
  details.lipat > summary::before { content: "▸ "; color: var(--faint); }
  details.lipat[open] > summary::before { content: "▾ "; }

  .kode { font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted); }
  .id-order { font-family: var(--mono); font-size: var(--fs-sm);
              text-decoration: underline;
              text-decoration-color: color-mix(in oklch, var(--accent) 45%, transparent);
              text-underline-offset: 3px; }
}

@layer utilitas {
  .lewati {
    position: absolute; left: var(--s-3); top: -60px; z-index: 100;
    background: var(--accent); color: var(--accent-fg);
    padding: var(--s-2) var(--s-3); border-radius: var(--r-md);
  }
  .lewati:focus { top: var(--s-2); }
  .sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  .muted { color: var(--muted); }
  .faint { color: var(--faint); }
  .kecil { font-size: var(--fs-xs); }
  .tebal { font-weight: 600; }
  .kanan { text-align: right; }
  .nowrap { white-space: nowrap; }
  /* Beda dari .nowrap: ini juga memotong dengan titik-titik alih-alih
   * meluber. Dipakai kolom teks bebas (catatan, item rusak) di tabel Cek
   * rusak -- teks panjang yang MEMBUNGKUS ke beberapa baris adalah
   * penyebab utama baris tabel jadi tinggi; teks utuhnya tetap ada lewat
   * atribut title (hover/fokus keyboard). */
  .satu-baris { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Ikon svg dari ui.ikon() TIDAK punya ukuran bawaan (cuma viewBox) --
   * setiap tempat lain yang memakainya (.btn, .kerja-peringatan, nav)
   * membatasi lewat selector "X svg" milik masing-masing. Baris di kolom
   * Catatan tabel Cek rusak tidak punya pembatas itu, jadi svg-nya
   * melebar ke ukuran ganti bawaan browser (~300x150px) -- satu baris
   * jadi setinggi kartu lagi, persis kebalikan dari perbaikan compact. */
  .tanda-peringatan { display: inline-flex; vertical-align: -2px; }
  .tanda-peringatan svg { width: 13px; height: 13px; }
  .baris { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
  .tumpuk { display: grid; gap: var(--s-2); }
  .mt-3 { margin-top: var(--s-3); } .mt-4 { margin-top: var(--s-4); }
  .mb-3 { margin-bottom: var(--s-3); } .mb-4 { margin-bottom: var(--s-4); }
  .mb-5 { margin-bottom: var(--s-5); }
}

@layer tata {
  /* Halaman tanpa sesi (login): tidak ada kerangka, isi dipusatkan. */
  .isi.tamu { min-height: 100vh; display: grid; place-items: center;
              max-width: none; padding: var(--s-5); }
}

@layer komponen {
  /* ── Blok kepala kerja (Cari order) ───────────────────────────────────
   * .antrean/.kerja/.kerja-bukti/.kerja-putusan (kartu per order di Cek
   * rusak) DIHAPUS 2026-09-16: layar itu kembali jadi tabel (lihat
   * cek_rusak.html) atas permintaan eksplisit pengguna -- PIC yang
   * memakainya sehari-hari lebih familiar dengan spreadsheet. .kerja-kepala
   * dan .kerja-uang TETAP, dipakai blok identitas+angka di Cari order
   * (cari.html); .kerja-peringatan TETAP, dipakai kolom Catatan di tabel
   * Cek rusak yang baru.
   */
  .kerja-kepala { display: flex; justify-content: space-between; gap: var(--s-4);
                  align-items: flex-start; flex-wrap: wrap; }
  .kerja-uang { text-align: right; flex: none; }
  .kerja-uang .nilai { font-size: var(--fs-lg); font-weight: 500; }

  .kerja-peringatan {
    display: flex; gap: var(--s-2); align-items: flex-start; margin: 0;
    padding: var(--s-2) var(--s-3); font-size: var(--fs-sm); line-height: 1.5;
    border-left: 2px solid var(--st-diajukan); background: var(--surface-2);
    border-radius: var(--r-sm); color: var(--muted);
  }
  .kerja-peringatan b { color: var(--st-diajukan); }
  .kerja-peringatan svg { width: 15px; height: 15px; flex: none; margin-top: 2px;
                          color: var(--st-diajukan); }

  @media (max-width: 599.98px) {
    .kerja-kepala { flex-direction: column; }
    .kerja-uang { text-align: left; }
  }
}

@layer komponen {
  /* ── Dua kolom setara, runtuh jadi satu di bawah 1100px ──────────────── */
  .dua-kolom { display: grid; gap: var(--s-3);
               grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
  .dua-kolom > .kartu { margin-top: 0; }

  /* ── Daftar fakta (pasangan label-nilai) ─────────────────────────────
   * <dl>, bukan <table>: ini pasangan, dan pembaca layar membacanya sebagai
   * pasangan alih-alih sebagai dua sel yang kebetulan bersebelahan.
   */
  .fakta { display: grid; grid-template-columns: 148px 1fr; gap: 7px var(--s-3);
           margin: 0; font-size: var(--fs-sm); align-items: baseline; }
  .fakta dt { color: var(--faint); font-size: var(--fs-xs); }
  .fakta dd { margin: 0; color: var(--fg); overflow-wrap: anywhere; }
  @media (max-width: 479.98px) {
    .fakta { grid-template-columns: 1fr; gap: 2px var(--s-3); }
    .fakta dt { margin-top: var(--s-2); }
  }
}

/* ============================================================================
 * LAPISAN INTERAKSI (2026-09-17)
 *
 * Ditambahkan SEBAGAI LAPISAN, bukan sebagai redesign: tidak satu pun token,
 * warna, ukuran, jarak, atau hierarki di atas berkas ini berubah. Yang
 * ditambahkan hanya keadaan (hover/tekan/memuat/sukses/galat) dan gerak yang
 * MENJELASKAN perubahan.
 *
 * Tiga aturan yang mengikat seluruh bagian ini:
 *
 * 1. GERAK HANYA UNTUK MENJELASKAN. Kalau sebuah animasi tidak menjawab
 *    "apa yang berubah / dari mana / ke mana / harus lihat ke mana", ia
 *    dekorasi dan tidak boleh ada. Itu sebabnya .stat TIDAK diberi keadaan
 *    hover: kartu itu bukan tautan dan bukan tombol. Memberinya reaksi hover
 *    adalah janji palsu -- pembaca mengira ada yang bisa diklik, lalu tidak
 *    terjadi apa-apa.
 * 2. WARNA JENUH TETAP HANYA BERARTI STATUS. Keadaan sukses/galat memakai
 *    --st-disetujui/--st-ditolak karena keduanya MEMANG status hasil
 *    tindakan, bukan karena hijau dan merah enak dilihat.
 * 3. SELURUH GERAK DI DALAM prefers-reduced-motion: no-preference. Pengguna
 *    reduced-motion mendapat halaman yang LENGKAP dan semua keadaannya tetap
 *    terbaca -- keadaan disampaikan warna, bentuk, dan teks lebih dulu;
 *    gerak cuma lapisan paling atas yang boleh dicabut tanpa kehilangan apa
 *    pun.
 *
 * Seluruh animasi memakai transform/opacity (dikerjakan compositor), tidak
 * pernah width/height/top/left, kecuali satu: tinggi <details> yang memang
 * tidak punya padanan transform. Tidak ada blur baru, tidak ada pustaka.
 * ========================================================================= */

@layer dasar {
  :root {
    /* Tingkat halaman. Dua yang lain (--t-cepat 120, --t-sedang 200) sudah
     * ada sejak awal; yang ini disebut DESIGN.md tapi belum pernah punya
     * token karena belum ada yang memakainya. */
    --t-lambat: 320ms;
  }

  /* Tanpa ini, tiap kali fragmen ditukar seluruh halaman ikut meluncur
   * karena browser menganimasikan akar dokumen secara bawaan. Yang boleh
   * bergerak cuma wilayah yang benar-benar berganti isi. */
  @view-transition { navigation: none; }
}

@layer komponen {
  /* -- Tekan --------------------------------------------------------------
   * Turun setengah piksel dan mengecil 1,5%. Cukup untuk terasa fisik,
   * terlalu kecil untuk terbaca sebagai animasi. Dipasang pada :active,
   * BUKAN lewat JS, supaya reaksinya terjadi di frame yang sama dengan
   * tekanan jari -- tidak ada round-trip, tidak ada listener.
   */
  @media (prefers-reduced-motion: no-preference) {
    .btn { transition: background-color var(--t-cepat) var(--ease),
                       border-color var(--t-cepat) var(--ease),
                       color var(--t-cepat) var(--ease),
                       transform 90ms var(--ease); }
    .btn:active:not([aria-disabled="true"]):not(:disabled) {
      transform: translateY(0.5px) scale(0.985); }
    .nav a { transition: background-color var(--t-cepat) var(--ease),
                         color var(--t-cepat) var(--ease); }
    .input, select.input, textarea.input {
      transition: border-color var(--t-cepat) var(--ease); }
  }

  /* -- Keadaan tombol: memuat / sukses / galat -----------------------------
   * Satu atribut, empat keadaan. data-keadaan dipasang JS; tanpa JS atribut
   * ini tidak pernah muncul dan tombol tetap tombol biasa yang mengirim
   * form-nya seperti sedia kala.
   *
   * Label tombol TIDAK diganti teks "Menyimpan...": mengganti teks membuat
   * lebar tombol melompat dan baris di sebelahnya ikut bergeser. Yang
   * berubah cuma opacity label dan satu cincin berputar di atasnya, jadi
   * geometri baris tidak bergerak sama sekali.
   */
  .btn[data-keadaan] { position: relative; }
  .btn[data-keadaan="memuat"] { pointer-events: none; }
  .btn[data-keadaan="memuat"] > * { opacity: 0.25; }
  .btn[data-keadaan="memuat"]::after {
    content: ""; position: absolute; inset: 50% auto auto 50%;
    width: 13px; height: 13px; margin: -6.5px 0 0 -6.5px;
    border: 1.5px solid currentColor; border-right-color: transparent;
    border-radius: 50%; opacity: 0.85;
  }
  /* Putaran terus-menerus DIBENARKAN di sini dan hanya di sini: selama ia
   * berputar, sistem memang sedang bekerja. Ia berhenti begitu jawabannya
   * datang, jadi ia melaporkan keadaan hidup, bukan menghias. */
  @media (prefers-reduced-motion: no-preference) {
    .btn[data-keadaan="memuat"]::after { animation: putar 620ms linear infinite; }
    @keyframes putar { to { transform: rotate(1turn); } }
  }

  .btn[data-keadaan="sukses"] { border-color: var(--st-disetujui);
                                color: var(--st-disetujui); }
  .btn[data-keadaan="galat"]  { border-color: var(--st-ditolak);
                                color: var(--st-ditolak); }

  /* -- Baris tabel: umpan balik sentuh + masuk/keluar ----------------------
   * Baris di Cek rusak adalah unit kerja, bukan sekadar baris data: satu
   * baris = satu order yang sedang diputuskan. Ia butuh tiga hal yang baris
   * tabel biasa tidak butuh -- tanda sedang diproses, tanda berhasil, dan
   * cara pergi yang terlihat kalau filter membuatnya tidak lagi termasuk.
   */
  @media (prefers-reduced-motion: no-preference) {
    .tabel tbody tr td { transition: background-color var(--t-cepat) var(--ease); }
  }
  /* Baris yang sedang dikirim: diredupkan dan dikunci, tapi TIDAK
   * disembunyikan -- orang harus tetap bisa membaca order mana yang sedang
   * diproses. */
  .tabel tbody tr[aria-busy="true"] td { opacity: 0.55; }
  .tabel tbody tr[aria-busy="true"] :is(input, button, select) { pointer-events: none; }

  /* Garis kiri, bukan bidang berwarna: baris tabel yang seluruhnya dicat
   * hijau membuat mata mengira kolom-kolomnya ikut berarti sesuatu. */
  .tabel tbody tr[data-keadaan="sukses"] td:first-child {
    box-shadow: inset 2px 0 0 var(--st-disetujui); }
  .tabel tbody tr[data-keadaan="galat"] td:first-child {
    box-shadow: inset 2px 0 0 var(--st-ditolak); }

  @media (prefers-reduced-motion: no-preference) {
    tr.masuk > td { animation: baris-masuk var(--t-sedang) var(--ease) backwards; }
    @keyframes baris-masuk {
      from { opacity: 0; transform: translateY(-4px); }
      to   { opacity: 1; transform: none; }
    }
    /* Keluar memakai opacity saja, tanpa menggeser: baris yang meluncur
     * keluar menarik mata ke arah yang salah -- yang penting adalah baris
     * TETANGGANYA yang naik mengisi tempatnya. */
    tr.pergi > td { animation: baris-pergi 160ms var(--ease) forwards; }
    @keyframes baris-pergi { to { opacity: 0; } }
  }

  /* -- Wilayah yang ditukar lewat fetch ------------------------------------
   * Selagi menunggu, wilayahnya tetap TERBACA dan tetap di tempatnya --
   * hanya diredupkan sedikit dan kursornya berubah. Tidak ada kerangka
   * abu-abu yang menggantikan isi: mengganti tabel yang sudah terbaca
   * dengan kotak-kotak kosong justru membuang informasi yang sudah ada di
   * layar, dan membuat pergantian terasa lebih lama daripada nyatanya.
   */
  [data-wilayah], .modal-muat { position: relative; }
  [data-wilayah][aria-busy="true"], .modal-muat[aria-busy="true"] { cursor: progress; }
  [data-wilayah][aria-busy="true"] > *, .modal-muat[aria-busy="true"] > * { pointer-events: none; }
  @media (prefers-reduced-motion: no-preference) {
    [data-wilayah], .modal-muat { transition: opacity var(--t-cepat) var(--ease); }
    [data-wilayah][aria-busy="true"], .modal-muat[aria-busy="true"] { opacity: 0.62; }
  }

  /* Garis tipis berjalan di tepi atas wilayah yang sedang dimuat. Satu-
   * satunya gerak berulang selain cincin tombol, dan alasannya sama: ia
   * melaporkan bahwa sesuatu sedang berjalan, dan ia berhenti sendiri. */
  [data-wilayah][aria-busy="true"]::before, .modal-muat[aria-busy="true"]::before {
    content: ""; position: absolute; inset: -1px 0 auto; height: 2px;
    border-radius: var(--r-sm); background: var(--accent); opacity: 0.9;
    transform-origin: left; z-index: 5; transform: scaleX(0.35);
  }
  @media (prefers-reduced-motion: no-preference) {
    [data-wilayah][aria-busy="true"]::before, .modal-muat[aria-busy="true"]::before {
      animation: laju 1100ms var(--ease) infinite; }
    @keyframes laju {
      0%   { transform: scaleX(0);    opacity: 0.9; }
      55%  { transform: scaleX(0.85); opacity: 0.9; }
      100% { transform: scaleX(1);    opacity: 0; }
    }
  }

  /* -- Pergantian isi wilayah (View Transitions) ---------------------------
   * Dipakai kalau browser mendukungnya; kalau tidak, penukaran tetap
   * terjadi, cuma tanpa silang-pudar. Tidak ada polyfill -- satu kemampuan
   * yang hilang dengan anggun tidak perlu 30 KB pengganti.
   */
  @media (prefers-reduced-motion: no-preference) {
    ::view-transition-old(wilayah) {
      animation: pudar-keluar 110ms var(--ease) forwards; }
    ::view-transition-new(wilayah) {
      animation: pudar-masuk var(--t-sedang) var(--ease); }
    @keyframes pudar-keluar { to { opacity: 0; } }
    @keyframes pudar-masuk {
      from { opacity: 0; transform: translateY(3px); }
      to   { opacity: 1; transform: none; }
    }
  }

  /* -- Angka yang berubah --------------------------------------------------
   * Hanya dipasang kalau nilainya BENAR-BENAR berbeda dari sebelumnya --
   * JS yang memutuskan, dan ia membandingkan teks lama dengan teks baru.
   * Animasi hitung-naik saat halaman pertama dimuat sengaja TIDAK ada:
   * tidak ada yang berubah saat halaman baru dibuka, jadi tidak ada yang
   * perlu dijelaskan. Angka yang menghitung sendiri di muka pembaca adalah
   * dekorasi, dan dekorasi pada angka uang adalah yang paling mahal.
   */
  @media (prefers-reduced-motion: no-preference) {
    .stat-nilai[data-berubah] { animation: nilai-berganti 520ms var(--ease); }
    @keyframes nilai-berganti {
      0%   { opacity: 0; transform: translateY(-3px); }
      40%  { opacity: 1; transform: none; }
    }
  }
  /* Tanda "yang ini barusan berubah" yang tetap ada untuk reduced-motion,
   * karena ia keadaan, bukan gerak. --accent (bukan warna status): yang
   * dinyatakan adalah "perhatikan ini", bukan sebuah klaim_status. */
  .stat[data-berubah] { border-left: 2px solid var(--accent); }

  /* -- Kabar (umpan balik hasil tindakan) ----------------------------------
   * Bukan "toast" yang melayang di sudut dan hilang sebelum dibaca. Ia
   * duduk di bawah kanan, memakai kosakata .catatan yang sudah ada, dan
   * GALAT TIDAK PERNAH hilang sendiri -- kegagalan yang menghapus dirinya
   * setelah empat detik adalah kegagalan yang tidak dilaporkan.
   */
  .kabar-wadah {
    position: fixed; right: var(--s-4); bottom: var(--s-4); z-index: 60;
    display: grid; gap: var(--s-2); width: min(360px, calc(100vw - 32px));
    pointer-events: none;
  }
  .kabar {
    pointer-events: auto; display: flex; gap: var(--s-3); align-items: flex-start;
    padding: var(--s-3) var(--s-4); font-size: var(--fs-sm); line-height: 1.5;
    background: var(--surface); color: var(--muted);
    border: 1px solid var(--line); border-left: 2px solid var(--line-kuat);
    border-radius: var(--r-md); box-shadow: var(--naik);
  }
  .kabar b { color: var(--fg); font-weight: 600; }
  .kabar.sukses { border-left-color: var(--st-disetujui); }
  .kabar.galat  { border-left-color: var(--st-ditolak); }
  .kabar > svg { width: 15px; height: 15px; flex: none; margin-top: 2px;
                 color: var(--faint); }
  .kabar .tutup { margin-left: auto; flex: none; }
  @media (prefers-reduced-motion: no-preference) {
    .kabar { animation: kabar-masuk var(--t-sedang) var(--ease); }
    @keyframes kabar-masuk {
      from { opacity: 0; transform: translateY(6px) scale(0.99); }
      to   { opacity: 1; transform: none; }
    }
    .kabar.pergi { animation: kabar-pergi 160ms var(--ease) forwards; }
    @keyframes kabar-pergi { to { opacity: 0; transform: translateY(4px); } }
  }

  /* -- Lipat (details) yang membuka dengan tinggi, bukan menyentak ---------
   * Satu-satunya tempat di berkas ini yang menganimasikan tinggi, dan itu
   * disengaja: tidak ada padanan transform untuk "isi yang tumbuh dari nol".
   * Dipagari @supports supaya browser yang belum punya interpolate-size
   * tetap membuka seketika seperti sedia kala -- bukaan yang seketika jauh
   * lebih baik daripada bukaan yang berhenti setengah jalan.
   *
   * Dikecualikan untuk <details> yang isinya POSITION:ABSOLUTE (popover
   * "Tandai banyak sekaligus" di Cek rusak): menganimasikan tinggi kotak
   * yang tidak memakan tinggi sama sekali tidak menghasilkan apa pun selain
   * satu frame kosong.
   */
  @supports (interpolate-size: allow-keywords) {
    @media (prefers-reduced-motion: no-preference) {
      details.lipat:not(.popover) { interpolate-size: allow-keywords; }
      details.lipat:not(.popover)::details-content {
        height: 0; overflow: clip; opacity: 0;
        transition: height var(--t-sedang) var(--ease),
                    content-visibility var(--t-sedang) allow-discrete,
                    opacity var(--t-cepat) var(--ease);
      }
      details.lipat:not(.popover)[open]::details-content { height: auto; opacity: 1; }
    }
  }
  details.lipat > summary::before { display: inline-block; }
  @media (prefers-reduced-motion: no-preference) {
    details.lipat > summary::before {
      transition: transform var(--t-cepat) var(--ease); }
  }

  /* -- Kolom yang bisa diurut ----------------------------------------------
   * Panah hanya muncul pada kolom yang MEMANG bisa diurut, dan hanya
   * berwarna penuh pada kolom yang sedang mengurut. Kolom lain menahan
   * panahnya sampai disentuh mata (hover/fokus) -- 16 panah abu-abu di
   * kepala tabel adalah kebisingan, bukan petunjuk.
   */
  .tabel th.urut > a { color: inherit; display: inline-flex; align-items: center;
                       gap: 4px; text-decoration: none; }
  .tabel th.urut > a::after { content: "\2195"; opacity: 0; font-size: 0.75em; }
  .tabel th.urut > a:hover::after,
  .tabel th.urut > a:focus-visible::after { opacity: 0.55; }
  .tabel th.urut[aria-sort="ascending"]  > a::after { content: "\2191"; opacity: 1; }
  .tabel th.urut[aria-sort="descending"] > a::after { content: "\2193"; opacity: 1; }
  .tabel th.urut[aria-sort] > a { color: var(--fg); }
  @media (prefers-reduced-motion: no-preference) {
    .tabel th.urut > a::after { transition: opacity var(--t-cepat) var(--ease); }
  }

  /* -- Bar atas: umpan balik terikat gulir ---------------------------------
   * Bar sudah sticky sejak awal; yang ditambahkan cuma pemisah yang muncul
   * SAAT ada isi di belakangnya. Digerakkan animation-timeline: scroll(),
   * bukan listener scroll di JS -- ia berjalan di compositor dan tidak
   * memanggil satu baris JavaScript pun per frame.
   *
   * Pseudo-element ber-opacity, BUKAN box-shadow pada .bar itu sendiri:
   * opacity dikerjakan compositor, box-shadow memaksa cat ulang tiap frame
   * pada elemen yang menempel di atas seluruh halaman.
   */
  .bar::after {
    content: ""; position: absolute; inset: auto 0 -9px; height: 8px;
    pointer-events: none; opacity: 0;
    background: linear-gradient(to bottom, oklch(0 0 0 / 0.16), transparent);
  }
  :root[data-tema="terang"] .bar::after {
    background: linear-gradient(to bottom, oklch(0 0 0 / 0.07), transparent); }
  @supports (animation-timeline: scroll()) {
    @media (prefers-reduced-motion: no-preference) {
      .bar::after {
        animation: bar-pemisah linear both;
        animation-timeline: scroll(root block);
        animation-range: 0 56px;
      }
      @keyframes bar-pemisah { to { opacity: 1; } }
    }
  }

  /* -- Papan pintasan keyboard --------------------------------------------
   * Dibuka dengan "?" dan ditutup dengan Esc. <dialog> asli, bukan div yang
   * berpura-pura jadi dialog: fokus terperangkap, Esc bekerja, dan pembaca
   * layar tahu ini lapisan modal -- ketiganya gratis dari elemen itu sendiri.
   */
  dialog.papan { padding: 0; border: 1px solid var(--line); border-radius: var(--r-lg);
                 background: var(--surface); color: var(--fg);
                 box-shadow: var(--naik); width: min(420px, calc(100vw - 32px)); }
  dialog.papan::backdrop { background: oklch(0 0 0 / 0.5); }
  dialog.papan .kartu-kepala { border-bottom: 1px solid var(--line); }
  dialog.papan .kartu-badan { padding: var(--s-4); }
  dialog.papan dl { display: grid; grid-template-columns: 84px 1fr;
                    gap: 7px var(--s-3); margin: 0; font-size: var(--fs-sm);
                    align-items: baseline; }
  dialog.papan dt { margin: 0; }
  dialog.papan dd { margin: 0; color: var(--muted); }
  kbd { font-family: var(--mono); font-size: var(--fs-xs); padding: 1px 5px;
        border: 1px solid var(--line-kuat); border-bottom-width: 2px;
        border-radius: var(--r-sm); background: var(--surface-2); color: var(--fg); }
  /* Di sini, bukan di @layer tata bersama aturan .bar lain: kbd di atas ada
   * di lapisan komponen dan mengalahkan tata apa pun spesifisitasnya. Tanpa
   * ini tombol "?" jadi kotak putih di bar navy pada tema terang. */
  .bar kbd { background: var(--bar-hover); color: var(--bar-fg);
             border-color: var(--bar-line); }
  /* Di dalam .btn (inline-flex) kbd jadi item flex, jadi padding dan
   * tepinya ikut menambah tinggi: tombol "?" dulu 30px di sebelah tombol
   * kecil lain yang 28px. */
  .btn kbd { line-height: 1; padding-block: 2px; }
  /* Alasan yang sama untuk bar status: .btn di lapisan ini, jadi pewarnaan
   * ulangnya harus di sini juga. Tepi dicampur dari --bar-muted, bukan
   * --bar-line: garis itu cuma ~1,7:1 di atas bidang, di bawah 3:1. */
  .bar-status .btn { background: var(--bar-hover); color: var(--bar-fg);
                     border-color: color-mix(in oklch, var(--bar-muted) 55%, var(--bar-bg)); }
  .bar-status .btn:hover { background: color-mix(in oklch, var(--bar-hover) 85%, var(--bar-fg)); }
  /* Ikon di bar gelap ikut warna teks; --muted di atas navy terlalu redup. */
  .bar-status .btn svg { color: inherit; }
  .bar-status .paginasi .posisi { color: var(--bar-muted); }
  /* Masuk DAN keluar (2026-10-15): @starting-style memberi keadaan awal,
   * display/overlay allow-discrete menahan dialog di lapisan atas sampai
   * pudarnya selesai. Dulu hanya animasi masuk; menutup = hilang seketika. */
  @media (prefers-reduced-motion: no-preference) {
    dialog.papan, dialog.papan::backdrop {
      transition: opacity var(--t-cepat) var(--ease), transform var(--t-cepat) var(--ease),
                  overlay var(--t-cepat) allow-discrete, display var(--t-cepat) allow-discrete;
      opacity: 0;
    }
    dialog.papan { transform: translateY(6px) scale(0.985); }
    dialog.papan[open], dialog.papan[open]::backdrop { opacity: 1; transform: none;
                                                       transition-duration: var(--t-sedang); }
    @starting-style {
      dialog.papan[open], dialog.papan[open]::backdrop { opacity: 0; }
      dialog.papan[open] { transform: translateY(6px) scale(0.985); }
    }
  }
}

@layer utilitas {
  /* Dipasang JS pada <html> selama satu permintaan fetch berjalan. Satu
   * kait untuk apa pun yang perlu tahu "aplikasi sedang menunggu jaringan"
   * tanpa tiap komponen memasang listener sendiri. */
  :root[data-sibuk] { cursor: progress; }

  /* Gulir mulus hanya kalau pengguna tidak menolak gerak. Dipasang di
   * utilitas (bukan dasar) supaya ia menang atas scroll-behavior apa pun
   * yang mungkin ditulis komponen. */
  @media (prefers-reduced-motion: no-preference) {
    :root { scroll-behavior: smooth; }
  }

  /* Geser trackpad yang sampai ujung tabel berhenti di tabel, tidak
   * diteruskan jadi gestur Kembali browser atau geser halaman.
   *
   * TANPA scroll-snap (dicabut 2026-09-24). Sejak a1d0cfe .gulir memakai
   * `scroll-snap-type: x proximity` dengan titik henti hanya pada sel
   * PERTAMA tiap baris -- artinya satu-satunya titik henti adalah tepi kiri,
   * bukan batas tiap kolom seperti maksudnya. Browser lalu menarik setiap
   * geseran yang "masih dekat" kembali ke 0: diukur 2026-09-24 pada tabel
   * 3200px di layar 1280px, geser ke 40/150/400px kembali ke 0, baru 900px
   * yang bertahan. Titik henti per kolom juga tidak dipasang sebagai
   * gantinya: dengan kolom Detail lengket di kanan, tabel selebar ini tetap
   * terasa tersangkut. */
  .gulir { overscroll-behavior-x: contain; }

  /* .muted ada di lapisan ini; di bar status yang gelap ia harus ikut. */
  .bar-status .muted { color: var(--bar-muted); }
}

@layer komponen {
  /* ── Layar Manajemen ───────────────────────────────────────────────────
   * Satu bentuk baru saja: corong banding. Sisanya memakai primitif yang
   * sudah ada (.stat, .kartu, .tabel, .batang) apa adanya -- layar baru
   * bukan alasan menumbuhkan sistem kedua di samping yang pertama.
   *
   * Ini SATU-SATUNYA tempat di aplikasi ini yang menampilkan kelima warna
   * status berdampingan dalam satu bidang, dan justru karena itu ia tunduk
   * pada aturan yang sama: tiap warna di sini menjawab "berarti apa?"
   * dengan satu nilai klaim_status, tidak pernah dengan "supaya batangnya
   * tidak membosankan".
   */
  .corong { display: flex; height: 40px; border-radius: var(--r-sm);
            overflow: hidden; background: var(--surface-2); }
  /* min-width, bukan sekadar persentase: tahap yang isinya satu order dari
   * seribu tetap harus punya lebar yang bisa dilihat. Tanpa ini "DITOLAK
   * 0,1%" lenyap sama sekali, dan tahap yang lenyap terbaca sebagai tahap
   * yang tidak ada -- padahal DITOLAK adalah kerugian nyata yang justru
   * paling perlu terlihat. */
  .corong-bagian { min-width: 3px; }
  .corong-bagian:first-child { border-radius: var(--r-sm) 0 0 var(--r-sm); }
  .corong-bagian:last-child { border-radius: 0 var(--r-sm) var(--r-sm) 0; }

  .corong-legenda { display: grid; gap: var(--s-3); margin-top: var(--s-3);
                    grid-template-columns: repeat(auto-fit, minmax(146px, 1fr)); }
  .corong-kunci { display: flex; align-items: center; gap: 6px;
                  font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.03em; }
  .corong-titik { width: 8px; height: 8px; border-radius: var(--r-sm); flex: none; }
  .corong-nilai { font-family: var(--mono); font-variant-numeric: tabular-nums;
                  font-size: var(--fs-sm); margin-top: 3px; }
  /* Baris kedua yang jelas lebih rendah, pola yang sama dengan .stat-syarat:
   * cacahan menempel pada angka uangnya, tidak pernah menyaingi. */
  .corong-cacah { font-family: var(--mono); font-variant-numeric: tabular-nums;
                  font-size: var(--fs-xs); color: var(--faint); }

  /* Warna status sebagai bidang isi. Dipisah dari .lencana (yang memberi
   * warna pada TEKS dan garis) karena di sini yang diwarnai adalah luasan,
   * dan keduanya tidak bisa memakai aturan yang sama. */
  .isi-belum     { background: var(--st-belum); }
  .isi-diajukan  { background: var(--st-diajukan); }
  .isi-disetujui { background: var(--st-disetujui); }
  .isi-ditolak   { background: var(--st-ditolak); }
  .isi-diterima  { background: var(--st-diterima); }
  /* Gugatan: arsiran dari hue pasangannya, padanan garis putus-putus di
   * .lencana. Segmen corong yang sewarna penuh dengan DISETUJUI akan dibaca
   * sebagai uang yang kita menangkan lewat banding. */
  .isi-digugat, .isi-gugatan_menang, .isi-gugatan_kalah {
    background: repeating-linear-gradient(135deg, var(--isi-g) 0 3px,
                                          transparent 3px 6px);
    box-shadow: inset 0 0 0 1px var(--isi-g);
  }
  .isi-digugat        { --isi-g: var(--st-diajukan); }
  .isi-gugatan_menang { --isi-g: var(--st-disetujui); }
  .isi-gugatan_kalah  { --isi-g: var(--st-ditolak); }
  /* Nilai klaim_status yang tidak dikenal. Sengaja netral DAN sengaja tetap
   * tampil: ia berarti ada jalur tulis yang tidak lewat tulis.ubah_klaim(),
   * dan itu justru yang paling perlu terlihat. */
  .isi-asing     { background: var(--line-kuat); }

  /* Batang umur banding. Memakai .batang-* yang sudah ada; yang ditambahkan
   * cuma dua keadaan yang MEMANG status tenggat, bukan kuantitas. */
  .batang-isi.lewat    { background: var(--sla-lewat); }
  .batang-isi.mendesak { background: var(--sla-mendesak); }
  .batang-isi.tak-tentu { background: var(--line-kuat); }

  /* Beban PIC: satu jalur, tiga segmen. Bukan tiga batang terpisah -- yang
   * ditanyakan dispatcher adalah "berapa banyak dari antrean orang ini yang
   * mendesak", dan itu pertanyaan tentang PROPORSI di dalam satu beban. */
  .beban-jalur { display: flex; height: 18px; border-radius: var(--r-sm);
                 overflow: hidden; background: var(--surface-2); }
  .beban-bagian { min-width: 0; }
  .beban-bagian.aman     { background: var(--muted); }
  .beban-bagian.mendesak { background: var(--sla-mendesak); }
  .beban-bagian.lewat    { background: var(--sla-lewat); }
  .beban-bagian.peluang  { background: var(--grafik-2); }
  .beban-bagian.hangus   { background: var(--kpi-merah); }
}

@layer komponen {
  /* Kartu angka yang menandai KERUGIAN, bukan urgensi. Amber di aplikasi
   * ini berarti "segera kerjakan"; untuk order yang tenggatnya sudah lewat
   * itu menyuruh orang mengejar yang tidak bisa dikejar. Token yang sama
   * dengan lencana DITOLAK, karena artinya memang sama. */
  .stat.perhatian.buruk { border-left-color: var(--st-ditolak); }
}

/* ══ Papan Kerja ═══════════════════════════════════════════════════════════
 * Blok @layer komponen KEDUA, bukan sisipan ke yang pertama. Lapisan CSS
 * memang bergabung, jadi urutan kaskadenya identik -- dan komponen yang lahir
 * bersama satu layar lebih mudah dibaca (dan dihapus, kalau layarnya pensiun)
 * kalau ia duduk dalam satu blok yang bisa dilihat sekaligus.
 *
 * Spesifikasinya di retvalweb/DESIGN.md. Aturan yang mengikat seluruh blok
 * ini cuma satu: warna jenuh HANYA berarti status, dan biru HANYA berarti
 * "bisa ditindak".
 */
@layer komponen {

  /* ── Chip potongan antrean ─────────────────────────────────────────────
   * Tautan, bukan tombol: tiap potongan adalah alamat yang bisa ditandai.
   * Yang aktif dibedakan permukaan + garis, bukan bidang warna penuh.
   */
  .chip-baris { display: flex; flex-wrap: wrap; gap: var(--s-2);
                margin-bottom: var(--s-4); }
  .chip {
    display: inline-flex; align-items: center; gap: var(--s-2);
    padding: 5px var(--s-3); border-radius: 999px;
    border: 1px solid var(--line); color: var(--muted);
    font-size: var(--fs-sm); font-weight: 500; white-space: nowrap;
  }
  .chip:hover { border-color: var(--line-kuat); color: var(--fg);
                text-decoration: none; }
  .chip.aktif { border-color: var(--accent); color: var(--fg);
                background: var(--accent-lemah); }
  .chip b { font-family: var(--mono); font-size: var(--fs-xs); font-weight: 500;
            color: var(--muted); }
  .chip.aktif b { color: var(--fg); }

  /* ── Kalimat cakupan ───────────────────────────────────────────────────
   * Kalimat, bukan deretan chip untuk ditafsirkan sendiri. Sengaja tidak
   * diberi ikon atau bingkai: ia harus terbaca sebagai keterangan, bukan
   * sebagai peringatan.
   */
  .cakupan { font-size: var(--fs-sm); color: var(--muted);
             margin: 0 0 var(--s-3); max-width: 90ch; }
  .cakupan b { color: var(--fg); font-weight: 500; }

  /* ── Penanda jalur ─────────────────────────────────────────────────────
   * Undel yang 94%, jadi Undel yang jadi dasar: teks polos, tenang. Retur
   * pembeli yang 6% yang ditandai. Keduanya NETRAL -- jalur bukan status,
   * dan warna jenuh di sini akan menabrak lencana status di baris yang sama.
   */
  .jalur { font-size: var(--fs-xs); white-space: nowrap; cursor: help; }
  .jalur.undel { color: var(--muted); }
  .jalur.retur {
    color: var(--fg); padding: 1px var(--s-2); border-radius: var(--r-sm);
    border: 1px solid var(--line-kuat); background: var(--surface-2);
  }

  /* ── Umur / tenggat — tanda tangan layar ini ───────────────────────────
   *
   *   Batang Undel sengaja TIDAK punya garis akhir. Batang retur pembeli
   *   punya.
   *
   * Undel memudar ke kanan lewat mask, bukan gradien warna: gradien akan
   * memperkenalkan warna kedua ke dalam batang yang artinya sudah dipakai
   * status. Mask hanya mengurangi opasitas bentuk yang sama, jadi warnanya
   * tetap satu dan artinya tetap satu.
   *
   * Yang punya ambang mendapat ::after -- satu garis 2px di ujung kanan
   * lajur, bukan di ujung batang: ambangnya tetap di tempat yang sama
   * sementara batangnya tumbuh ke arahnya. Garis yang ikut bergerak bersama
   * batang tidak menyatakan apa pun.
   */
  /* max-width ikut min-width: batang ini dibaca dengan MEMBANDINGKAN panjang
   * satu baris dengan baris lain, dan panjang yang ikut melebar mengikuti
   * selnya membuat perbandingan itu bohong. Di modal detail selnya jauh lebih
   * lebar daripada di tabel; tanpa batas atas, batang yang sama tampil dua
   * panjang berbeda untuk order yang sama. */
  .umur { display: grid; gap: 3px; width: 104px; cursor: help; }
  .umur-batang {
    position: relative; display: block; height: 3px; border-radius: 999px;
    background: var(--surface-2);
  }
  .umur-batang::before {
    content: ""; position: absolute; inset: 0 auto 0 0;
    width: var(--isi, 0%); border-radius: 999px; background: var(--muted);
  }
  .umur-teks { font-family: var(--mono); font-size: var(--fs-xs);
               color: var(--muted); white-space: nowrap; }

  /* Undel: tanpa garis akhir, dan ujung batangnya memudar. */
  .umur.terbuka .umur-batang::before {
    mask-image: linear-gradient(to right, black 55%, transparent);
  }

  /* Retur pembeli: garis ambang di ujung lajur. */
  .umur.aman .umur-batang::after,
  .umur.mendesak .umur-batang::after,
  .umur.lewat .umur-batang::after {
    content: ""; position: absolute; top: -2px; bottom: -2px; right: 0;
    width: 2px; border-radius: 1px; background: var(--line-kuat);
  }
  .umur.mendesak .umur-batang::before { background: var(--sla-mendesak); }
  .umur.mendesak .umur-teks { color: var(--sla-mendesak); }
  .umur.lewat .umur-batang::before { background: var(--sla-lewat); }
  .umur.lewat .umur-teks { color: var(--sla-lewat); }

  /* Sudah diajukan: tidak ada batang sama sekali. Tenggatnya memang sudah
   * terpakai, dan lajur kosong menyatakan itu lebih jelas daripada batang
   * abu-abu yang masih mengundang dibaca sebagai kemajuan. */
  .umur.selesai .umur-teks { color: var(--faint); }

  /* Tenggat marketplace di kolom Tenggat (baca._tenggat_mkp). Nama kanalnya
   * ("Shopee" / "TikTok") ditebalkan: itu yang membedakannya dari tanggal ambang internal di baris
   * sebelahnya, dan ia satu-satunya tenggat di kolom ini yang datang dari
   * marketplace. "lewat" --muted, BUKAN --sla-lewat: akibatnya kalau lewat
   * belum diketahui (PERTANYAAN_OPS #5), dan merah akan mengklaim sebaliknya.
   * --muted, bukan --faint, karena ini teks 12px yang tetap harus terbaca. */
  .tsh-sumber { font-weight: 600; }
  .wms-belum { font-weight: 600; }
  /* Penanda hasil pencarian di luar antrean: teks redup, bukan lencana --
     warna di baris ini milik status banding (lihat .jalur). */
  .luar-antrean { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; cursor: help; }
  .tsh.mendesak { color: var(--sla-mendesak); }
  .tsh.lewat { color: var(--muted); }
  .tsh-catatan { display: block; margin-top: var(--s-1); }
  /* Tiket DIAJUKAN yang lewat irama cek 24 jam (baca._jatuh_cek). Warna
   * mendesak, bukan lewat: tiketnya masih bisa diselamatkan dengan dicek
   * sekarang -- yang sudah hangus tidak terlihat dari data kita. */
  .cek-lewat { font-weight: 600; color: var(--sla-mendesak); margin-top: var(--s-1); white-space: nowrap; }
  /* Jejak tindakan dari modal Detail (ui.js catatDariModal), bertahan sampai
   * halaman dimuat ulang: PIC melihat order mana yang barusan ia sentuh.
   * Teks + garis kiri, bukan baris bercat -- sama dengan keadaan sukses. */
  .tercatat { font-weight: 600; color: var(--st-disetujui); margin-top: var(--s-1); white-space: nowrap; }
  .papan-tabel tbody tr[data-tercatat] td:first-child { box-shadow: inset 2px 0 0 var(--st-disetujui); }
  /* Baris yang sudah keluar dari daftar tapi dibiarkan di tempatnya: semua
   * isinya diredupkan (sudah lama), kecuali penanda dan alasannya. */
  .papan-tabel tbody tr[data-basi] > td:not(.sel-status),
  .papan-tabel tbody tr[data-basi] .sel-status > :not(.tercatat) { opacity: 0.55; }
  /* Kalimat hasil di dalam modal: kabar biasa tertutup lapisan <dialog>. */
  .detail-kabar { margin: 0 0 var(--s-3); padding: var(--s-2) var(--s-3);
                  border-left: 2px solid var(--st-disetujui); background: var(--surface-2);
                  font-size: var(--fs-base); }
  .detail-kabar.galat { border-left-color: var(--st-ditolak); }

  /* ── Tabel antrean ─────────────────────────────────────────────────────
   * Tinggi baris ditekan: antrean dibaca dengan memindai ke bawah, dan tiap
   * piksel tinggi baris adalah satu baris lebih sedikit yang terlihat
   * sekaligus.
   */
  .papan-tabel .sel-pilih { width: 32px; padding-right: 0; }
  .papan-tabel .sel-umur { width: 1%; }
  /* width 1% + nowrap = selebar isinya dan tidak lebih. Kolom ini menambah
   * lebar tabel yang MEMANG sudah meluap di layar 1.280px (lihat komentar
   * kolom status retur); ia ada atas keputusan Juan 2026-09-22, dan
   * ongkosnya ditekan sekecil mungkin, bukan dipura-purakan tidak ada. */
  .papan-tabel .sel-dicatat { width: 1%; font-variant-numeric: tabular-nums; }
  /* nowrap: penanda foto duduk SEBARIS dengan tombol Detail. Tanpa ini
   * ia turun ke baris kedua dan setiap baris berfoto jadi dua kali
   * lebih tinggi -- 38,4% antrean berfoto, di layar yang seluruh tata
   * letaknya dipilih supaya rapat (lihat tbody.rapat). */
  .papan-tabel .sel-detail { width: 1%; text-align: right;
                             white-space: nowrap; }
  /* Padding mendatar 8px, bukan 12px: sebelas kolom field + dua kontrol
   * membuat tabel 1.338px di wadah 1.232px (layar 1.280px, diukur di
   * browser 2026-09-23). 13 sel x 8px = 104px -- cukup untuk data biasa,
   * TIDAK dijamin untuk brand panjang atau nilai delapan digit. */
  .papan-tabel th, .papan-tabel .rapat td { padding-left: var(--s-2);
                                            padding-right: var(--s-2); }
  /* Karena itu jaminannya di sini: kolom Detail lengket di tepi kanan.
   * Mengklik baris tidak membuka modal -- hanya tombol ini (dan Enter), jadi
   * tombol yang tergulir keluar layar adalah detail yang tidak terjangkau
   * tetikus. Latarnya harus pekat, kalau tidak isi yang digulir terlihat
   * menembus; tiap keadaan baris karena itu diulang di sini. */
  .papan-tabel th:last-child, .papan-tabel .sel-detail {
    position: sticky; right: 0; border-left: 1px solid var(--line);
  }
  .papan-tabel th:last-child { z-index: 2; }
  .papan-tabel .sel-detail { z-index: 1; background: var(--surface); }
  .papan-tabel tbody tr:nth-child(even) .sel-detail { background: var(--zebra); }
  .papan-tabel tbody tr:hover .sel-detail,
  .papan-tabel tbody tr[data-fokus] .sel-detail { background: var(--surface-2); }
  .papan-tabel tbody tr:has(.pilih-baris:checked) .sel-detail {
    background: linear-gradient(var(--accent-lemah), var(--accent-lemah)) var(--surface);
  }
  /* Satu-satunya kolom yang nilainya kalimat, jadi satu-satunya yang boleh
   * dipotong. Teks utuhnya tetap ada di title pada <td>-nya. */
  .papan-tabel .sel-retur { max-width: 16ch; }
  .papan-tabel .sel-retur .lencana, .papan-tabel .sel-retur .faint {
    display: block; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Remarks: teks bebas sampai 400 karakter, dipotong dengan cara yang
   * sama. Utuhnya di title <td> dan di modal Detail. */
  .papan-tabel .sel-remarks { max-width: 20ch; }
  .papan-tabel .sel-remarks > span {
    display: block; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Judul kolom boleh membungkus dua baris; nilainya tidak. "Nilai
   * Pengembalian Dana" sebaris penuh memaksa kolomnya 188px untuk angka yang
   * cuma butuh 110px. Namanya TIDAK dipendekkan -- itu kata yang dipakai tim
   * ops dan yang sama dengan judul kolom di berkas unduhan. */
  .papan-tabel thead th { white-space: normal; }
  /* Tabel menggulir di dalam wadahnya sendiri (2026-09-24). Sebelumnya
   * kepala kolom TIDAK PERNAH lengket di layar ini: overflow-x pada .gulir
   * menjadikannya scroll container, jadi `top: 0` milik .tabel th menempel
   * pada wadah yang tidak pernah digulir tegak -- 50 baris lewat dan nama
   * kolomnya sudah lama hilang di atas. Dengan tinggi terbatas, wadah ini
   * yang digulir tegak dan kepalanya ikut lengket.
   *
   * Tingginya = jendela dikurangi bar atas, bar status (61px, diukur
   * 2026-09-24 di 1280x800) dan sedikit napas: begitu halaman digulir sampai
   * tabel, tabel mengisi layar persis di antara kedua bar. max() menjaga
   * layar pendek tetap menampilkan beberapa baris. overscroll-behavior-y
   * SENGAJA dibiarkan default: gulir yang sampai ujung tabel harus
   * diteruskan ke halaman, kalau tidak paginasi dan legenda di bawahnya
   * tidak terjangkau roda tetikus dari atas tabel. */
  .papan-gulir {
    max-height: max(320px, calc(100dvh - var(--bar-h) - 80px));
    overflow: auto;
    /* Bidang sendiri, bukan --bg halaman (2026-09-25): tanpa ini tabel
     * melebur ke latar dan batas antrean tidak terlihat. */
    background: var(--surface);
    border: 1px solid var(--line); border-radius: var(--r-md);
  }
  /* Kepala di atas sel Detail yang lengket ke kanan (z-index 1): tanpa ini
   * sel Detail baris yang tergulir ke atas tampak menembus kepala. Sudut
   * kanan atas lengket di dua arah, jadi ia yang paling atas. */
  .papan-tabel thead th { z-index: 3; }
  .papan-tabel thead th:last-child { z-index: 4; }
  /* Atas: setinggi kepala yang lengket (~50px), supaya J/K tidak menaruh
   * baris terfokus di bawahnya. Bawah: di atas bar status, untuk kasus
   * wadah belum seluruhnya di layar dan halamannya yang ikut digulir. */
  .papan-tabel tbody tr { scroll-margin-block: 56px 80px; }
  /* Belang: 13 kolom rapat dibaca mendatar, dan mata mudah loncat baris.
   * Ditaruh SEBELUM keadaan terpilih/terfokus supaya keduanya menang. */
  .papan-tabel tbody tr:nth-child(even) { background: var(--zebra); }
  /* Baris terpilih dan baris terfokus keyboard memakai AKSEN, bukan warna
   * status: keduanya keadaan interaksi, bukan keadaan data. */
  .papan-tabel tbody tr:has(.pilih-baris:checked) { background: var(--accent-lemah); }
  .papan-tabel tbody tr[data-fokus] {
    background: var(--surface-2); box-shadow: inset 2px 0 0 var(--accent);
  }
  .pri-satu { color: var(--fg); font-weight: 600; }

  /* Tombol salin (ui.salin). Redup sampai disentuh: empat per baris x 50
   * baris adalah 200 ikon, dan yang dibaca tetap nilainya, bukan tombolnya.
   * 24px = target sentuh minimum WCAG 2.2, tanpa menambah tinggi baris
   * rapat (margin negatif mengembalikan ruangnya). Centang menggantikan
   * ikon sebentar sesudah berhasil -- jawaban atas tindakan pengguna, jadi
   * tidak digerbang kurangGerak: tidak ada gerak, hanya tukar ikon. */
  .salin {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; margin: -4px 0 -4px 2px; padding: 0;
    vertical-align: middle; border: 0; border-radius: var(--r-sm);
    background: transparent; color: var(--faint); cursor: pointer;
  }
  .salin[hidden] { display: none; }
  .salin:hover { background: var(--surface-2); color: var(--fg); }
  .salin svg { width: 14px; height: 14px; }
  /* Aksen, bukan hijau status: "tersalin" keadaan interaksi, bukan data. */
  .salin-ok { display: none; color: var(--accent-teks); }
  .salin[data-tersalin] .salin-ikon { display: none; }
  .salin[data-tersalin] .salin-ok { display: inline-flex; }
  /* Tombol unduh foto memakai bentuk yang sama; selama foto diambil satu
   * per satu ia redup dan tidak menerima klik kedua. */
  .salin[aria-busy="true"] { cursor: progress; opacity: .5; }
  /* Tombol unduhan XLSX/CSV selama server menyusun berkas: teksnya sudah
   * berganti "Menyiapkan berkas…", di sini cukup tanda sedang bekerja. */
  .btn[data-unduh-berkas][aria-busy="true"] { cursor: progress; opacity: .7; }

  /* Tooltip (2026-10-14, keputusan Juan; dulu sengaja tidak ada). Satu
   * #tip untuk seluruh halaman, dipasang ui.js (pasangTooltip) dari
   * [data-tip]: warna dibalik dari halaman (latar --fg, teks --bg) supaya
   * terbaca di tema terang maupun gelap tanpa token baru. pre-line: teks
   * boleh berbaris lewat &#10;. Tanpa JS yang tampil title bawaan. */
  .tip { position: fixed; z-index: 70; max-width: 34ch; padding: 6px var(--s-2);
         border-radius: var(--r-md); background: var(--fg); color: var(--bg);
         box-shadow: var(--naik); font-size: var(--fs-xs); line-height: 1.4;
         font-variant-numeric: tabular-nums; white-space: pre-line; text-wrap: pretty; }
  .tip[hidden] { display: none; }
  /* Anchor (2026-10-15): di atas sasarannya, dipusatkan; tidak muat di atas
   * -> di bawah. Hanya bila ui.js memasang [data-anchor] (sasaran HTML);
   * sasaran SVG tetap memakai left/top dari ui.js. */
  @supports (position-try-fallbacks: flip-block) {
    [data-tip-buka] { anchor-name: --tip; }
    /* inset-inline 8px: jarak dari tepi layar saat anchor-center menjepit. */
    .tip[data-anchor] { position-anchor: --tip; inset-block: auto; inset-inline: 8px; margin: 0 0 8px;
                        position-area: top span-all; justify-self: anchor-center;
                        position-try-fallbacks: flip-block; }
  }
  @media (prefers-reduced-motion: no-preference) {
    .tip { opacity: 0; transition: opacity var(--t-cepat) var(--ease); }
    .tip[data-tampil] { opacity: 1; }
  }
  /* Tombol (i) istilah: tenang (--faint) sampai disentuh; 24px = target
   * minimum WCAG 2.5.8, margin negatif supaya baris teks tidak membesar. */
  .tip-pemicu { display: inline-grid; place-items: center; width: 24px; height: 24px;
                margin: -4px 0; padding: 0; vertical-align: middle; border: 0;
                border-radius: 50%; background: transparent; color: var(--faint); cursor: help; }
  .tip-pemicu:hover, .tip-pemicu[data-tip-buka] { background: var(--surface-2); color: var(--fg); }
  .tip-pemicu svg { width: 14px; height: 14px; }

  /* Legenda pintasan: terlihat di kaki tabel, bukan tersembunyi di balik
   * tombol tanda tanya. Pintasan yang harus ditemukan dulu adalah pintasan
   * yang tidak dipakai. */
  .legenda { display: flex; flex-wrap: wrap; gap: var(--s-2);
             align-items: center; margin: var(--s-3) 0 0;
             font-size: var(--fs-xs); color: var(--faint); }
  .legenda kbd { margin-right: 2px; }

  /* ── Bar aksi seleksi ──────────────────────────────────────────────────
   * Mengambang di atas bar status, muncul hanya saat ada yang dipilih.
   * SELALU membawa jumlahnya: tombol massal tanpa angka di sebelahnya tidak
   * memberi tahu seberapa besar akibatnya.
   */
  .bar-seleksi {
    /* 44px, bukan --s-4: bar status menempel di kaki jendela dan tingginya
     * sekitar 28px. Diperiksa di browser 2026-09-22 -- pada offset 16px bar
     * ini duduk PERSIS di atas paginasi dan menutupinya. */
    position: sticky; bottom: 44px; z-index: 25;
    display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
    width: fit-content; margin: var(--s-4) auto 0;
    padding: var(--s-2) var(--s-3);
    background: var(--surface); border: 1px solid var(--line-kuat);
    border-radius: var(--r-lg); box-shadow: var(--naik);
  }
  .bar-seleksi[hidden] { display: none; }
  .bar-seleksi-jumlah { font-size: var(--fs-sm); color: var(--muted); }
  .bar-seleksi-jumlah b { color: var(--fg); font-family: var(--mono); }
  .bar-seleksi-aksi { display: flex; gap: var(--s-2);
                      padding-left: var(--s-3); border-left: 1px solid var(--line); }

  /* ── Modal ─────────────────────────────────────────────────────────────
   * <dialog> asli: perangkap fokus, Esc, dan inert latar belakang sudah
   * bawaan. ui.js hanya menambahkan pengembalian fokus ke pemicunya.
   */
  .modal {
    width: min(920px, calc(100vw - var(--s-5) * 2));
    max-height: calc(100vh - var(--s-6)); padding: var(--s-5);
    background: var(--surface); color: var(--fg);
    border: 1px solid var(--line-kuat); border-radius: var(--r-lg);
    box-shadow: var(--naik);
  }
  .modal.kecil { width: min(460px, calc(100vw - var(--s-5) * 2)); }
  /* Modal Detail Papan Kerja lebih lebar sejak tabel item (2026-10-03):
   * dua belas kolom di 920px berarti menggulir sebelum kolom Foto Barang
   * pun terlihat. Di bawah lebar ini ia tetap mengikuti layar. */
  .modal.detail { width: min(1240px, calc(100vw - var(--s-5) * 2)); }
  .modal::backdrop { background: oklch(0 0 0 / 0.55); }
  .modal h2 { font-family: var(--display); font-size: var(--fs-lg);
              margin: 0 var(--s-6) var(--s-4) 0; }
  .modal-tutup-form { float: right; margin: calc(var(--s-2) * -1) calc(var(--s-2) * -1) 0 0; }
  .modal-tutup { font-size: var(--fs-lg); line-height: 1; }
  /* position: relative supaya teks .sr (position: absolute) di dalamnya ikut
   * terpotong di sini. Tanpanya ia keluar ke <dialog>, dialog mendapat gulir
   * KEDUA, dan judul "Order ..." ikut tergulir keluar layar. */
  .modal-isi { overflow-y: auto; max-height: calc(100vh - var(--s-6) - 96px);
               position: relative; }

  /* ── Modal Detail, arah Stitch (2026-10-06) ──
   * Berani di SATU tempat: nomor pesanan besar dalam mono -- itu yang
   * disalin dan dicocokkan PIC ke Seller Center. Sisanya tenang: fakta order
   * sebagai pasangan label-nilai berbingkai tipis, bagian dipisah garis 1px,
   * judul bagian huruf biasa (bukan label kapital kecil). Kepala di luar
   * .modal-isi, jadi fakta dan geser ‹ › tetap terlihat saat isi digulir;
   * tinggi isinya dikurangi kepala yang sekarang dua baris. */
  /* Kolom flex: kepala setinggi isinya, .modal-isi mengisi sisanya dan
   * satu-satunya yang bergulir. max-height tebakan (100vh - N px) membuat
   * dialog ikut bergulir begitu fakta turun ke baris kedua. */
  .modal.detail[open] { display: flex; flex-direction: column; }
  .modal.detail .modal-isi { max-height: none; flex: 1 1 auto; min-height: 0; }
  .detail-kepala { flex: none; }
  .detail-kepala { display: grid; align-items: start; gap: var(--s-3) var(--s-4);
                   grid-template-columns: minmax(0, 1fr) auto auto;
                   grid-template-areas: "judul nav tutup" "fakta fakta fakta"
                                        "tindak tindak tindak";
                   padding-bottom: var(--s-4); margin-bottom: var(--s-4);
                   border-bottom: 1px solid var(--line); }
  .detail-kepala h2 { grid-area: judul; margin: 0; display: flex;
                      align-items: flex-end; gap: var(--s-2); min-width: 0; }
  .detail-kepala .modal-tutup-form { grid-area: tutup; float: none; margin: 0; }
  .detail-judul-label { display: block; font-family: var(--sans); font-size: var(--fs-xs);
                        font-weight: 500; color: var(--muted); margin-bottom: 2px; }
  .detail-judul-nomor { display: block; font-family: var(--mono); font-size: var(--fs-2xl);
                        font-weight: 600; letter-spacing: 0.01em; line-height: 1.15;
                        font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
                        user-select: all; }
  .detail-kepala h2 > .salin { margin-bottom: 3px; }
  .detail-nav { grid-area: nav; }
  .detail-nav:not([hidden]) { display: flex; align-items: center; gap: var(--s-1); }
  .detail-nav .btn { min-width: 28px; justify-content: center; font-size: var(--fs-md); line-height: 1; }
  .detail-nav-posisi { font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted);
                       font-variant-numeric: tabular-nums; padding-inline: var(--s-1);
                       white-space: nowrap; }
  .detail-kepala-fakta { grid-area: fakta; }
  /* Bar tindakan (2026-10-09, permintaan Juan): SEMUA tombol modal di
   * kepala, di bawah fakta, dan isi di bawahnya tinggal rincian. Dua baris:
   * langkah berikutnya (medan + tombol sebaris) lalu baris sunyi Koreksi /
   * Putusan laporan / Cari order. Lipatan yang dibuka mengambil satu baris
   * penuh; kepala menempel, jadi yang terlipat menjaga ruang gulir rincian. */
  .detail-kepala-tindak { grid-area: tindak; }
  .detail-kepala-tindak:empty { display: none; }
  .detail-tindak-bar { display: flex; flex-wrap: wrap; align-items: center;
                       gap: var(--s-2) var(--s-4); padding-top: var(--s-3);
                       border-top: 1px solid var(--line); }
  .detail-kepala-tindak .detail-kabar { margin: 0 0 var(--s-2); }
  .detail-tindak-bar > .detail-kabar { flex-basis: 100%; margin: 0; }
  .detail-tindak-bar .detail-tindak { flex: 1 1 100%; margin: 0; }
  .detail-tindak-bar .detail-tindak > p { margin: 0; }
  .detail-tindak .tindak-form { display: flex; flex-wrap: wrap; align-items: flex-end;
                 gap: var(--s-2) var(--s-3); max-width: none; }
  .tindak-form .medan-tiket { flex: 0 1 auto; min-width: 16ch; }
  .tindak-form .medan-catatan { flex: 1 1 24ch; min-width: 18ch; max-width: 48ch; }
  .tindak-form .medan label { font-size: var(--fs-xs); white-space: nowrap; }
  .tindak-form .tindak-ket { flex-basis: 100%; margin: 0; }
  .detail-tindak-bar .detail-koreksi,
  .detail-tindak-bar .detail-putus { flex: none; margin: 0; }
  .detail-tindak-bar .detail-koreksi[open],
  .detail-tindak-bar .detail-putus:has(details[open]) { flex-basis: 100%; order: 1; }
  .detail-tindak-bar .detail-putus form { margin-top: var(--s-2); padding-left: var(--s-4);
                                          border-left: 1px solid var(--line); }
  .detail-tindak-bar .detail-aksi { margin: 0 0 0 auto; padding: 0; border: 0; }
  .detail-fakta { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: 0; }
  .detail-fakta > div { display: flex; align-items: baseline; gap: var(--s-2);
                        padding: 3px var(--s-2); border: 1px solid var(--line);
                        border-radius: var(--r-md); background: var(--surface-2); }
  .detail-fakta dt { font-size: var(--fs-xs); color: var(--muted); }
  .detail-fakta dd { margin: 0; font-size: var(--fs-sm); font-weight: 500; }
  /* Warna di kepala (permintaan Juan 2026-10-06, "di Stitch berwarna"):
   * Brand navy terisi -- identitas toko, warna bar aplikasi. Chip lain
   * diberi latar tipis dari warna STATUS-nya sendiri (--w), jadi warnanya
   * tetap berarti: Prioritas I merah, II-IV zaitun, Good hijau; status
   * banding memakai lima warna status; tenggat marketplace < 48 jam merah. */
  .detail-fakta > .fakta-brand { background: var(--bar-bg); border-color: var(--bar-bg); }
  .fakta-brand dt { color: var(--bar-muted); }
  .fakta-brand dd { color: var(--bar-fg); }
  .detail-fakta > .berwarna {
    background: color-mix(in srgb, var(--w, var(--muted)) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--w, var(--muted)) 35%, var(--surface)); }
  .detail-fakta > .berwarna dd { color: var(--w, var(--fg)); }
  .pri-i { --w: var(--st-ditolak); }
  .pri-ii, .pri-iii, .pri-iv { --w: var(--st-diajukan); }
  .pri-good { --w: var(--st-disetujui); }
  .st-belum { --w: var(--st-belum); }
  .st-diajukan { --w: var(--st-diajukan); }
  .st-disetujui { --w: var(--st-disetujui); }
  .st-ditolak { --w: var(--st-ditolak); }
  .st-diterima { --w: var(--st-diterima); }
  .tg-mendesak { --w: var(--sla-lewat); }
  .tg-aman { --w: var(--st-disetujui); }
  .detail-fakta .berwarna .tsh { color: inherit; font-weight: 600; }
  .detail-fakta .pri-satu { color: inherit; }
  /* Ikon judul bagian: satu-satunya oranye di isi modal selain tombol
   * utama, sebagai penanda tempat mata berhenti saat menggulir. */
  .modal.detail .modal-isi h3 { display: flex; align-items: center; gap: var(--s-2); }
  .modal.detail .modal-isi h3 > svg { width: 18px; height: 18px; flex: none;
                                      color: var(--accent-teks); }

  /* Bagian-bagian isi dipisah garis, bukan kotak: kotak bersarang di dalam
   * modal yang sudah berbingkai membuat tiga lapis bingkai. Pengecualian:
   * .bukti-kosong, yang memang bingkai putus-putus. */
  .modal.detail .modal-isi > :is(section, .detail-aksi) {
    margin: var(--s-5) 0 0; padding-top: var(--s-5); border-top: 1px solid var(--line); }
  .modal.detail .modal-isi > :first-child { margin-top: 0; padding-top: 0; border-top: 0; }
  .detail-aksi { display: flex; justify-content: flex-end; gap: var(--s-2); }
  .detail-bagian h3 { margin: 0 0 var(--s-3); font-family: var(--display);
                      font-size: var(--fs-md); font-weight: 600; color: var(--fg); }

  .detail-daftar { display: grid; gap: var(--s-3) var(--s-5); margin: 0;
                   grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
  .detail-daftar > div.lebar { grid-column: 1 / -1; }
  .detail-daftar dt { font-size: var(--fs-xs); color: var(--faint); margin-bottom: 2px; }
  .detail-daftar dd { margin: 0; font-size: var(--fs-sm); }
  /* Dana Return: totalnya angka terbesar di bagian itu; rinciannya tetap
   * kalimat kecil di bawahnya (.dana-rinci). */
  .detail-dana .dana-total { font-family: var(--mono); font-size: var(--fs-lg);
                             font-weight: 600; font-variant-numeric: tabular-nums;
                             color: var(--st-disetujui); }
  .detail-dana .dana-total :is(.kecil, .dana-rinci, .dana-penjelasan) {
    font-size: var(--fs-xs); font-weight: 400; }
  .detail-dana .dana-total > .faint { font-family: var(--sans); font-size: var(--fs-sm);
                                      font-weight: 400; }

  /* Panel bukti, keadaan BERISI. Foto gudang ada untuk 38,4% baris antrean;
   * rekaman menjangkau lebih luas lagi, tapi berapa luasnya BELUM diukur.
   *
   * Petaknya netral seluruhnya -- tidak ada satu pun warna jenuh di sini.
   * Warna jenuh di aplikasi ini hanya boleh berarti status, dan "ada bukti"
   * bukan status. Yang membedakan petak ini dari latar cuma garis 1px dan
   * gambarnya sendiri, yang memang satu-satunya hal berwarna yang pantas
   * menarik mata di panel ini. */
  .bukti { margin-top: var(--s-5); }
  .bukti h3, .bukti-kosong h3 { margin: 0 0 var(--s-2); color: var(--fg);
                                font-size: var(--fs-md); font-weight: 600;
                                font-family: var(--display); }
  .bukti-ket { margin: 0 0 var(--s-3); font-size: var(--fs-sm);
               color: var(--muted); max-width: 72ch; }
  .bukti-ket b { color: var(--fg); font-weight: 500; }

  /* Baris resi di atas tiap kelompok petak. Mono, karena nomor resi dibaca
   * dengan MENCOCOKKAN karakter satu per satu ke layar gudang atau ke Seller
   * Center -- itu satu-satunya cara ia dipakai, dan huruf proporsional membuat
   * 0/O dan 1/l harus ditebak. Netral, bukan warna: ia keterangan, bukan
   * status. */
  .bukti-resi { margin: 0 0 var(--s-2); font-size: var(--fs-xs); color: var(--muted); }
  .bukti-resi b { font-family: var(--mono); color: var(--fg); font-weight: 400;
                  user-select: all; }
  .bukti-petak + .bukti-resi { margin-top: var(--s-4); }

  .bukti-petak { list-style: none; margin: 0; padding: 0; display: grid;
                 gap: var(--s-3);
                 grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
  .bukti-petak-tautan { display: block; border: 1px solid var(--line);
                        border-radius: var(--r-md); overflow: hidden;
                        background: var(--surface-2); text-decoration: none; }
  /* Rasio dikunci supaya tata letaknya tidak melompat saat gambar datang satu
   * per satu -- foto gudang seragam 640x480. Latar surface-2 menahan tempatnya
   * sebelum berkasnya sampai, jadi tidak ada petak putih berkedip. */
  .bukti-petak img { display: block; width: 100%; height: auto;
                     aspect-ratio: 4 / 3; object-fit: cover;
                     background: var(--surface-2); }
  .bukti-petak-label { display: block; padding: var(--s-2);
                       font-size: var(--fs-xs); color: var(--muted);
                       border-top: 1px solid var(--line); }
  /* Tombol unduh satu foto duduk di pojok strip label, bukan di atas
   * gambarnya: foto yang sedang diperiksa tidak boleh tertutup ikon. Ia di
   * luar <a>, jadi diletakkan absolut terhadap <li>; padding kanan label
   * memberi tempatnya. */
  .bukti-petak li { position: relative; }
  .bukti-petak li > .salin { position: absolute; right: 3px; bottom: 3px;
                             margin: 0; }
  .bukti-petak li:has(> .salin) .bukti-petak-label { padding-right: 32px; }
  .bukti-petak-tautan:hover { border-color: var(--accent); }
  .bukti-petak-tautan:hover .bukti-petak-label { color: var(--fg); }

  /* Daftar rekaman. Tautan biasa, TIDAK bergaya seperti petak foto: keduanya
   * bukan hal yang sama dan tidak boleh terlihat sama. Foto adalah bukti yang
   * akan dilampirkan; rekaman adalah rujukan yang cuma bisa ditonton. Petak
   * berbingkai untuk sesuatu yang tidak punya gambar cuma kotak kosong yang
   * berpura-pura punya isi. */
  .bukti-video-ket { margin-top: var(--s-4); }
  .bukti-video { list-style: none; margin: 0; padding: 0; display: grid;
                 gap: var(--s-2); }
  .bukti-video li { display: flex; align-items: center; gap: var(--s-2);
                    font-size: var(--fs-sm); }
  .bukti-video a { display: inline-flex; align-items: center; gap: var(--s-2); }
  .bukti-video svg { width: 15px; height: 15px; flex: none; }

  /* Panel bukti, keadaan KOSONG -- 1 dari 46.320 baris antrean, terukur
   * 2026-09-22. Ini cabang PERTAHANAN, bukan keadaan yang dirancang untuk
   * sering dilihat; jangan menatanya seolah kosong itu biasa.
   *
   * Ditata sebagai keterangan tenang, BUKAN sebagai peringatan: bingkai
   * bergaris putus dan warna netral, tidak ada yang salah di sini. */
  .bukti-kosong { margin-top: var(--s-5); padding: var(--s-4);
                  border: 1px dashed var(--line); border-radius: var(--r-md); }
  .bukti-kosong p { margin: 0; font-size: var(--fs-sm); color: var(--muted);
                    max-width: 72ch; }
  .bukti-kosong b { color: var(--fg); font-weight: 500; }

  /* Tabel item (2026-10-03): isi PERTAMA modal Detail, menjawab "foto ini
   * untuk item yang mana". Memakai .tabel dan .order-lengket yang sudah ada
   * -- kolom Nama Item lengket saat digulir ke kanan, sama seperti Order ID
   * di antrean -- bukan komponen tabel kedua.
   *
   * Satu-satunya penekanan: baris rusak diberi garis kiri dan kata "rusak"
   * tebal. Tidak ada warna jenuh -- rusak adalah fakta gudang, bukan status
   * banding, dan warna jenuh di aplikasi ini hanya untuk status. */
  .detail-item { margin: 0 0 var(--s-5); }

  /* Teks banding (2026-10-09): kalimat yang ops salin ke Seller Center,
   * di baris item sebelah kondisinya. Netral seperti sel lain -- bahan
   * kerja, bukan status. Lebar sel dibatasi supaya kalimatnya terbaca
   * sebagai paragraf, bukan satu baris panjang yang memaksa tabel digulir
   * jauh; pre-line mempertahankan baris URL video, dan URL boleh patah di
   * mana saja. */
  .item-tabel .item-teks { min-width: 18rem; max-width: 30rem; }
  .teks-label { margin: 0 0 var(--s-1); display: flex; align-items: center;
                gap: var(--s-2); font-weight: 600; color: var(--fg); }
  .teks-isi { margin: 0; color: var(--fg); white-space: pre-line;
              overflow-wrap: anywhere; }
  .item-gulir { border: 1px solid var(--line); border-radius: var(--r-md); }
  .item-gulir:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .item-tabel td { font-size: var(--fs-sm); }
  .item-tabel .mono { font-family: var(--mono); font-size: var(--fs-xs);
                      font-variant-numeric: tabular-nums; user-select: all; }
  /* Nama barang boleh membungkus, tapi tidak boleh jadi kolom satu kata:
   * nama Item_master sampai 60-an karakter. */
  .item-tabel .item-nama { min-width: 22ch; max-width: 32ch; color: var(--fg); }
  /* Kolom Nama lengket (.order-lengket). Tanpa scroll-padding, Tab ke foto
   * atau tautan Buka menggulir tabel tepat sejauh yang perlu -- dan elemen
   * yang difokus berhenti DI BAWAH kolom lengket itu. Nilainya sedikit di
   * atas lebar kolom Nama (32ch + padding); kelebihan hanya berarti tabel
   * bergeser sedikit lebih jauh. Di ponsel kolomnya disempitkan supaya
   * kolom lain masih punya ruang. */
  .item-gulir { scroll-padding-left: 16rem; }
  @media (max-width: 719.98px) {
    .item-tabel .item-nama { min-width: 14ch; max-width: 18ch; }
    .item-gulir { scroll-padding-left: 10rem; }
  }
  .item-tabel .item-catatan { min-width: 18ch; max-width: 36ch; color: var(--muted); }
  /* Baris tak layak jual: garis kiri saja; kodenya sendiri yang menjelaskan. */
  .item-tabel tr.item-rusak td:first-child { box-shadow: inset 3px 0 0 var(--st-ditolak); }
  .item-tabel td a { display: inline-flex; align-items: center; gap: var(--s-1); }
  .item-tabel td a svg { width: 15px; height: 15px; flex: none; }

  /* Foto barang di sel: petak kecil 64x48 (rasio foto gudang 4:3), tombol
   * unduh di bawahnya, bukan di atas gambarnya -- alasan yang sama dengan
   * petak di panel Bukti. Membungkus ke baris berikut kalau banyak; sel ini
   * dibatasi supaya satu item berfoto 100+ tidak meratakan tabel. */
  .item-foto { list-style: none; margin: 0; padding: 0; display: flex;
               flex-wrap: wrap; gap: var(--s-2); min-width: 64px;
               max-width: calc(64px * 4 + var(--s-2) * 3); }
  .item-foto li { display: grid; justify-items: center; gap: 2px; }
  .item-foto a { display: block; border: 1px solid var(--line);
                 border-radius: var(--r-sm); overflow: hidden;
                 background: var(--surface-2); }
  .item-foto a:hover { border-color: var(--accent); }
  .item-foto img { display: block; width: 64px; height: 48px; object-fit: cover; }
  .item-foto .salin { margin: 0; }

  /* Kepala paket (2026-10-03, sore): isian milik PAKET -- AWB, transporter,
   * username, rekaman, notes -- tampil sekali di sini, bukan di tiap baris
   * item. Memakai .detail-daftar yang sama dengan ringkasan order di bawah,
   * hanya lebih rapat: ia keterangan tabel di bawahnya, bukan isi utama. */
  .paket + .paket { margin-top: var(--s-5); padding-top: var(--s-4);
                    border-top: 1px solid var(--line); }
  .paket-judul { margin: 0 0 var(--s-2); font-size: var(--fs-sm); font-weight: 600;
                 color: var(--fg); }
  .detail-daftar.paket-kepala { gap: var(--s-2) var(--s-4); margin-bottom: var(--s-3);
                                grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .paket-kepala .mono { font-family: var(--mono); font-size: var(--fs-xs); user-select: all; }
  /* Notes gudang ditulis per baris ("3.OK / 2.PENYOK / 1.BOCOT"); dipadatkan
   * jadi satu baris, urutannya tidak terbaca. */
  .paket-catatan { white-space: pre-line; color: var(--muted); }
  .paket-video { display: inline-flex; align-items: center; gap: var(--s-1); }
  .paket-video svg { width: 15px; height: 15px; flex: none; }
  /* Foto resi di samping kepala paket; di layar sempit turun ke bawahnya. */
  /* Kartu logistik (arah Stitch, 2026-10-06): satu-satunya bidang berlatar
   * di isi modal, karena isinya yang paling sering disalin -- AWB, video. */
  .paket-atas { margin-bottom: var(--s-3); padding: var(--s-3) var(--s-4);
                background: var(--surface-2); border: 1px solid var(--line);
                border-radius: var(--r-lg); }
  .paket-video { padding: 2px var(--s-2); border: 1px solid transparent;
                 border-radius: var(--r-md); background: var(--accent-lemah);
                 color: var(--accent-teks); font-weight: 500; text-decoration: none; }
  .paket-video:hover { border-color: var(--accent); }
  .paket-atas .paket-kepala { margin-bottom: 0; }
  .paket-atas.ada-foto { display: grid; gap: var(--s-4);
                         grid-template-columns: minmax(0, 1fr) auto; align-items: start; }
  @media (max-width: 719.98px) {
    .paket-atas.ada-foto { grid-template-columns: 1fr; }
  }
  /* Rupa label sama dengan dt di sebelahnya: ia isian paket juga. */
  .paket-foto-label { margin: 0 0 2px; font-size: var(--fs-xs); color: var(--faint); }
  .paket-foto .bukti-petak { grid-template-columns: repeat(auto-fill, 120px);
                             max-width: calc(120px * 2 + var(--s-3)); }

  /* Riwayat banding dan Tindakan di modal (2026-09-23). Judulnya memakai
   * gaya judul Bukti, bukan gaya baru: tiga panel di satu modal yang judulnya
   * berbeda rupa terbaca sebagai tiga jenis hal, padahal ketiganya bagian
   * dari satu order. Riwayat berupa daftar rapat, bukan tabel enam kolom
   * seperti di Cari order -- IP dan "dari status" tidak dibutuhkan untuk
   * memutuskan langkah berikutnya. */
  .detail-riwayat, .detail-tindak, .detail-putus { margin-top: var(--s-5); }
  .detail-riwayat h3, .detail-tindak h3, .detail-putus h3 {
    margin: 0 0 var(--s-2); color: var(--fg); font-weight: 600;
    font-size: var(--fs-md); font-family: var(--display); }
  .detail-riwayat p, .detail-tindak p, .detail-putus p { margin: var(--s-2) 0 0; max-width: 72ch; }
  .riwayat-daftar { list-style: none; margin: 0; padding: 0; display: grid; }
  .riwayat-daftar li { display: flex; flex-wrap: wrap; align-items: baseline;
                       gap: var(--s-1) var(--s-2); padding: var(--s-2) 0;
                       border-bottom: 1px solid var(--line); font-size: var(--fs-sm); }
  .riwayat-daftar li:first-child { padding-top: 0; }
  .riwayat-oleh { color: var(--fg); }
  .riwayat-daftar .riwayat-catatan { flex-basis: 100%; margin: 0; color: var(--muted); }
  .detail-tindak form, .detail-putus form { max-width: 48ch; }
  .detail-putus form { margin-top: var(--s-3); }
  /* Koreksi status: satu klik lebih jauh dari langkah berikutnya, dan
   * rupanya sengaja lebih pelan -- ia untuk salah catat, bukan kerja harian. */
  .detail-koreksi { margin-top: var(--s-4); }
  .detail-koreksi > form { margin-top: var(--s-2); padding-left: var(--s-4);
                           border-left: 1px solid var(--line); }

  /* Tempel Order ID (dari Cek rusak). Di aliran halaman, bukan popover
   * mengambang: ia jarang dibuka, dan kotak yang menutupi tabel antrean
   * menyembunyikan baris yang sedang dicocokkan orang dengan Seller Center.
   *
   * papan-alat (2026-09-24): saringan + Tandai banyak dalam SATU bingkai --
   * saringan kiri, Unduh dan Tandai banyak kanan. Bingkainya pindah dari
   * .saring ke pembungkus. Ringkasan <details> diangkat ke ujung kanan baris
   * pertama saringan; isinya tetap mengalir DI BAWAH saringan, selebar
   * bingkai, jadi alasan "bukan popover" di atas tetap berlaku.
   *
   * Container query, bukan media query: yang menentukan muat-tidaknya adalah
   * lebar bingkai, dan di bawah ambangnya ringkasan kembali ke aliran biasa
   * di bawah saringan (susunan lama). Lebar combobox dipatok di sini saja
   * -- lebar bawaannya (215px) membuat enam medan tidak pernah satu baris. */
  .papan-alat { --lebar-tempel: 10.75rem; position: relative;
                container-type: inline-size; margin-bottom: var(--s-4);
                background: var(--surface); border: 1px solid var(--line);
                border-radius: var(--r-md); }
  .papan-alat > .saring { margin: 0; border: 0; background: none;
                          align-items: center; gap: var(--s-2); }
  /* Satu baris (2026-09-24): chip, cari, Filter, Unduh. Chip kehilangan
   * margin bawahnya sendiri -- jaraknya sekarang milik gap bar. */
  .papan-alat .chip-baris { margin: 0 var(--s-2) 0 0; }
  .papan-alat .saring-kanan { margin-left: auto; }

  /* Tab jenis (2026-10-09): bentuk tab bergaris bawah, bukan pil, supaya
   * terbaca sebagai tingkat DI ATAS chip status -- dua deret pil yang sama
   * sama-sama berawal "Semua" akan terbaca sebagai satu daftar. Di layar
   * sempit tab bergeser di dalam deretnya sendiri, tidak melebarkan halaman. */
  .tab-jenis { display: flex; gap: var(--s-1); margin-bottom: var(--s-3);
               border-bottom: 1px solid var(--line);
               overflow-x: auto; scrollbar-width: none; }
  .tab-jenis .chip { border: 0; border-bottom: 2px solid transparent;
                     border-radius: 0; padding: var(--s-2) var(--s-3);
                     margin-bottom: -1px; font-size: var(--fs-base); }
  .tab-jenis .chip:hover { border-bottom-color: var(--line-kuat); }
  .tab-jenis .chip.aktif { background: none; color: var(--fg);
                           border-bottom-color: var(--accent); }

  /* Tombol Filter + angka saringan aktif. Angkanya berbidang aksen karena
   * ia satu-satunya tanda bahwa panel yang tertutup sedang menyaring. */
  .tombol-filter { gap: 6px; }
  .tombol-filter svg { width: 14px; height: 14px; }
  .jumlah-filter { display: inline-grid; place-items: center; min-width: 18px;
    height: 18px; padding: 0 5px; border-radius: 999px; font-family: var(--mono);
    font-size: var(--fs-xs); font-weight: 600; background: var(--accent);
    color: var(--accent-fg); }

  /* Panel Filter (popover). Posisinya diikat ke tombolnya lewat anchor (di
   * bawah); browser tanpa anchor memakai ui.js, dan tanpa keduanya ia muncul
   * di tengah layar (bawaan popover) -- tetap bisa dipakai. overflow: visible supaya daftar combobox tidak terpotong
   * bingkai panel. Di browser tanpa popover, atributnya diabaikan dan panel
   * ini mengalir biasa di bar: aturan grid di bawah tetap berlaku. */
  .panel-filter {
    width: min(34rem, calc(100vw - 32px)); padding: var(--s-4);
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-3) var(--s-4); overflow: visible; color: var(--fg);
    background: var(--surface); border: 1px solid var(--line-kuat);
    border-radius: var(--r-md); box-shadow: var(--naik);
  }
  /* `display: grid` di atas MENIMPA display: none bawaan UA untuk popover
   * tertutup (aturan penulis menang atas UA), jadi penyembunyiannya ditulis
   * ulang di sini. Browser tanpa popover tidak mengenal :popover-open dan
   * membuang aturan ini seluruhnya -- panelnya tampil di aliran, sesuai
   * niat. margin/inset bawaan popover (tengah layar) dibiarkan: ui.js yang
   * menimpanya saat menempatkan panel di bawah tombol. */
  .panel-filter:not(:popover-open) { display: none; }
  .panel-filter .saring-medan { min-width: 0; }
  /* Anchor positioning (2026-10-15; Chrome 125+, ops 152+): panel diikat
   * ke tombolnya oleh browser, jadi ikut tepat saat halaman digulir --
   * dulu ui.js menghitung ulang di tiap event scroll, selalu satu frame
   * terlambat. Rata kiri dengan tombol; tidak muat ke kanan -> rata kanan;
   * tidak muat dua-duanya (telepon) -> di tengah layar. Tingginya tetap
   * diurus ui.js (tempatkanPanel). Browser tanpa anchor: ui.js seperti dulu. */
  @supports (position-try-fallbacks: flip-inline) {
    [popovertarget="panel-filter"] { anchor-name: --tombol-filter; }
    #panel-filter { position-anchor: --tombol-filter; inset: auto; margin: 6px 0 0;
                    position-area: bottom span-right;
                    position-try-fallbacks: flip-inline, --filter-tengah; }
    @position-try --filter-tengah { position-area: bottom span-all; justify-self: center; }
  }
  @media (prefers-reduced-motion: no-preference) {
    .panel-filter {
      transition: opacity var(--t-cepat) var(--ease), transform var(--t-cepat) var(--ease),
                  overlay var(--t-cepat) allow-discrete, display var(--t-cepat) allow-discrete;
    }
    .panel-filter:not(:popover-open) { opacity: 0; transform: translateY(-4px); }
    @starting-style {
      .panel-filter:popover-open { opacity: 0; transform: translateY(-4px); }
    }
  }
  /* Rentang tanggal: satu baris penuh, kolom + dua ujungnya berdampingan --
   * tiga medan yang dibaca sebagai satu kalimat ("Tanggal Pesanan dari ...
   * sampai ..."), bukan tiga saringan lepas di grid dua kolom. */
  .saring-tanggal { grid-column: 1 / -1; display: grid; gap: var(--s-2) var(--s-3);
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr);
    padding-top: var(--s-3); border-top: 1px solid var(--line); }
  .saring-tanggal > .kecil { grid-column: 1 / -1; color: var(--muted); }
  .panel-filter-aksi { grid-column: 1 / -1; display: flex; justify-content: flex-end;
    gap: var(--s-2); padding-top: var(--s-3); border-top: 1px solid var(--line); }
  @media (max-width: 480px) {
    .panel-filter { grid-template-columns: minmax(0, 1fr); }
    .saring-tanggal { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .saring-tanggal > .saring-medan:first-of-type { grid-column: 1 / -1; }
  }
  .papan-alat .tempel-borongan > summary { margin: 0 var(--s-4) var(--s-3);
                                           color: var(--fg); }
  .papan-alat .tempel-borongan > summary::before { color: var(--muted); }
  .papan-alat .tempel-borongan > form { padding: var(--s-3) var(--s-4);
                                        border-top: 1px solid var(--line); }
  .papan-alat .tempel-borongan > form > * { max-width: 48ch; }
  @container (min-width: 760px) {
    .papan-alat.ada-tempel > .saring {
      padding-right: calc(var(--s-4) + var(--lebar-tempel) + var(--s-2)); }
    /* Sebaris dengan baris pertama bar (align-items: center): padding
     * saringan + separuh selisih tinggi input (34px) dan tombol kecil (28px). */
    .papan-alat .tempel-borongan > summary {
      position: absolute; right: var(--s-4); top: calc(var(--s-3) + 3px);
      width: var(--lebar-tempel); margin: 0; }
    /* Isinya dirapatkan ke kanan, di bawah tombol yang membukanya -- bukan
     * di ujung kiri, jauh dari tempat mata orang baru saja mengklik. */
    .papan-alat .tempel-borongan > form {
      grid-template-columns: minmax(0, 48ch); justify-content: end; }
  }

  .detail-aksi { display: flex; flex-wrap: wrap; gap: var(--s-2);
                 margin-top: var(--s-5); padding-top: var(--s-4);
                 border-top: 1px solid var(--line); }

  /* Konfirmasi: akibatnya disebut sebelum tombolnya, dan yang difokus adalah
   * opsi AMAN. Urutan tombolnya mengikuti itu -- Batal dulu, baru aksinya. */
  .konfirmasi-ket { font-size: var(--fs-sm); color: var(--muted);
                    margin: 0 0 var(--s-4); max-width: 60ch; }
  .konfirmasi-tombol { display: flex; gap: var(--s-2); justify-content: flex-end;
                       margin-top: var(--s-4); }

  @media (prefers-reduced-motion: no-preference) {
    /* SATU gerak di seluruh layar ini, dan ia menjawab tindakan pengguna:
     * modal yang muncul harus terbaca datang dari mana. Tidak ada
     * fade-and-slide-up pada baris tabel, tidak ada transisi pada hover
     * kartu -- antrean yang bergerak sendiri saat dibaca memperlambat
     * pembacaannya. */
    .modal, .modal::backdrop {
      transition: opacity var(--t-cepat) var(--ease), transform var(--t-cepat) var(--ease),
                  overlay var(--t-cepat) allow-discrete, display var(--t-cepat) allow-discrete;
      opacity: 0;
    }
    .modal { transform: translateY(6px) scale(0.99); }
    /* Masuk 200ms, keluar 120ms: yang ditutup tidak perlu dilihat lagi. */
    .modal[open], .modal[open]::backdrop { opacity: 1; transform: none;
                                           transition-duration: var(--t-sedang); }
    @starting-style {
      .modal[open], .modal[open]::backdrop { opacity: 0; }
      .modal[open] { transform: translateY(6px) scale(0.99); }
    }
    .bar-seleksi { transition: opacity var(--t-cepat) var(--ease); }
  }

  @media (max-width: 719.98px) {
    /* Di lebar telepon bar seleksi merentang penuh dan menempel di kaki:
     * bar mengambang di tengah layar sempit menutupi baris yang sedang
     * dipilih orang. */
    .bar-seleksi { width: auto; margin-inline: 0; bottom: 0;
                   border-radius: var(--r-md) var(--r-md) 0 0; }
    .modal, .modal.detail { width: 100vw; max-width: 100vw; max-height: 100vh;
             border-radius: 0; border: 0; padding: var(--s-4); }
    /* Di telepon kepala Detail tidak menempel: fakta yang turun ke lima
     * baris memakan separuh layar. Dialognya sendiri yang bergulir, kepala
     * ikut. Geser ‹ › pindah ke baris sendiri supaya nomor pesanan 18 digit
     * TikTok tetap satu baris. */
    .modal.detail[open] { display: block; overflow-y: auto; }
    .modal.detail .modal-isi { overflow: visible; }
    .detail-kepala { grid-template-columns: minmax(0, 1fr) auto;
                     grid-template-areas: "judul tutup" "nav nav" "fakta fakta"
                                          "tindak tindak"; }
    .tindak-form .medan-tiket, .tindak-form .medan-catatan { flex: 1 1 100%; max-width: none; }
    .tindak-form .baris .btn { flex: 1 1 auto; justify-content: center; min-height: 40px; }
    .detail-judul-nomor { font-size: var(--fs-lg); }
  }
}


/* ── Beranda ───────────────────────────────────────────────────────────────
 *
 * Susunan dari mock Stitch (2026-09-23): pita sapaan, empat kartu angka,
 * kisi 2/3 + 1/3, baris pintasan. Isinya tetap data kita; padanan tiap kotak
 * mock ada di komentar pembuka templates/beranda.html.
 *
 * Berani di SATU tempat: tombol utama di pita (satu-satunya bidang oranye
 * di layar). Empat kartu angka cuma membawa arti lewat garis atas 3px, dan
 * angkanya ikut berwarna hanya saat keadaannya tenggat.
 */
@layer komponen {

  /* ── Pita kepala ─────────────────────────────────────────────────────────
   * Ditarik keluar dari padding .isi dengan margin negatif yang SAMA dengan
   * padding .isi di tiap lebar. Kalau keduanya berbeda, tepi pita tidak lagi
   * sejajar dengan tepi kartu di bawahnya. */
  .muka-kepala {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    justify-content: space-between; gap: var(--s-4) var(--s-5);
    margin: calc(var(--s-5) * -1) calc(var(--s-5) * -1) var(--s-5);
    padding: var(--s-5);
    background: var(--surface-2); border-bottom: 1px solid var(--line);
  }
  .muka-kepala .kepala { margin: 0; min-width: 0; flex: 1 1 420px; }
  .muka-tindakan { display: flex; flex-wrap: wrap; align-items: center;
                   gap: var(--s-3); }
  .muka-hari {
    display: flex; align-items: center; gap: var(--s-2); margin: 0;
    padding: 4px var(--s-3); min-height: 40px;
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-md); font-size: var(--fs-xs); line-height: 1.35;
  }
  .muka-hari > span { display: flex; flex-direction: column; }
  .muka-hari svg { width: 18px; height: 18px; color: var(--muted); flex: none; }
  .btn.besar { min-height: 40px; padding: var(--s-2) var(--s-4);
               font-size: var(--fs-base); font-weight: 600; }
  .btn.besar svg { width: 16px; height: 16px; }

  /* ── Empat kartu angka ─────────────────────────────────────────────────── */
  .kpi-baris {
    list-style: none; margin: 0 0 var(--s-5); padding: 0;
    display: grid; gap: var(--s-3);
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  }
  .kpi {
    min-width: 0; background: var(--surface);
    border: 1px solid var(--line); border-top: 3px solid var(--line-kuat);
    border-radius: var(--r-md);
  }
  .kpi.lewat    { border-top-color: var(--sla-lewat); }
  .kpi.mendesak { border-top-color: var(--sla-mendesak); }
  .kpi.aksen    { border-top-color: var(--accent); }
  .kpi.diajukan { border-top-color: var(--st-diajukan); }
  .kpi-isi {
    display: flex; flex-direction: column; gap: var(--s-2); height: 100%;
    padding: var(--s-4); color: inherit; border-radius: inherit;
  }
  a.kpi-isi:hover { background: var(--surface-2); text-decoration: none; }
  /* Tinggi dua baris label: tanpa ini angka kartu yang labelnya membungkus
   * turun satu baris dari tetangganya, dan keempatnya berhenti terbaca
   * sebagai satu deret. */
  .kpi-kepala { display: flex; align-items: flex-start;
                justify-content: space-between; gap: var(--s-2);
                min-height: calc(2 * 1.35 * 0.8125rem); }
  .kpi-label { font-size: var(--fs-sm); font-weight: 600; color: var(--fg);
               line-height: 1.35; }
  .kpi-chip {
    flex: none; font-size: var(--fs-xs); font-weight: 600; white-space: nowrap;
    padding: 2px 6px; border-radius: var(--r-sm);
    background: var(--surface-2); color: var(--muted);
  }
  .kpi.lewat .kpi-chip    { color: var(--sla-lewat); }
  .kpi.mendesak .kpi-chip { color: var(--sla-mendesak); }
  .kpi-nilai {
    font-family: var(--display); font-weight: 700; font-size: var(--fs-3xl);
    line-height: 1.1; letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums; color: var(--fg);
  }
  .kpi.lewat .kpi-nilai    { color: var(--sla-lewat); }
  .kpi.mendesak .kpi-nilai { color: var(--sla-mendesak); }
  .kpi-nilai .satuan { font-family: var(--sans); font-size: var(--fs-sm);
                       font-weight: 500; letter-spacing: 0;
                       color: var(--muted); margin-left: 0.4em; }
  .kpi-kaki {
    margin-top: auto; padding-top: var(--s-2); border-top: 1px solid var(--line);
    font-size: var(--fs-xs); line-height: 1.45; color: var(--muted);
  }
  .kpi-lewat { color: var(--sla-lewat); font-weight: 600; }
  .kpi-cek { color: var(--sla-mendesak); font-weight: 600; }

  /* ── Kisi utama ──────────────────────────────────────────────────────────
   * 2fr/1fr seperti mock: kiri memuat tabel lima kolom, kanan daftar pendek. */
  .beranda-kisi { display: grid; gap: var(--s-5); align-items: start;
                  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
  .beranda-kolom { min-width: 0; }
  .beranda-kolom > .kartu { margin: 0; }
  .beranda-kolom > .kartu + .kartu { margin-top: var(--s-5); }

  .kartu-tanda {
    margin-left: auto; font-size: var(--fs-xs); font-weight: 700;
    padding: 2px 8px; border-radius: var(--r-sm);
    background: var(--accent-lemah); color: var(--accent-teks);
  }
  .kartu-kaki {
    display: flex; flex-wrap: wrap; align-items: center;
    justify-content: space-between; gap: var(--s-2) var(--s-4);
    padding: var(--s-3) var(--s-4); border-top: 1px solid var(--line);
    font-size: var(--fs-xs);
  }
  .kartu-kaki a { display: inline-flex; align-items: center; gap: 4px;
                  font-weight: 600; }
  .kartu-kaki svg { width: 14px; height: 14px; }

  .tabel.kritis td { vertical-align: middle; }
  .kritis-kanal, .kritis-sub { display: block; margin-top: 2px;
                               font-size: var(--fs-xs); color: var(--muted); }
  .kritis-kanal { text-transform: capitalize; }
  .tabel.kritis .btn svg { width: 13px; height: 13px; }

  /* ── Batang komposisi ────────────────────────────────────────────────────
   * Tata bahasa yang SAMA dengan .umur, dan itu syarat, bukan pengulangan
   * gaya: kelompok Undel tidak punya garis akhir, kelompok retur pembeli
   * punya -- retur pembeli punya ambang, Undel tidak. Kalau di sini keduanya
   * digambar dengan cara lain, PIC belajar dua kosakata untuk satu perbedaan.
   *
   * Di dalam tiap kelompok ada dua pecahan yang dibedakan TERANG, bukan hue.
   * Hue sudah punya arti (lima keadaan klaim_status). Satu-satunya kecuali
   * "mendekati ambang": itu memang keadaan tenggat, dan memakai warnanya.
   *
   * PUDAR TIDAK DIPAKAI, meski .umur memakainya untuk Undel. Di .umur
   * panjang menyatakan WAKTU yang ujungnya tidak diketahui; di sini panjang
   * menyatakan PORSI yang diketahui persis. Terlihat juga saat dirender: pada
   * batang lebar, mask yang sama membuat 94% terbaca kira-kira 60%.
   *
   * `.pecah > .pecah-retur::after`, bukan `.pecah-retur::after` polos: aturan
   * dasar kelompoknya `.pecah > span` (0-1-1). Pelajaran lama dari layar
   * ini -- kekalahan spesifisitas di sini terlihat seperti pilihan desain. */
  .pecah { display: flex; gap: 3px; height: 10px;
           margin: var(--s-1) 0 var(--s-4); }
  .pecah > span {
    flex: 0 0 var(--bagian, 0%); min-width: 2px; position: relative;
    display: flex; gap: 2px;
  }
  .pecah > span > span { min-width: 0; border-radius: 2px; }
  .pecah .pecah-lama, .pecah .pecah-dekat { flex: 0 0 var(--bagian, 0%); }
  .pecah .pecah-baru, .pecah .pecah-aman  { flex: 1 1 0; }
  .pecah .pecah-lama  { background: var(--muted); }
  .pecah .pecah-baru  { background: var(--line-kuat); }
  .pecah .pecah-dekat { background: var(--sla-mendesak); }
  .pecah .pecah-aman  { background: var(--fg); }
  .pecah > .pecah-retur::after {
    content: ""; position: absolute; top: -3px; bottom: -3px; right: -4px;
    width: 2px; border-radius: 1px; background: var(--fg);
  }

  /* 2x2, bukan empat sejajar seperti mock: nama tiap pecahan membawa kotak
   * ui.jalur ("Retur pembeli") plus keterangannya, dan di lebar 185px itu
   * terpenggal jadi tiga baris sehingga keempat angkanya tidak lagi sebaris.
   * Dua kolom juga memasangkannya menurut jalur, sama dengan batangnya. */
  .komposisi {
    list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .komposisi > li {
    display: flex; flex-direction: column; gap: 4px; min-width: 0;
    padding: var(--s-3); background: var(--surface-2);
    border-radius: var(--r-md);
  }
  .komposisi-nama { display: flex; align-items: center; flex-wrap: wrap;
                    gap: 6px; font-size: var(--fs-xs); font-weight: 600; }
  .swatch { display: inline-block; width: 10px; height: 10px; flex: none;
            border-radius: 2px; }
  .swatch.lama  { background: var(--muted); }
  .swatch.baru  { background: var(--line-kuat); }
  .swatch.dekat { background: var(--sla-mendesak); }
  .swatch.aman  { background: var(--fg); }
  .komposisi-angka { font-size: var(--fs-xs); color: var(--muted); }
  .komposisi-angka b { font-family: var(--display); font-size: var(--fs-lg);
                       font-weight: 700; color: var(--fg); margin-right: 4px;
                       font-variant-numeric: tabular-nums; }
  .komposisi-ket { font-size: var(--fs-xs); color: var(--muted); line-height: 1.4; }

  /* ── Antrean per marketplace ─────────────────────────────────────────── */
  .daftar-kanal { list-style: none; margin: 0; padding: 0;
                  display: flex; flex-direction: column; gap: var(--s-2); }
  .daftar-kanal a {
    display: grid; align-items: center; gap: 2px var(--s-3);
    grid-template-columns: 32px minmax(0, 1fr) auto 16px;
    grid-template-areas: "ini nama jumlah ikon" "ini angka dekat ikon";
    padding: var(--s-2) var(--s-3); color: inherit; text-decoration: none;
    border: 1px solid var(--line); border-radius: var(--r-md);
  }
  .daftar-kanal a:hover { background: var(--surface-2); text-decoration: none; }
  .kanal-inisial {
    grid-area: ini; width: 32px; height: 32px;
    display: grid; place-items: center;
    border-radius: var(--r-md); background: var(--surface-2);
    border: 1px solid var(--line); font-family: var(--mono);
    font-size: var(--fs-xs); font-weight: 600; color: var(--muted);
  }
  /* Nama dan singkatan kanal dipetakan di template (TikTok/TTS, Shopee/SH,
   * singkatan yang dipakai tim ops, 2026-10-09); kanal lain jatuh ke nilai
   * datanya apa adanya, dikapitalkan lewat CSS -- kanal ketiga tetap tampil
   * walau belum ada di peta. */
  .kanal-nama { grid-area: nama; text-transform: capitalize; font-weight: 600; }
  .kanal-angka { grid-area: angka; font-size: var(--fs-xs); color: var(--muted);
                 font-variant-numeric: tabular-nums; }
  .kanal-jumlah { grid-area: jumlah; justify-self: end; font-size: var(--fs-xs);
                  color: var(--muted); }
  .kanal-jumlah b { color: var(--fg); font-variant-numeric: tabular-nums; }
  .kanal-dekat { grid-area: dekat; justify-self: end; font-size: var(--fs-xs);
                 color: var(--muted); }
  .kanal-dekat.perhatian { color: var(--sla-mendesak); font-weight: 600; }
  .daftar-kanal svg { grid-area: ikon; width: 16px; height: 16px;
                      color: var(--faint); }
  .kanal-total {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s-3); margin: var(--s-3) 0 0; padding: var(--s-2) var(--s-3);
    background: var(--surface-2); border-radius: var(--r-md);
    font-size: var(--fs-xs); color: var(--muted);
  }
  .kanal-total b { color: var(--fg); }

  .ticketing { display: grid; gap: var(--s-3); margin: 0; }
  .ticketing > div { display: flex; align-items: baseline;
                     justify-content: space-between; gap: var(--s-3); }
  .ticketing dt { margin: 0; font-size: var(--fs-sm); color: var(--muted); }
  .ticketing dd { margin: 0; font-family: var(--display); font-weight: 700;
                  font-size: var(--fs-lg); font-variant-numeric: tabular-nums; }
  .ticketing-total { border-top: 1px solid var(--line); padding-top: var(--s-3); }
  .ticketing-total dt { color: var(--fg); }

  /* ── Pintasan ────────────────────────────────────────────────────────── */
  .pintasan-beranda {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
    margin-top: var(--s-5); padding: var(--s-3) var(--s-4);
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-md);
  }
  .pintasan-judul { display: inline-flex; align-items: center; gap: 6px;
                    margin-right: var(--s-2); font-size: var(--fs-xs);
                    font-weight: 600; color: var(--muted); }
  .pintasan-judul svg { width: 14px; height: 14px; }

  @media (max-width: 1099.98px) {
    .beranda-kisi { grid-template-columns: minmax(0, 1fr); }
  }
  /* .isi turun ke padding --s-4 di bawah 900px (lihat @layer tata); margin
   * negatif pita mengikutinya. */
  @media (max-width: 899.98px) {
    .muka-kepala { margin: calc(var(--s-4) * -1) calc(var(--s-4) * -1) var(--s-4);
                   padding: var(--s-4); }
  }
  @media (max-width: 719.98px) {
    .muka-tindakan, .muka-tindakan > * { flex: 1 1 100%; }
    .muka-tindakan > .btn { justify-content: center; }
  }
  /* Kartu angka bertumpuk satu kolom: tidak ada tetangga untuk disejajarkan,
   * cadangan dua baris label tinggal ruang kosong. */
  @media (max-width: 479.98px) {
    .kpi-kepala { min-height: 0; }
    .komposisi { grid-template-columns: minmax(0, 1fr); }
  }

  @media (prefers-reduced-motion: no-preference) {
    a.kpi-isi, .daftar-kanal a {
      transition: background-color var(--t-cepat) var(--ease);
    }
  }
}

@layer komponen {

  /* ── Tab layar (Paket di Transporter) ─────────────────────────────────────
   * Tautan biasa dengan aria-current, bukan tab JS: tiap tab route sendiri.
   * Garis bawah aksen meniru penanda aktif bar
   * atas supaya "yang sedang dibuka" punya satu tanda di seluruh aplikasi. */
  .tab-layar {
    display: flex; flex-wrap: wrap; gap: var(--s-1);
    margin: calc(var(--s-3) * -1) 0 var(--s-5);
    border-bottom: 1px solid var(--line);
  }
  .tab-layar a {
    display: inline-flex; align-items: center; min-height: 40px;
    padding: 0 var(--s-3); color: var(--muted);
    font-size: var(--fs-sm); font-weight: 600;
  }
  .tab-layar a:hover { color: var(--fg); text-decoration: none; }
  .tab-layar a[aria-current="page"] {
    color: var(--fg); box-shadow: inset 0 -2px 0 var(--accent);
  }

  /* ── Laporan saya ────────────────────────────────────────────────────────
   * Arah A "Ringkasan minggu" (bab "Laporan saya" di DESIGN.md). Berani di
   * SATU tempat: angka besar banding dan batang harinya, satu-satunya bidang
   * aksen di layar. Sisanya netral -- kartu, garis, --muted. Warna tenggat
   * hanya muncul di batang "masih di tangan Anda" dan memakai arti yang sama
   * dengan Beranda; Undel tidak pernah menyentuhnya. */
  .lap-kepala {
    display: flex; flex-wrap: wrap; align-items: flex-end;
    justify-content: space-between; gap: var(--s-3) var(--s-5);
  }
  .lap-kepala .kepala { margin-bottom: var(--s-3); min-width: 0; flex: 1 1 360px; }
  .lap-kepala .chip-baris { margin-bottom: var(--s-3); }
  .lap-privat {
    display: flex; align-items: flex-start; gap: 6px;
    margin: 0 0 var(--s-5); font-size: var(--fs-xs); color: var(--muted);
  }
  .lap-privat svg { width: 14px; height: 14px; flex: none; margin-top: 1px; }

  /* Kalimat pembuka: dibaca, bukan dipindai. Lebar dibatasi supaya tidak
   * menjadi satu baris panjang di layar lebar. */
  .lap-utama {
    max-width: 64ch; margin: 0 0 var(--s-2);
    font-size: var(--fs-lg); line-height: 1.5; color: var(--fg);
  }
  .lap-utama b { font-family: var(--display); font-weight: 700;
                 font-variant-numeric: tabular-nums; }
  .lap-sebagian { margin: 0; font-size: var(--fs-xs); color: var(--faint); }

  .lap-kisi { display: grid; gap: var(--s-5); align-items: start;
              margin-top: var(--s-5);
              grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
  .lap-kisi > .kartu { margin: 0; min-width: 0; }

  .lap-besar { display: flex; flex-wrap: wrap; align-items: baseline;
               gap: var(--s-1) var(--s-3); margin: 0 0 var(--s-4); }
  .lap-besar-n {
    font-family: var(--display); font-weight: 700; font-size: var(--fs-5xl);
    line-height: 1; letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums; color: var(--fg);
  }
  .lap-besar-ket { font-size: var(--fs-sm); color: var(--muted); }
  .lap-besar-ket b { color: var(--fg); }

  /* Batang per hari. Kolom flex yang rata bawah: label angka duduk tepat di
   * atas batangnya tanpa menghitung tinggi. `flex: none` wajib -- tanpa itu
   * batang hari tersibuk menyusut untuk memberi tempat labelnya dan skalanya
   * berhenti jujur. Luapannya naik ke padding atas wadah, bukan keluar. */
  .lap-hari {
    display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
    gap: var(--s-2); height: 136px; padding-top: 20px;
    border-bottom: 1px solid var(--line-kuat);
  }
  .lap-hari.rapat { gap: 2px; }
  .lap-hari-kolom {
    position: relative; display: flex; flex-direction: column;
    justify-content: flex-end; align-items: center; min-width: 0;
  }
  .lap-hari-kolom > * { flex: none; }
  .lap-hari-kolom i {
    width: min(100%, 36px);
    background: var(--accent); border-radius: var(--r-sm) var(--r-sm) 0 0;
  }
  .lap-hari.rapat .lap-hari-kolom i { width: 100%; border-radius: 1px 1px 0 0; }
  .lap-hari-kolom span {
    order: -1; margin-bottom: 2px; font-family: var(--mono);
    font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums;
  }
  /* Periode lalu: garis putus pada skala yang sama. <s> dipakai karena tidak
   * membawa isi; text-decoration-nya dimatikan. */
  .lap-hari-kolom s {
    position: absolute; left: 8%; right: 8%; height: 0;
    border-top: 2px dashed var(--muted); text-decoration: none;
  }
  .lap-hari.rapat .lap-hari-kolom s { left: 0; right: 0; border-top-width: 1px; }
  /* Hari yang belum datang: tanpa batang, tanpa nol. Titik di alas saja,
   * supaya minggu tetap terbaca tujuh hari. */
  .lap-hari-kolom.nanti::after {
    content: ""; width: 4px; height: 4px; margin-bottom: 4px;
    border-radius: 50%; background: var(--line-kuat);
  }
  .lap-sumbu {
    display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
    gap: var(--s-2); margin-top: var(--s-1);
    font-size: var(--fs-xs); color: var(--muted); text-align: center;
  }
  .lap-sumbu.rapat { gap: 2px; }
  .lap-sumbu span { white-space: nowrap; min-width: 0; }
  /* Label hari ini lebih lebar dari kolomnya: rata kanan lewat flex supaya
   * luapannya ke kiri, tetap di dalam kartu. */
  .lap-sumbu.rapat span:last-child { display: flex; justify-content: flex-end; }
  .lap-legenda {
    display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-4);
    margin: var(--s-3) 0 var(--s-4); font-size: var(--fs-xs); color: var(--muted);
  }
  .lap-legenda span { display: inline-flex; align-items: center; gap: 6px; }
  .lap-legenda i { width: 10px; height: 10px; border-radius: 2px;
                   background: var(--accent); }
  .lap-legenda s { width: 16px; border-top: 2px dashed var(--muted);
                   text-decoration: none; }

  .lap-baris { display: grid; margin: 0; border-top: 1px solid var(--line); }
  .lap-baris > div {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s-3); padding: var(--s-2) 0; border-bottom: 1px solid var(--line);
  }
  .lap-baris dt { margin: 0; font-size: var(--fs-sm); color: var(--muted); }
  .lap-baris dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }

  .lap-sub { margin: var(--s-5) 0 var(--s-2); font-size: var(--fs-sm);
             font-weight: 600; color: var(--fg); position: static;
    padding: 10px var(--s-3); }
  .lap-keadaan { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
  .lap-keadaan li {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s-3); padding: 4px 0; font-size: var(--fs-sm);
  }
  .lap-keadaan li > span { display: inline-flex; align-items: center; gap: var(--s-2);
                           color: var(--muted); }
  .lap-keadaan b { font-variant-numeric: tabular-nums; }
  .titik { display: inline-block; width: 8px; height: 8px; flex: none;
           border-radius: 50%; background: var(--st-belum); }
  .titik.diajukan  { background: var(--st-diajukan); }
  .titik.disetujui { background: var(--st-disetujui); }
  .titik.ditolak   { background: var(--st-ditolak); }

  .lap-daftar { margin-top: var(--s-3); }
  .lap-daftar summary {
    display: inline-flex; align-items: center; min-height: 32px;
    cursor: pointer; font-size: var(--fs-sm); font-weight: 600; color: var(--accent-teks);
  }
  .lap-daftar summary:hover { text-decoration: underline; }
  .lap-daftar .gulir {
    max-height: 420px; overflow: auto; margin-top: var(--s-2);
    border: 1px solid var(--line); border-radius: var(--r-md);
  }
  .lap-daftar .tabel { margin: 0; }
  .lap-kanal { display: block; margin-top: 2px; font-size: var(--fs-xs);
               color: var(--muted); text-transform: capitalize; }

  .lap-kecil { margin: var(--s-3) 0 0; font-size: var(--fs-xs); line-height: 1.5;
               color: var(--muted); max-width: 72ch; }

  /* Masih di tangan: warna yang sama dengan batang komposisi Beranda
   * (.pecah) untuk arti yang sama. */
  .lap-tangan { margin-top: var(--s-5); padding-top: var(--s-4);
                border-top: 1px solid var(--line); }
  .lap-tangan .lap-sub { margin-top: 0; }
  .lap-tangan-batang { display: flex; gap: 2px; height: 10px; margin: var(--s-2) 0; }
  .lap-tangan-batang span { min-width: 2px; border-radius: 2px; }
  .lap-tangan-batang .aman, .lap-kunci i.aman { background: var(--fg); }
  .lap-tangan-batang .mendesak, .lap-kunci i.mendesak { background: var(--sla-mendesak); }
  .lap-tangan-batang .lewat, .lap-kunci i.lewat { background: var(--sla-lewat); }
  .lap-tangan-batang .tanpa-tenggat, .lap-kunci i.tanpa-tenggat { background: var(--line-kuat); }
  .lap-kunci { display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-4);
               margin: 0; font-size: var(--fs-xs); color: var(--muted); }
  .lap-kunci span { display: inline-flex; align-items: center; gap: 6px; }
  .lap-kunci i { width: 10px; height: 10px; border-radius: 2px; }
  .lap-kunci b { color: var(--fg); font-variant-numeric: tabular-nums; }

  /* Umur Undel: panjang = jumlah order, tanpa garis akhir dan tanpa warna
   * tenggat -- jalur ini tidak punya tenggat yang diketahui. */
  .lap-umur { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
  .lap-umur li {
    display: grid; align-items: center; gap: var(--s-3);
    grid-template-columns: 84px minmax(0, 1fr) 4.5em;
    font-size: var(--fs-sm);
  }
  .lap-umur li > span { color: var(--muted); white-space: nowrap; }
  .lap-umur i { height: 10px; border-radius: 2px; background: var(--surface-2); }
  .lap-umur i::before {
    content: ""; display: block; height: 100%; min-width: 2px;
    width: var(--bagian, 0%); border-radius: inherit; background: var(--muted);
  }
  .lap-umur b { text-align: right; font-variant-numeric: tabular-nums; }

  .lap-tercatat {
    margin-top: var(--s-5); padding: var(--s-3) var(--s-4);
    background: var(--surface-2); border-left: 2px solid var(--line-kuat);
    border-radius: 0 var(--r-md) var(--r-md) 0; font-size: var(--fs-sm);
  }
  .lap-tercatat p { margin: 0; }
  .lap-tercatat p + p { margin-top: var(--s-2); font-size: var(--fs-xs); }
  .lap-tercatat-n { font-family: var(--display); font-size: var(--fs-2xl);
                    font-weight: 700; margin-right: 6px;
                    font-variant-numeric: tabular-nums; }

  .lap-kisi + .catatan { margin-top: var(--s-5); }

  @media (max-width: 1099.98px) {
    .lap-kisi { grid-template-columns: minmax(0, 1fr); }
  }
  @media (max-width: 479.98px) {
    .lap-besar-n { font-size: var(--fs-4xl); }
    .lap-hari, .lap-sumbu { gap: var(--s-1); }
    .lap-umur li { grid-template-columns: 72px minmax(0, 1fr) 4em; gap: var(--s-2); }
  }

  @media (prefers-reduced-motion: no-preference) {
    .tab-layar a { transition: color var(--t-cepat) var(--ease); }
  }
}

/* ══ Dashboard (2026-09-25) ═════════════════════════════════════════════
 * Satu pita angka, satu pita kualitas data, dua kisi. Seluruh warna dari
 * token yang sudah ada: --accent untuk kolom grafik (kuantitas), --grafik-2
 * untuk seri kedua (batang brand, bulan terpilih), --st-diajukan hanya untuk
 * "perlu diperiksa"/"terlambat" -- keadaan yang memang menuntut orang
 * melihat. Tidak ada warna yang berarti identitas brand.
 */
@layer komponen {
  .dsb-alat { display: flex; align-items: center; justify-content: space-between;
              gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-3); }
  .dsb-alat p { margin: 0; }
  .dsb > * + * { margin-top: var(--s-4); }

  /* ── Dashboard gabungan (2026-09-30) ───────────────────────────────────
   * Alat + indeks bagian satu baris, menempel di bawah bar: Segarkan paling
   * dibutuhkan saat orang sudah jauh di bawah. Indeks = tautan biasa; yang
   * sedang dibaca ditandai ui.js lewat aria-current. Penandanya bidang
   * --surface-2, BUKAN garis bawah aksen: garis bawah sudah dipakai
   * .tab-layar tepat di atasnya, dan dua baris bergaris-bawah terbaca
   * sebagai dua tingkat tab. */
  .dsb-alat-lengket {
    position: sticky; top: var(--bar-h); z-index: 10;
    padding: var(--s-1) 0; background: var(--bg);
    border-bottom: 1px solid var(--line);
  }
  .dsb-indeks { display: flex; flex-wrap: wrap; gap: 0 var(--s-1); }
  .dsb-indeks a {
    display: inline-flex; align-items: center; min-height: 36px;
    padding: 0 var(--s-3); border-radius: var(--r-sm);
    color: var(--muted); font-size: var(--fs-sm); font-weight: 600;
  }
  .dsb-indeks a:hover { color: var(--fg); text-decoration: none; }
  .dsb-indeks a[aria-current] { color: var(--fg); background: var(--surface-2);
                                box-shadow: inset 0 0 0 1px var(--line); }

  /* Kepala bagian: pemisah BAB, bukan kartu. Judul 20px -- jelas di atas
   * judul kartu (15px) -- lalu chip cakupan. Dua cakupan hidup di satu
   * halaman, jadi setiap bab menyebut cakupannya dengan kata yang sama. */
  .dsb-bagian { scroll-margin-top: calc(var(--bar-h) + 56px); }
  .dsb > .dsb-bagian:not(:first-child) { margin-top: var(--s-6); }
  .dsb-bagian > * + * { margin-top: var(--s-4); }
  .dsb-bagian-kepala {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-3);
    padding-top: var(--s-4); border-top: 1px solid var(--line-kuat);
  }
  .dsb > .dsb-bagian-kepala:first-child { padding-top: 0; border-top: 0; }
  .dsb-bagian-judul { margin: 0; font-size: var(--fs-xl); font-weight: 700;
                      letter-spacing: -0.01em; }
  /* Teks biasa (2026-10-15). Dulu chip bergaris berlatar --surface-2 --
   * di samping tombol gaya Stripe ia terbaca seperti tombol yang bisa diklik. */
  .dsb-cakupan { font-size: var(--fs-xs); font-weight: 500; color: var(--muted); }
  /* flex-basis 100% TANPA max-width: batas lebar ikut mengecilkan ukuran
   * hipotetis butir flex, dan kalimatnya lalu muat di samping chip. */
  .dsb-bagian-sub { flex: 0 0 100%; margin: 0; text-wrap: pretty;
                    font-size: var(--fs-sm); color: var(--muted); }
  .dsb-bagian-sub b { color: var(--fg); font-weight: 600; }

  /* Kartu angka yang punya baris di baliknya = tautan (ui.stat href=).
   * HANYA kartu bertautan yang bereaksi pada hover -- lihat aturan 1 di
   * lapisan interaksi. Petunjuk "Buka ..." selalu terlihat: di layar sentuh
   * tidak ada hover yang bisa mengungkapkannya. */
  a.stat-tautan { display: block; color: inherit; text-decoration: none; }
  .dsb-kpi > a.stat-tautan:hover { background: var(--surface-2); }
  .dsb-kpi > a.stat-tautan:focus-visible { outline-offset: -2px; }
  .stat-buka { display: flex; align-items: center; gap: 4px; margin-top: var(--s-2);
               font-size: var(--fs-xs); font-weight: 600; color: var(--accent-teks); }
  .stat-buka svg { width: 12px; height: 12px; }
  a.stat-tautan:hover .stat-buka { text-decoration: underline; }
  /* Di pita, bingkai kiri kartu dilepas; "sudah lewat" ditandai garis atas
   * merah -- warna yang sama dengan lencana lewat, satu-satunya status
   * bermerah di aplikasi ini. */
  .dsb-kpi > .stat.buruk { box-shadow: inset 0 2px 0 var(--st-ditolak); }

  /* Butir legenda corong dan sel tabel brand yang bisa disaring di Papan
   * Kerja. Warna tetap warna teks: tabel dengan tiga puluh angka oranye
   * berteriak; yang memberi tahu "bisa diklik" adalah hover dan fokus. */
  .corong-butir { display: block; padding: 6px; margin: -6px; border-radius: var(--r-sm);
                  color: inherit; }
  a.corong-butir:hover { background: var(--surface-2); text-decoration: none; }
  a.corong-butir:hover .corong-nilai { text-decoration: underline; }
  .corong-butir.nol .corong-kunci, .corong-butir.nol .corong-nilai { color: var(--faint); }
  .sel-tautan { color: inherit; }
  td > .sel-tautan { display: block; }
  .sel-tautan:hover { color: var(--accent-teks); text-decoration: underline; }
  .batang-nama.sel-tautan { color: var(--muted); }
  .batang-nama.sel-tautan:hover { color: var(--fg); }
  .tabel-kaki { margin: 0; padding: var(--s-2) var(--s-4); border-top: 1px solid var(--line);
                font-size: var(--fs-xs); color: var(--muted); }
  /* "aman" = sisa, bukan sorotan: netral seperti di Beban tiap PIC. Dulu
   * jatuh ke --accent bawaan .batang-isi, sehingga batang yang paling
   * tidak mendesak justru yang paling berwarna. */
  .batang-isi.aman { background: var(--muted); }
  /* Di bawah ~768px alat + indeks terlipat dua baris (~90px). Menempelkan
   * itu memakan seperlima layar selamanya; indeks tetap berguna sebagai
   * daftar lompat di atas halaman. */
  @media (max-width: 767.98px) {
    .dsb-alat-lengket { position: static; }
    .dsb-bagian { scroll-margin-top: calc(var(--bar-h) + var(--s-2)); }
  }


  /* ── Pita angka: .stat tanpa bingkainya sendiri ─────────────────────── */
  .dsb-kpi { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
             background: var(--surface); border: 1px solid var(--line);
             border-radius: var(--r-md); }
  .dsb-kpi > .stat { border: 0; border-radius: 0; background: transparent;
                     min-width: 0; padding: var(--s-3) var(--s-4) var(--s-4); }
  .dsb-kpi > .stat + .stat { border-left: 1px solid var(--line); }
  .dsb-kpi .stat-label { display: flex; align-items: center; gap: var(--s-2);
                         min-height: 20px; }
  .dsb-kpi .stat-nilai { font-size: var(--fs-2xl); white-space: nowrap;
                         overflow-wrap: normal; letter-spacing: -0.01em; }
  .dsb-kpi .stat-nilai.kecil { font-size: var(--fs-lg); padding-block: 5px; letter-spacing: 0; }
  .dsb-kpi .stat-syarat b { color: var(--fg); font-weight: 600;
                            font-variant-numeric: tabular-nums; }
  /* Penanda "berubah" di pita: kata di samping label, bukan garis kiri
   * kartu (.stat[data-berubah]) -- di pita, garis itu jatuh tepat di atas
   * pemisah sel dan terbaca sebagai pemisah yang tebal. */
  .dsb-kpi > .stat[data-berubah] { border-left: 0; }
  .dsb-kpi > .stat + .stat[data-berubah] { border-left: 1px solid var(--line); }
  .dsb-kpi > .stat[data-berubah] .stat-label::after {
    content: "berubah"; text-transform: none; letter-spacing: 0;
    font-weight: 500; color: var(--accent-teks);
  }

  .dsb-tanda { display: inline-flex; align-items: center; height: 18px;
               padding: 0 6px; border-radius: var(--r-sm); border: 1px solid currentColor;
               font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0;
               text-transform: none; white-space: nowrap; }
  .dsb-tanda.perhatian { color: var(--st-diajukan); }

  /* Tarik sekarang, di bawah kalimat jadwal kartu "Terakhir diperbarui".
   * Garis tipis memisahkan fakta (kapan data ditarik) dari tindakan
   * (tarik lagi sekarang). */
  .dsb-tarik { display: grid; justify-items: start; gap: var(--s-1);
               margin-top: var(--s-3); padding-top: var(--s-3);
               border-top: 1px solid var(--line); }
  .dsb-tarik form { margin: 0; }
  .dsb-tarik-catatan { color: var(--st-ditolak); }


  /* ── Lebar ───────────────────────────────────────────────────────────── */
  @media (max-width: 999.98px) {
    .dsb-kpi { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .dsb-kpi > .stat:nth-child(3), .dsb-kpi > .stat:nth-child(3)[data-berubah] { border-left: 0; }
    .dsb-kpi > .stat:nth-child(n+3) { border-top: 1px solid var(--line); }
  }
  @media (max-width: 599.98px) {
    .dsb-kpi { grid-template-columns: minmax(0, 1fr); }
    .dsb-kpi > .stat + .stat, .dsb-kpi > .stat + .stat[data-berubah] {
      border-left: 0; border-top: 1px solid var(--line); }
    .dsb-kpi .stat-nilai { white-space: normal; overflow-wrap: anywhere; }
    .dsb-indeks a { padding-inline: var(--s-2); }
  }

}

/* ── Job (/job, admin) ─────────────────────────────────────────────────────
 * Status job BUKAN status banding, jadi tidak memakai bentuk lencana:
 * teks + ikon saja. Merah = gagal (kegagalan sudah terjadi), zaitun =
 * terlambat atau jadwal mati (periksa segera), selain itu tanpa warna.
 */
@layer komponen {
  .job .tabel tbody th {
    position: static; z-index: auto; background: transparent;
    font-size: inherit; font-weight: 400; letter-spacing: 0;
    text-transform: none; color: var(--fg); white-space: normal;
    padding: 10px var(--s-3); vertical-align: top;
    border-bottom: 1px solid color-mix(in oklch, var(--line) 60%, transparent);
  }
  .job .tabel tbody tr:last-child th { border-bottom: 0; }
  .job .tabel tbody tr:hover th { background: var(--surface-2); }
  .job .tabel .tebal { font-weight: 600; }

  .job-nama { display: grid; gap: 2px; min-width: 16rem; max-width: 30rem; }
  .job-nama code { font-family: var(--mono); }
  .job-perlu > th:first-child { box-shadow: inset 2px 0 0 var(--sla-mendesak); }
  .job-perlu.buruk > th:first-child { box-shadow: inset 2px 0 0 var(--sla-lewat); }
  .job-waktu .kecil { display: block; }
  .job-jadwal { min-width: 8rem; }

  .job-keadaan { display: grid; gap: 2px; justify-items: start; min-width: 12rem; }
  .job-st { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); }
  .job-st svg { width: 14px; height: 14px; flex: none; }
  .job-st.jalan { color: var(--fg); font-weight: 600; }
  .job-st.gagal { color: var(--sla-lewat); font-weight: 600; }
  .job-st.telat { color: var(--sla-mendesak); font-weight: 600; font-size: var(--fs-sm); }
  .job-titik { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }

  .job-perintah { display: inline-block; min-width: 16rem; font-family: var(--mono);
                  font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
  .job-galat { min-width: 20rem; }
  .job-galat code { display: block; font-family: var(--mono); font-size: var(--fs-xs);
                    color: var(--muted); white-space: pre-wrap; overflow-wrap: anywhere;
                    max-height: 7.5em; overflow-y: auto; }

  /* Di ponsel deskripsi dan nama unit disembunyikan: tanpa itu kolom Job
   * memakan seluruh layar dan Keadaan -- alasan halaman ini dibuka --
   * terdorong ke luar layar. */
  @media (max-width: 599.98px) {
    .job-nama { min-width: 8rem; }
    .job-rinci { display: none; }
    .job-keadaan { min-width: 10rem; }
  }
}

/* ── /penugasan: pindah banyak + akses toko (2026-10-07) ──────────────────
 * Hasil pindah memakai .catatan.baik, bukan .buruk: ui.js membaca
 * .catatan.buruk[role=alert] sebagai galat dan menolak menukar wilayahnya.
 */
@layer komponen {
  .catatan.baik { border-left-color: var(--st-disetujui); }
  .catatan p + p, .catatan p + ul { margin-top: var(--s-2); }

  .pindah { display: grid; gap: var(--s-4); }
  /* Bukan .baris: itu di @layer utilitas dan akan menang atas display:none
   * penyembunyi bagian di bawah. */
  .pindah-baris { display: flex; align-items: flex-end; gap: var(--s-3); flex-wrap: wrap; }
  .pindah .medan { flex: 1 1 200px; min-width: 0; }
  .pindah .pindah-alasan { flex: 2 1 280px; }
  .pindah-sumber { display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5);
                   border: 0; padding: 0; margin: 0; }
  .pindah-sumber legend { font-size: var(--fs-xs); font-weight: 500; color: var(--muted);
                          margin-bottom: var(--s-2); padding: 0; }
  .pindah-sumber label { display: inline-flex; align-items: center; gap: var(--s-2);
                         min-height: 32px; cursor: pointer; }
  .pindah-catatan { flex-basis: 100%; margin: 0; }
  /* Bagian yang tidak dipilih disembunyikan. Browser tanpa :has() menampilkan
   * keduanya -- server hanya membaca bagian milik `sumber` yang dipilih. */
  .pindah:has(input[name="sumber"][value="tempel"]:checked) .bagian-pic,
  .pindah:has(input[name="sumber"][value="pic"]:checked) .bagian-tempel { display: none; }

  .pindah-lewat { margin: var(--s-1) 0 0; padding-left: 1.1em; }
  .pindah-lewat summary { cursor: pointer; color: var(--fg); }
  .pindah-nomor { margin-top: var(--s-1); overflow-wrap: anywhere; color: var(--muted); }

  .akses-bantu { margin: 0 0 var(--s-4); max-width: 70ch; }
  .akses { border-top: 1px solid var(--line); }
  .akses:last-of-type { border-bottom: 1px solid var(--line); }
  .akses > summary { display: flex; align-items: center; gap: var(--s-3);
                     min-height: 44px; padding: var(--s-2) 0; cursor: pointer; }
  .akses[open] > summary { margin-bottom: var(--s-2); }
  .akses form { display: grid; gap: var(--s-3); justify-items: start;
                padding-bottom: var(--s-4); }
  .akses .gulir { max-width: 100%; }
  .akses-tabel { width: auto; }
  .akses-tabel td, .akses-tabel thead th:not(:first-child) { text-align: center; }
  .akses-kotak { display: inline-flex; align-items: center; justify-content: center;
                 min-width: 44px; min-height: 32px; cursor: pointer; }
  .akses-kotak input { width: 16px; height: 16px; accent-color: var(--accent); }

  /* ── Paket batal (2026-10-07) ─────────────────────────────────────────
   * Filter kurir + status, kartu jumlah per status (juga tautan saringan),
   * lalu daftar. Warna hanya untuk "Perlu dikejar"; kartu yang sedang
   * menyaring ditandai garis aksen yang sama dengan tab aktif. */
  .pb-saring .medan:first-of-type { min-width: min(320px, 100%); }
  .pb-stat-kisi { margin-bottom: var(--s-5); }
  .pb-stat { display: flex; flex-direction: column; color: inherit;
             text-decoration: none; transition: border-color var(--t-cepat) var(--ease); }
  .pb-stat:hover { border-color: var(--line-kuat); }
  .pb-stat[aria-current] { border-color: var(--accent);
                           box-shadow: inset 0 -2px 0 var(--accent); }
  .pb-stat.kejar .stat-nilai { color: var(--sla-lewat); }
  .pb-kepala { justify-content: space-between; align-items: flex-end; }
  .pb-kepala h2 { margin-bottom: 2px; }
  .pb-kode { font-family: var(--mono); font-size: var(--fs-sm); }
  .pb-st.kejar { color: var(--sla-lewat); font-weight: 600; }
  .pb-alasan { min-width: 20ch; max-width: 36ch; }
}

/* ── Target hari ini (2026-10-07) ─────────────────────────────────────────
 * Strip di atas Papan Kerja (ops) dan kartu di /penugasan (leader). Satu
 * baris ringkas, bukan empat kartu angka: ini pengingat di atas tempat
 * kerja, bukan dashboard -- tabel antrean tetap yang paling besar di layar.
 * Satu-satunya yang diberi bobot adalah batang progres banding. */
@layer komponen {
  .strip-target {
    display: flex; flex-wrap: wrap; align-items: stretch;
    gap: 0; margin: 0 0 var(--s-4);
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-md);
  }
  .strip-sel {
    display: flex; align-items: baseline; flex-wrap: wrap;
    gap: var(--s-1) var(--s-2); padding: var(--s-2) var(--s-4);
    min-height: 44px; align-content: center;
    border-right: 1px solid var(--line); color: var(--fg);
  }
  .strip-sel:last-child { border-right: 0; }
  a.strip-sel { text-decoration: none; }
  a.strip-sel:hover { background: var(--surface-2); }
  a.strip-sel:hover .strip-label { text-decoration: underline; }
  .strip-label { font-size: var(--fs-sm); color: var(--muted); }
  .strip-nilai { font-family: var(--mono); font-variant-numeric: tabular-nums;
                 font-weight: 600; font-size: var(--fs-md); }
  .strip-utama { flex: 1 1 16rem; }
  .strip-utama .progres { flex: 1 1 8rem; max-width: 18rem; align-self: center; }

  .progres { display: inline-flex; align-items: center; gap: var(--s-2);
             min-width: 7rem; }
  .progres-batang { flex: 1 1 auto; height: 6px; min-width: 3rem;
                    background: var(--surface-2); border-radius: 3px;
                    overflow: hidden; }
  .progres-batang > span { display: block; height: 100%;
                           background: var(--accent); border-radius: 3px; }
  .progres.penuh .progres-batang > span { background: var(--st-disetujui); }
  .progres-angka { font-family: var(--mono); font-variant-numeric: tabular-nums;
                   font-size: var(--fs-sm); color: var(--muted); min-width: 3.5ch;
                   text-align: right; }

  .target-samakan { flex-wrap: wrap; gap: var(--s-2) var(--s-3);
                    padding: var(--s-3) var(--s-4);
                    border-bottom: 1px solid var(--line); }
  .target-samakan label { font-size: var(--fs-base); color: var(--muted); }
  .target-isi { inline-size: 7rem; }
  .tabel select.input.kecil { inline-size: auto; }
  .target-total { font-family: var(--mono); font-variant-numeric: tabular-nums; }
  .target-kaki { flex-wrap: wrap; gap: var(--s-2) var(--s-3); align-items: center;
                 padding: var(--s-3) var(--s-4); border-top: 1px solid var(--line); }
  .target-kaki .faint { max-width: 80ch; }

  /* HP: progres banding satu baris penuh di atas (ia yang dikejar), tiga
   * angka lain sejajar di bawahnya, label di atas angka. */
  @media (max-width: 640px) {
    .strip-target { display: grid; grid-template-columns: repeat(3, 1fr); }
    .strip-sel { flex-direction: column; align-items: flex-start;
                 justify-content: center; padding: var(--s-2) var(--s-3); }
    .strip-utama { grid-column: 1 / -1; order: -1; flex-direction: row;
                   align-items: center; border-right: 0;
                   border-bottom: 1px solid var(--line); }
    .strip-sel:last-child { border-right: 0; }
  }
}

/* ── Banding transporter (2026-10-16) ─────────────────────────────────────
 * Memakai kelas Paket batal (pb-*). Di sini hanya kolom centang dan baris
 * tombol salin: kotak 44px supaya bisa dicentang di HP. */
@layer komponen {
  .bt-pilih { inline-size: 44px; padding-inline: var(--s-2); text-align: center; }
  .bt-pilih label { display: inline-flex; align-items: center; justify-content: center;
                    min-width: 32px; min-height: 32px; cursor: pointer; }
  .bt-pilih input { width: 16px; height: 16px; accent-color: var(--accent); }
  .bt-tindak { flex-wrap: wrap; gap: var(--s-2); justify-content: flex-end; }
  .bt-catatan { min-width: 16ch; max-width: 32ch; white-space: pre-line; }
}

/* ── Log aktivitas (2026-10-07) ───────────────────────────────────────────
 * Memakai kelas ringkasan & daftar Paket batal (pb-*): pola yang sama,
 * ringkasan per orang yang angkanya menyaring daftar. Di sini hanya yang
 * berbeda. */
@layer komponen {
  .log-bantu { align-self: center; }
  .log-ringkas tbody th a { color: var(--fg); }
  .log-ringkas tbody th a[aria-current] { box-shadow: inset 0 -2px 0 var(--accent); }
  .log-daftar td { vertical-align: top; }
}

/* ── Kondisi barang per brand (2026-10-07) ────────────────────────────────
 * Sel selain Bagus diwarnai menurut porsinya di baris brand (--p, 0..1):
 * 1.385 order berat di brand 13 ribu order dan di brand 2 ribu order bukan
 * keadaan yang sama. Puncaknya 35%, cukup untuk dipindai tanpa menenggelamkan
 * angkanya. Satu-satunya warna di tabel ini. */
@layer komponen {
  .kondisi-matriks td.panas {
    background: color-mix(in oklch, var(--st-ditolak) calc(var(--p) * 35%), transparent);
  }
  .kondisi-matriks tbody tr:hover td.panas {
    background: color-mix(in oklch, var(--st-ditolak) calc(var(--p) * 35%), var(--surface-2));
  }
  /* Nama brand = th baris (untuk pembaca layar), tapi dibaca sebagai data:
   * bukan gaya judul kolom .tabel th yang kecil, kapital, dan pudar. */
  .kondisi-matriks tbody th, .kondisi-matriks tfoot th {
    font-size: inherit; font-weight: 600; letter-spacing: normal;
    text-transform: none; color: var(--fg); padding: 10px var(--s-3);
  }
  .kondisi-matriks tbody th {
    border-bottom: 1px solid color-mix(in oklch, var(--line) 60%, transparent);
  }
  .kondisi-matriks tfoot th, .kondisi-matriks tfoot td {
    border-top: 1px solid var(--line-kuat); border-bottom: 0;
    padding: 10px var(--s-3);
  }
  .kondisi-matriks tfoot th { position: sticky; left: 0;
                              background: var(--surface); z-index: 2;
                              border-right: 1px solid var(--line); }
  .kondisi-kode { margin-top: var(--s-4); max-width: 80ch; }
  .kondisi-kode summary { cursor: pointer; color: var(--muted); }
  .kondisi-kode p { margin: var(--s-3) 0; }
}

/* ── Laporan harian leader ops di Dashboard (2026-10-08) ──────────────────
 * Tabelnya meniru sheet leader (Return Value, Daily Banding, Point
 * Activity) supaya bisa dicocokkan sel demi sel; satu-satunya warna jenuh
 * adalah lencana grading, memakai token status yang sama dengan lencana
 * banding (hijau/kuning/merah). Kepala tabel Daily Banding dan Point
 * Activity memakai --bar-bg seperti sheet leader -- bidang gelap yang sama
 * dengan bar atas, bukan warna baru. */
@layer komponen {
  .lap-segar { display: flex; flex-wrap: wrap; align-items: flex-start;
               gap: var(--s-3) var(--s-5); padding: var(--s-3) var(--s-4);
               background: var(--surface); border: 1px solid var(--line);
               border-radius: var(--r-md); }
  .lap-segar > .stat { border: 0; padding: 0; background: transparent; }
  .lap-segar .dsb-tarik { margin-top: 0; padding-top: 0; border-top: 0; }

  .lap-kartu { background: var(--surface); border: 1px solid var(--line);
               border-radius: var(--r-md); min-width: 0; }
  .dsb-bagian > .lap-kartu + .lap-kartu { margin-top: var(--s-3); }
  .lap-kartu-kepala { display: flex; justify-content: space-between; align-items: baseline;
                      gap: var(--s-3); flex-wrap: wrap; padding: var(--s-2) var(--s-4);
                      border-bottom: 1px solid var(--line); background: var(--surface-2);
                      border-radius: var(--r-md) var(--r-md) 0 0; }
  .lap-kartu-kepala h3 { margin: 0; font-family: var(--display); font-size: var(--fs-md); }
  .lap-kartu-kepala span { font-size: var(--fs-xs); color: var(--muted); }

  .lap-rv, .lap-db, .lap-pa, .lap-naik { border-collapse: collapse;
                                         font-variant-numeric: tabular-nums; }
  .lap-rv { width: 100%; font-size: var(--fs-xs); }
  .lap-rv th, .lap-rv td { padding: 5px var(--s-2); text-align: right; white-space: nowrap;
                           border-bottom: 1px solid var(--line); border-right: 1px solid var(--line); }
  .lap-rv thead th { background: var(--surface-2); color: var(--muted); font-weight: 600;
                     text-align: center; }
  .lap-rv thead tr:first-child th { color: var(--fg); }
  /* Kolom brand menempel saat tabel digeser: dua belas bulan x dua kanal
   * tidak muat di layar mana pun, dan angka tanpa nama brand tidak berarti. */
  .lap-rv tbody th, .lap-rv tfoot th, .lap-rv .lap-brand {
    position: sticky; left: 0; z-index: 1; text-align: left; min-width: 160px;
    background: var(--surface); }
  .lap-rv thead .lap-brand { background: var(--surface-2); }
  .lap-rv tbody th { font-weight: 600; }
  .lap-rv td.abu { background: var(--surface-2);
                   background-image: repeating-linear-gradient(135deg, transparent 0 6px,
                     color-mix(in oklch, var(--line) 70%, transparent) 6px 7px); }
  .lap-rv tfoot th, .lap-rv tfoot td { font-weight: 700; border-top: 2px solid var(--line-kuat);
                                       background: var(--surface-2); }
  .lap-rv .tebal { font-weight: 600; }

  .lap-naik-wadah { padding: var(--s-2) var(--s-4) var(--s-3); }
  .lap-naik { font-size: var(--fs-sm); }
  .lap-naik th, .lap-naik td { padding: 3px var(--s-4) 3px 0; text-align: right; white-space: nowrap; }
  .lap-naik thead th { font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }
  .lap-naik tbody th { text-align: left; font-weight: 500; min-width: 240px; }
  .lap-naik .lap-naik-angka td { font-weight: 600; color: var(--fg); }
  .lap-naik .tebal { font-weight: 700; }

  .lap-tanggal { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin: 0; }
  .lap-tanggal label { font-size: var(--fs-sm); color: var(--muted); }
  .lap-tanggal .input { inline-size: auto; }

  .lap-dua { display: grid; gap: var(--s-4); align-items: start;
             grid-template-columns: minmax(0, 680px) minmax(0, 1fr); }
  .lap-catatan { display: grid; gap: var(--s-2); font-size: var(--fs-sm); color: var(--muted);
                 max-width: 60ch; }
  .lap-catatan p { margin: 0; }
  .lap-catatan b { color: var(--fg); font-weight: 600; }

  .lap-db { width: 100%; font-size: var(--fs-sm); }
  .lap-db th, .lap-db td { padding: 4px var(--s-3); text-align: right; white-space: nowrap; }
  .lap-db thead th, .lap-pa thead th { background: var(--bar-bg); color: var(--bar-fg);
                                       font-weight: 600; }
  .lap-db thead th:first-child { text-align: left; border-radius: var(--r-md) 0 0 0; }
  .lap-db thead th:last-child { border-radius: 0 var(--r-md) 0 0; }
  .lap-db tbody th { text-align: left; font-weight: 400; padding-left: var(--s-5); }
  .lap-db .lap-db-brand th { font-weight: 700; padding: var(--s-3) var(--s-3) 3px;
                             border-bottom: 1px solid var(--accent); }
  .lap-db .lap-db-grand th { background: var(--bar-bg); color: var(--bar-fg); border-bottom: 0; }
  .lap-db .lap-db-total th, .lap-db .lap-db-total td { font-weight: 700;
                                                       border-top: 1px solid var(--line); }

  .lap-pa { width: 100%; font-size: var(--fs-sm); }
  .lap-pa th, .lap-pa td { padding: 6px var(--s-3); text-align: right; white-space: nowrap;
                           border-bottom: 1px solid var(--line); }
  .lap-pa thead th { text-align: center; white-space: normal; line-height: 1.25;
                     vertical-align: bottom; }
  .lap-pa thead th:first-child { border-radius: var(--r-md) 0 0 0; }
  .lap-pa thead th:last-child { border-radius: 0 var(--r-md) 0 0; }
  .lap-pa tbody th, .lap-pa tfoot th { text-align: left; font-weight: 600; }
  .lap-pa .kiri { text-align: left; }
  .lap-pa .tengah { text-align: center; }
  .lap-pa .tebal { font-weight: 600; }
  .lap-pa .lap-poin { display: block; font-weight: 400; font-size: var(--fs-xs); color: var(--bar-muted); }
  .lap-pa .lap-target { display: block; font-size: var(--fs-xs); }
  .lap-pa tr.lap-pa-sela > * { border-top: 2px solid var(--line-kuat); }
  .lap-pa tfoot th, .lap-pa tfoot td { font-weight: 700; background: var(--surface-2);
                                       border-top: 2px solid var(--line-kuat); border-bottom: 0; }

  .lap-grade { display: inline-block; min-width: 64px; text-align: center;
               font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.03em;
               padding: 1px 6px; border-radius: var(--r-sm); border: 1px solid currentColor; }
  .lap-grade.high   { color: var(--st-disetujui); }
  .lap-grade.medium { color: var(--st-diajukan); }
  .lap-grade.low    { color: var(--st-ditolak); }

  .lap-legenda { display: grid; grid-template-columns: repeat(2, minmax(0, 320px)); gap: var(--s-5);
                 padding: var(--s-4); border-top: 1px solid var(--line); font-size: var(--fs-sm); }
  .lap-legenda table { width: 100%; border-collapse: collapse; }
  .lap-legenda th, .lap-legenda td { padding: 3px 0; text-align: left;
                                     border-bottom: 1px solid var(--line); }
  .lap-legenda td:last-child, .lap-legenda th:last-child { text-align: right; }
  .lap-legenda thead th { color: var(--muted); font-weight: 600; }

  @media (max-width: 899.98px) {
    .lap-dua, .lap-legenda { grid-template-columns: minmax(0, 1fr); }
  }
}

/* ── Dashboard ala gambar (2026-10-09) ──────────────────────────────────
 * Satu kisi padat menggantikan enam bab bergulir. Kartu KPI BERWARNA atas
 * keputusan Juan -- pengecualian sadar atas "warna jenuh hanya untuk
 * status" (DESIGN.md "Dashboard ala gambar"). Warnanya cuma di bidang tipis
 * kartu dan lingkaran ikon; angkanya tetap --fg supaya kontras tidak
 * bergantung pada warna. Shopee = --accent, TikTok = --grafik-2, sama
 * dengan grafik lama. */
@layer komponen {
  :root { --kpi-oranye: #FF6B35; --kpi-hijau: #7fd49a; --kpi-merah: #ff8a80;
          --kpi-kuning: #f0c060; --kpi-ikon-fg: #0B1220; }
  :root[data-tema="terang"] { --kpi-oranye: #E8521A; --kpi-hijau: #1e8a4a;
          --kpi-merah: #c62828; --kpi-kuning: #b58500; --kpi-ikon-fg: #ffffff; }

  /* Jarak bawah = .kepala biasa. Tanpa kalimat deskripsi (9 Okt) margin 0
     membuat isi menempel ke judul. */
  .dsb-kepala { display: flex; justify-content: space-between; align-items: flex-end;
                gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-5); }
  .dsb-kepala .kepala { margin-bottom: 0; }
  .dsb2 { display: grid; gap: var(--s-4); margin-top: var(--s-4); }
  .dsb2 .kartu + .kartu { margin-top: 0; }

  .dsb-status { display: flex; flex-wrap: wrap; align-items: center;
                gap: var(--s-2) var(--s-5); padding: var(--s-2) var(--s-4);
                background: var(--surface); border: 1px solid var(--line);
                border-radius: var(--r-md); font-size: var(--fs-sm); color: var(--muted); }
  .dsb-status .angka { color: var(--fg); font-weight: 600; }
  .dsb-status-keadaan { display: inline-flex; align-items: center; gap: var(--s-2);
                        color: var(--fg); font-weight: 600; }
  .dsb-status-titik { width: 8px; height: 8px; border-radius: 50%;
                      background: var(--st-disetujui); }
  .dsb-status.telat .dsb-status-titik { background: var(--sla-mendesak); }
  .dsb-status-jadwal { flex: 1 1 280px; }
  /* Saringan Jalur di sebelah tombol unduh popup Return Value: tinggi dan
   * huruf .btn.kecil, lebar seisinya (bukan 100% .input). */
  .rv-unduh { gap: var(--s-2); align-items: center; flex-wrap: wrap; }
  .rv-unduh select.input { width: auto; }
  .dsb-status .dsb-tarik { margin: 0; padding: 0; border: 0; justify-items: end;
                           margin-left: auto; text-align: right; }
  .dsb-tarik-pesan { margin: 0; font-size: var(--fs-xs); }

  .dsb2 .kpi-baris { display: grid; gap: var(--s-3); grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .dsb2 .kpi { --k: var(--kpi-oranye); display: flex; gap: var(--s-3); align-items: center;
         padding: var(--s-4); border-radius: var(--r-lg); color: var(--fg);
         background: color-mix(in srgb, var(--k) 9%, var(--surface));
         border: 1px solid color-mix(in srgb, var(--k) 35%, var(--line)); }
  .dsb2 .kpi.hijau { --k: var(--kpi-hijau); }
  .dsb2 .kpi.merah { --k: var(--kpi-merah); }
  .dsb2 .kpi.kuning { --k: var(--kpi-kuning); }
  .dsb2 a.kpi { text-decoration: none; transition: border-color var(--t-cepat); }
  .dsb2 a.kpi:hover { border-color: var(--k); text-decoration: none; }
  .dsb2 .kpi-ikon { flex: none; display: grid; place-items: center; width: 40px; height: 40px;
              border-radius: 50%; background: var(--k); color: var(--kpi-ikon-fg); }
  .dsb2 .kpi-ikon svg { width: 20px; height: 20px; }
  .dsb2 .kpi-isi { display: grid; gap: 2px; min-width: 0; }
  .dsb2 .kpi-label { font-size: var(--fs-sm); font-weight: 600; color: var(--muted); }
  .dsb2 .kpi-nilai { font-family: var(--display); font-weight: 700; font-size: var(--fs-2xl);
               line-height: 1.15; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
  .dsb2 .kpi-nilai .satuan { font-family: var(--sans); font-size: var(--fs-base); font-weight: 500;
                       color: var(--muted); }
  .dsb2 .kpi-syarat { font-size: var(--fs-xs); color: var(--muted); }
  .dsb2 .kpi-buka { display: inline-flex; align-items: center; gap: 2px; margin-top: var(--s-1);
              font-size: var(--fs-xs); font-weight: 600; color: var(--accent-teks); }
  .dsb2 .kpi-buka svg { width: 14px; height: 14px; }
  .dsb-cakupan-baris { margin: calc(var(--s-2) * -1) 0 0; font-size: var(--fs-xs); color: var(--faint); }
  /* Rincian kartu peluang/hangus: tiap baris tautan ke Papan Kerja. */
  .dsb2 .kpi:has(.kpi-rincian) { align-items: flex-start; }
  .kpi-rincian { list-style: none; margin: var(--s-2) 0 0; padding: var(--s-2) 0 0;
                 border-top: 1px solid color-mix(in srgb, var(--k, var(--line)) 30%, var(--line));
                 display: grid; gap: var(--s-2); font-size: var(--fs-sm); }
  .kpi-rincian li { display: grid; gap: 0; }
  .kpi-rincian a { font-weight: 600; }
  .kpi-rincian-angka { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
  .kpi-dasar { margin: var(--s-2) 0 0; font-size: var(--fs-xs); color: var(--muted); text-wrap: pretty; }

  .dsb-kisi { display: grid; gap: var(--s-4); align-items: stretch; }
  .dsb-kisi.rv   { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); }
  /* Kolom kanan dibatasi supaya tabel lebar di kiri (Daily Banding 16
   * kolom, Point Activity 12) muat tanpa digeser di 1440px. */
  .dsb-kisi.db   { grid-template-columns: minmax(0, 1fr) minmax(280px, 340px); }
  .dsb-kisi.tim  { grid-template-columns: minmax(0, 1fr) minmax(320px, 400px); }
  .dsb-kisi.tiket { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
  .dsb2 .lap-pa th, .dsb2 .lap-pa td { padding-inline: var(--s-2); }
  .dsb-kisi > .kartu { margin: 0; min-width: 0; }
  .dsb-panel { display: flex; flex-direction: column; }
  .dsb-panel > .kartu-kepala { justify-content: space-between; }
  .dsb-panel > .kartu-kepala h2 { margin: 0; font-family: var(--display); font-size: var(--fs-md); }
  .dsb-panel-kaki { margin-top: auto; }
  .dsb-panel > .catatan { margin: var(--s-3) var(--s-4) 0; }

  /* Grafik tren: SVG berskala; teks sumbu memakai px viewBox, jadi
   * ukurannya ikut lebar kartu dan tetap terbaca dari 320px ke atas. */
  .tren { display: block; width: 100%; height: auto; overflow: visible; }
  .tren-garis { stroke: var(--line); stroke-width: 1; }
  .tren-sumbu { fill: var(--faint); font-family: var(--mono); font-size: 11px; }
  .tren-sorot { fill: transparent; }
  .tren-bulan:hover .tren-sorot { fill: var(--surface-2); }
  .tren-total { fill: none; stroke: var(--fg); stroke-width: 1.5; stroke-linejoin: round; }
  .tren-total.berjalan { stroke-dasharray: 4 4; }
  .tren-titik { fill: var(--surface); stroke: var(--fg); stroke-width: 1.5; }
  .isi-shopee { background: var(--accent); fill: var(--accent); }
  .isi-tiktok { background: var(--grafik-2); fill: var(--grafik-2); }
  .tren-legenda { display: flex; flex-wrap: wrap; gap: var(--s-1) var(--s-4); margin-top: var(--s-2);
                  font-size: var(--fs-xs); color: var(--muted); }
  .tren-legenda span { display: inline-flex; align-items: center; gap: 6px; }
  .tren-legenda i { width: 10px; height: 10px; border-radius: 2px; }
  .tren-legenda s { width: 14px; border-top: 1.5px solid var(--fg); }
  .tren-catatan { margin: var(--s-2) 0 0; }

  /* Grafik ECharts Dashboard (2026-10-15, keputusan Juan). ui.js membuat
   * .grafik-kanvas (aria-hidden) dan memberi data-grafik-aktif; sejak itu
   * .grafik-asal (SVG/HTML server) tinggal untuk pembaca layar, dan muncul
   * lagi begitu fokus keyboard masuk ke tautannya (daftar Beban PIC). Tanpa
   * JS, atau bila ECharts gagal dimuat, .grafik-asal tampil seperti dulu.
   * Tinggi Beban PIC dan Sisa deadline ditulis ui.js dari jumlah barisnya. */
  .grafik { position: relative; min-width: 0; }
  .grafik-kanvas { width: 100%; }
  .grafik-tren .grafik-kanvas { height: 260px; }
  .grafik-donat { width: 150px; flex: none; }
  .grafik-donat .grafik-kanvas { height: 150px; }
  .grafik-corong .grafik-kanvas { height: 40px; }
  .grafik-sisa .grafik-kanvas { margin-bottom: var(--s-2); }
  .grafik[data-grafik-aktif] > .grafik-asal:not(:focus-within) {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  @media (max-width: 767px) { .grafik-tren .grafik-kanvas { height: 220px; } }

  .donat-wadah { display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
                 gap: var(--s-4); flex: 1; }
  .donat { width: 150px; height: 150px; flex: none; }
  .donat circle { fill: none; stroke-width: 16; }
  .donat-shopee { stroke: var(--accent); }
  .donat-tiktok { stroke: var(--grafik-2); }
  .donat-label { fill: var(--muted); font-size: 9px; }
  .donat-nilai { fill: var(--fg); font-size: 12.5px; font-weight: 700; font-family: var(--display); }
  .donat-legenda { display: grid; gap: var(--s-3); margin: 0; font-size: var(--fs-sm); }
  .donat-legenda dt { display: flex; align-items: center; gap: 6px; color: var(--muted); }
  .donat-legenda dt i { width: 10px; height: 10px; border-radius: 2px; }
  .donat-legenda dd { margin: 2px 0 0 16px; display: grid; }
  .donat-legenda dd b { font-family: var(--display); font-size: var(--fs-lg); }
  .donat-legenda dd span { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }

  /* Return Value per Brand (2026-10-14): deret kartu selebar blok, lima
   * terlihat di desktop, tiga di tablet, satu-dan-sedikit di telepon (kartu
   * berikutnya mengintip = tanda bisa digeser). Gulir native, TANPA
   * scroll-snap (snap pernah membuat tabel melompat balik ke kiri); ui.js
   * memutarnya lewat scrollTo. position: relative supaya offsetLeft kartu
   * dihitung dari deret ini. */
  .dsb-blok > .kartu { margin: 0; min-width: 0; }
  .rvb-deret { --kolom: 5; position: relative; display: flex; gap: var(--s-3); margin: 0;
               padding: var(--s-4); list-style: none; overflow-x: auto;
               overscroll-behavior-x: contain; }
  .rvb-deret[data-putar-aktif] { scrollbar-width: none; }
  .rvb-deret[data-putar-aktif]::-webkit-scrollbar { display: none; }
  .rvb-kartu { flex: 0 0 calc((100% - (var(--kolom) - 1) * var(--s-3)) / var(--kolom));
               min-width: 0; display: grid; gap: 2px; align-content: start;
               padding: var(--s-3); border: 1px solid var(--line); border-radius: var(--r-md);
               background: var(--surface-2); }
  .rvb-urut { font-family: var(--mono); font-size: var(--fs-xs); color: var(--faint); }
  .rvb-nama { font-weight: 600; font-size: var(--fs-sm); overflow-wrap: anywhere; }
  .rvb-nilai { margin-top: var(--s-1); font-family: var(--display); font-weight: 700;
               font-size: var(--fs-xl); line-height: 1.2; font-variant-numeric: tabular-nums;
               overflow-wrap: anywhere; }
  .rvb-persen, .rvb-kanal-teks { font-size: var(--fs-xs); color: var(--muted);
                                 font-variant-numeric: tabular-nums; }
  .rvb-kanal { display: flex; height: 6px; margin-top: var(--s-2); overflow: hidden;
               border-radius: var(--r-sm); background: var(--line); }

  /* Insight bergulir (2026-10-14): semua sorotan ditumpuk di satu sel grid,
   * jadi tinggi kotak = sorotan tertinggi dan tidak ada yang melompat. Yang
   * menunggu di bawah (atau di atas saat mundur), yang aktif di tempatnya,
   * yang keluar naik dan memudar. Hanya aktif kalau ui.js memasang
   * data-putar-aktif -- yaitu bila JS jalan DAN tidak reduced-motion. */
  .dsb-insight { align-self: start; }
  .sorotan[data-putar-aktif] { display: grid; overflow: hidden; }
  .sorotan[data-putar-aktif] > li { grid-area: 1 / 1; visibility: hidden; opacity: 0;
                                    transform: translateY(100%); }
  .sorotan[data-putar-aktif][data-arah="mundur"] > li:not([data-keadaan]) { transform: translateY(-100%); }
  .sorotan[data-putar-aktif] > li[data-keadaan="aktif"] { visibility: visible; opacity: 1; transform: none; }
  .sorotan[data-putar-aktif] > li[data-keadaan="keluar"] { transform: translateY(-100%); }
  .sorotan[data-putar-aktif][data-arah="mundur"] > li[data-keadaan="keluar"] { transform: translateY(100%); }
  @media (prefers-reduced-motion: no-preference) {
    .sorotan[data-putar-aktif] > li[data-keadaan] {
      transition: transform var(--t-lambat) var(--ease), opacity var(--t-lambat) var(--ease),
                  visibility var(--t-lambat); }
  }

  /* Kendali putar: Mundur / Jeda / Maju + posisi, di ujung kanan kepala
   * kartu. Target 28px (WCAG 2.5.8 minta 24). */
  .putar-kendali { display: inline-flex; align-items: center; gap: var(--s-1); margin-left: auto;
                   align-self: center; }
  .putar-kendali[hidden], .putar-tombol[hidden] { display: none; }
  .putar-tombol { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0;
                  border: 1px solid var(--line); border-radius: var(--r-md);
                  background: var(--surface); color: var(--muted); cursor: pointer; }
  .putar-tombol:hover { background: var(--surface-2); color: var(--fg); }
  .putar-tombol svg { width: 14px; height: 14px; }
  .putar-tombol > span { display: contents; }
  .putar-tombol[aria-pressed="true"] .putar-ikon-jeda,
  .putar-tombol:not([aria-pressed="true"]) .putar-ikon-lanjut { display: none; }
  .putar-posisi { min-width: 5ch; font-family: var(--mono); font-size: var(--fs-xs);
                  color: var(--faint); text-align: right; }

  /* Judul kartu + tombol (i): tombol di luar <h2>, supaya nama judul bagi
   * pembaca layar tidak ikut berisi "Apa itu …". */
  .judul-tip { display: inline-flex; align-items: center; gap: var(--s-1); }

  /* Dua blok Dashboard (2026-10-14): uang yang sudah masuk vs nilai order
   * yang belum jadi uang. Satu pembeda saja -- kepala bab (gaya .dsb-bagian
   * yang sudah ada) dan, untuk blok kedua, bingkai putus-putus: "belum
   * pasti". Tanpa warna baru. */
  .dsb-blok { display: grid; gap: var(--s-3); min-width: 0; }
  .dsb-blok > .dsb-bagian-kepala { padding-top: 0; border-top: 0; }
  .dsb-blok.belum-uang { padding: var(--s-4); border: 1px dashed var(--line-kuat);
                         border-radius: var(--r-lg); }
  .dsb-tumpuk { display: grid; gap: var(--s-4); min-width: 0; align-content: start; }
  .dsb-tumpuk > .kartu { margin: 0; min-width: 0; }
  /* Tiga jalur berdampingan (2026-10-15, permintaan Juan), satu kolom per
   * jalur. Kartu yang lebih sempit dari 24rem (tablet, telepon) tidak muat
   * "Rp1.377.859.008" per kolom, jadi tiap jalur jadi satu baris: label
   * kiri, angka rata kanan. Diukur dari lebar kartu, bukan layar. */
  .kartu-badan:has(> .jalur-legenda) { container-type: inline-size; }
  .jalur-legenda { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--s-4); }
  .jalur-legenda dd { margin-left: 0; }
  @container (max-width: 24rem) {
    .jalur-legenda { grid-template-columns: 1fr; }
    .jalur-legenda > div { display: grid; grid-template-columns: 1fr auto; align-items: baseline;
                           column-gap: var(--s-3); }
    .jalur-legenda dd { margin: 0; text-align: right; }
  }
  .bd-daftar .bd-subjudul > span { grid-column: 1 / -1; padding-top: var(--s-2);
                                   font-size: var(--fs-xs); font-weight: 600; color: var(--muted); }
  .bd-daftar .bd-anak > :nth-child(2) { padding-left: var(--s-3); color: var(--muted); }

  /* Daily Banding dipivot: Prioritas jadi kolom, kepala navy seperti
   * sheet leader. Kolom brand menempel saat tabel digeser di layar sempit. */
  .lap-db2 { width: 100%; border-collapse: collapse; font-size: var(--fs-xs);
             font-variant-numeric: tabular-nums; }
  .lap-db2 th, .lap-db2 td { padding: 5px 6px; text-align: right; white-space: nowrap;
                             border-bottom: 1px solid var(--line); }
  .lap-db2 thead th { background: var(--bar-bg); color: var(--bar-fg); font-weight: 600;
                      text-align: center; border-bottom-color: var(--bar-line); }
  .lap-db2 .kiri, .lap-db2 tbody th, .lap-db2 tfoot th { text-align: left; }
  .lap-db2 tbody th, .lap-db2 tfoot th, .lap-db2 thead th.kiri {
    position: sticky; left: 0; z-index: 1; background: var(--surface); font-weight: 600; }
  .lap-db2 thead th.kiri { background: var(--bar-bg); }
  .lap-db2 .lap-db2-total { border-left: 2px solid var(--line-kuat); }
  .lap-db2 thead .lap-db2-total { border-left-color: var(--bar-line); }
  .lap-db2 .tebal { font-weight: 700; }
  .lap-db2 tfoot th, .lap-db2 tfoot td { font-weight: 700; background: var(--surface-2);
                                         border-top: 2px solid var(--line-kuat); }

  .sorotan { list-style: none; margin: 0; padding: var(--s-3) var(--s-4) var(--s-4);
             display: grid; gap: var(--s-3); }
  .sorotan li { display: grid; gap: 2px; padding-left: var(--s-3);
                border-left: 3px solid var(--line-kuat); font-size: var(--fs-sm); }
  .sorotan li.baik      { border-left-color: var(--st-disetujui); }
  .sorotan li.perhatian { border-left-color: var(--sla-mendesak); }
  .sorotan li.buruk     { border-left-color: var(--sla-lewat); }
  .sorotan b { font-weight: 600; }
  .sorotan span { color: var(--muted); }

  /* Grade terisi penuh (gambar acuan); kata di dalamnya tetap pembawa
   * artinya, warna hanya penegas. */
  .lap-grade { min-width: 0; padding: 2px var(--s-2); border: 0; border-radius: 999px; }
  .lap-grade.high   { color: var(--surface); background: var(--st-disetujui); }
  .lap-grade.medium { color: var(--surface); background: var(--st-diajukan); }
  .lap-grade.low    { color: var(--surface); background: var(--st-ditolak); }

  .bd-kisi { display: grid; gap: var(--s-4) var(--s-6); padding: var(--s-4);
             grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .bd-blok { min-width: 0; }
  .bd-blok h3 { margin: 0 0 var(--s-2); font-size: var(--fs-sm); font-weight: 600; }
  .bd-daftar { list-style: none; margin: var(--s-2) 0 0; padding: 0; font-size: var(--fs-sm);
               display: grid; grid-template-columns: 10px minmax(0, 1fr) auto auto;
               column-gap: var(--s-2); }
  .bd-daftar li { display: contents; }
  .bd-daftar li > * { padding: 3px 0; border-bottom: 1px solid var(--line); align-self: center; }
  .bd-daftar li > .corong-titik { padding: 0; border: 0; }
  .bd-daftar .angka { text-align: right; font-variant-numeric: tabular-nums; }
  .bd-catatan { margin: var(--s-2) 0 0; }
  .corong-titik.tenggat-lewat    { background: var(--sla-lewat); }
  .corong-titik.tenggat-mendesak { background: var(--sla-mendesak); }
  .corong-titik.tenggat-aman     { background: var(--muted); }
  .corong-titik.tenggat-tanpa-tenggat { background: var(--line-kuat); }

  .aksi-daftar { list-style: none; margin: 0; padding: var(--s-3) var(--s-4);
                 display: grid; gap: var(--s-3); font-size: var(--fs-sm); }
  .aksi-daftar li { display: flex; gap: var(--s-2); align-items: flex-start; }
  .aksi-daftar svg { flex: none; width: 16px; height: 16px; margin-top: 2px;
                     color: var(--accent-teks); }

  .modal.lebar { width: min(1240px, calc(100vw - var(--s-5) * 2)); }
  .modal-isi > .lap-kartu + .lap-kartu { margin-top: var(--s-4); }

  @media (max-width: 1199.98px) {
    .dsb2 .kpi-baris { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .dsb2 .kpi-baris > :first-child { grid-column: 1 / -1; }
    .dsb-kisi.rv { grid-template-columns: minmax(0, 1fr); }
    .dsb-kisi.rv > .dsb-tumpuk { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .rvb-deret { --kolom: 3; }
    .dsb-kisi.db, .dsb-kisi.tim { grid-template-columns: minmax(0, 1fr); }
    .bd-kisi { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .bd-blok:first-child { grid-column: 1 / -1; }
  }
  @media (max-width: 767.98px) {
    .dsb2 .kpi-baris, .dsb-kisi.rv, .dsb-kisi.tiket, .bd-kisi,
    .dsb-kisi.rv > .dsb-tumpuk { grid-template-columns: minmax(0, 1fr); }
    .rvb-deret { --kolom: 1.25; }
    .dsb-status .dsb-tarik { margin-left: 0; justify-items: start; text-align: left; }
    .dsb2 .kpi { padding: var(--s-3); }
    .dsb2 .kpi-ikon { width: 32px; height: 32px; }
    .dsb2 .kpi-ikon svg { width: 16px; height: 16px; }
    /* Grafik menyusut ke ~0,6x di telepon; teks sumbu dibesarkan dalam
     * satuan viewBox supaya tetap ~10px di layar. */
    .tren-sumbu { font-size: 17px; }
  }
  @media (max-width: 719.98px) {
    .modal.lebar { width: 100vw; max-width: 100vw; max-height: 100vh;
                   border-radius: 0; border: 0; padding: var(--s-4); }
  }
}
