/* ==========================================================================
   LiveChat Audit — sistem desain
   --------------------------------------------------------------------------
   Satu berkas, tanpa build step, tanpa sumber daya luar. Gerak antarmuka ada
   di `public/js/motion.js` (anime.js, di-vendor lokal) — lihat bagian 2b.
   Urutannya:

     1. token          warna, jarak, radius, bayangan
     2. dasar          reset, tipografi, fokus
     3. kerangka       topbar, sidebar, konten, footer
     4. komponen       kartu, tabel, chip, tombol, form, alert, …
     5. halaman        yang benar-benar khusus satu halaman saja
     6. utilitas       kelas kecil serba guna
     7. responsif      titik henti + preferensi pengguna

   Prinsip yang dipakai berulang di bawah:

   - **Warna tidak pernah menjadi satu-satunya pembawa makna.** Setiap status
     punya teks, dan chip berwarna selalu memuat kata statusnya.
   - **Angka disejajarkan.** Semua kolom angka memakai `tabular-nums`; kolom
     yang bergoyang tidak bisa dibandingkan sekilas.
   - **Yang berbahaya harus terlihat berbeda.** Tindakan yang tidak bisa
     dibatalkan tidak pernah memakai gaya yang sama dengan tindakan biasa.
   ========================================================================== */

/* ========================================================== 0. huruf lokal */

/*
 * Inter, disimpan lokal (D116). Bukan dari Google Fonts: berkas ini satu-satunya
 * cara memenuhi "tetapkan typeface" di brief tanpa melanggar aturan yang sudah
 * ada di kepala berkas ini -- tanpa sumber daya luar. Halaman tidak pernah
 * menunggu server orang lain, dan tampilannya sama di Windows maupun Mac.
 *
 * Satu berkas variable per subset, bobot 400-700 -- 48 KB latin + 85 KB
 * latin-ext, bukan empat berkas statis.
 *
 * `font-display: swap` disengaja: teks muncul segera dengan huruf sistem lalu
 * berganti. Untuk alat kerja, teks yang terbaca telat lebih buruk daripada
 * pergeseran kecil sekali di awal.
 */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
    U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+1E00-1EFF, U+2020, U+20A0-20AB, U+2113,
    U+2C60-2C7F, U+A720-A7FF;
}

/* =============================================================== 1. token */

:root {
  color-scheme: light dark;

  /*
   * Netral bernada dingin — bukan abu murni. Selisih rona kecil (~250°) di
   * seluruh tangga permukaan inilah yang membuat antarmuka terasa dirancang,
   * bukan default. Semua nilai diverifikasi kontrasnya terhadap permukaan.
   */

  /* -- permukaan ------------------------------------------------------- */
  /*
   * Kanvas diturunkan sedikit (D116) supaya kartu benar-benar terangkat
   * darinya, bukan cuma diberi garis.
   */
  --canvas: #eaeef3;
  --surface: #ffffff;
  --surface-2: #f1f4f8;
  --surface-3: #e2e7ee;
  --overlay: rgb(255 255 255 / 78%);

  /*
   * Bidang baca (D116) -- token baru.
   *
   * Di alat ini ada dua jenis permukaan yang berbeda pekerjaannya: yang
   * DIBACA berjam-jam (transkrip, bukti) dan yang DIOPERASIKAN (antrean,
   * panel putusan, navigasi). Sebelumnya keduanya kartu putih yang sama.
   *
   * Bedanya rona, bukan terang: bidang baca netral, kanvas dingin. Halus --
   * tapi mata tahu ia sedang membaca atau sedang bekerja, dan itu mengurangi
   * lelah pada sesi panjang.
   */
  --surface-baca: #fcfcfd;

  /* -- garis ----------------------------------------------------------- */
  /*
   * Garis sengaja nyaris tak terlihat. Hierarki dibawa ukuran dan ruang;
   * garis hanya menandai batas wadah, tidak pernah membagi isinya.
   */
  --border: #dfe4ec;
  --border-strong: #c5ccd9;

  /*
   * Garis yang MENANDAI KONTROL, dipisahkan dari garis yang menggambar kotak
   * (D116). WCAG 1.4.11 menuntut 3:1 untuk batas yang jadi satu-satunya
   * penanda sebuah kontrol -- dan `--border-strong` lama (#d3d6e2) cuma
   * mencapai 1,5:1 di atas kartu putih. Setiap kolom isian di 32 layar
   * melanggarnya sampai perubahan ini.
   *
   * #828997 adalah nilai paling RINGAN yang masih lolos di ketiga latar
   * tempat kolom isian benar-benar muncul: kartu 3,52:1, kanvas 3,02:1,
   * surface-2 3,19:1. Lebih terang sedikit saja, gagal.
   */
  --border-control: #828997;

  /* -- teks ------------------------------------------------------------ */
  --text: #15161c;
  --text-2: #4a4b57;
  --text-3: #666770; /* kontras 5.6:1 di atas --surface, 4.6:1 di atas --surface-3 */

  /* -- aksen ----------------------------------------------------------- */
  /*
   * Teal, dan pilihannya bukan selera (D116).
   *
   * Di antarmuka ini setiap rona HANGAT sudah dipakai makna -- empat tingkat
   * keparahan, `warn`, `danger` -- dan biru sudah jadi `info`. Teal satu-
   * satunya rona dingin yang tersisa yang tidak akan pernah disalahartikan
   * sebagai sebuah putusan atas seorang agent.
   */
  --accent: #0f6f68;
  --accent-2: #0d8478; /* pasangan gradien untuk tombol & merek */
  --accent-hover: #0b544f;
  --accent-soft: #e4f2f0;
  --accent-text: #ffffff;

  /* -- makna ----------------------------------------------------------- */
  --ok: #15803d;
  --ok-soft: #f0fdf4;
  --warn: #b45309;
  --warn-soft: #fffbeb;
  --danger: #b91c1c;
  --danger-soft: #fef2f2;
  --info: #1d4ed8;
  --info-soft: #eff6ff;

  /* -- keparahan rubrik ------------------------------------------------ */
  --sev-ringan: #4b5563;
  --sev-sedang: #b45309;
  --sev-berat: #c2410c;
  --sev-fatal: #b91c1c;

  /* -- bentuk ---------------------------------------------------------- */
  /* Dinaikkan dua piksel pada pass D86: sudut yang sedikit lebih bulat adalah
     perbedaan paling murah antara "formulir kantor" dan "produk". */
  --radius: 12px;
  --radius-sm: 8px;
  --radius-lg: 20px;
  --radius-pill: 999px;

  /*
   * Bayangan tiga lapis (D86): garis kontak 1px, jatuhan dekat, jatuhan jauh
   * yang sangat lunak. Lapisan dekatnya yang membuat kartu terasa MENEMPEL,
   * lapisan jauhnya yang membuatnya terasa punya berat — satu lapis saja
   * selalu terlihat seperti stiker.
   */
  --shadow-sm:
    0 0 0 1px rgb(16 18 28 / 2%),
    0 1px 2px rgb(16 18 28 / 5%);
  --shadow:
    0 0 0 1px rgb(16 18 28 / 2%),
    0 2px 4px rgb(16 18 28 / 4%),
    0 12px 32px -16px rgb(16 18 28 / 18%);
  --shadow-lg:
    0 0 0 1px rgb(16 18 28 / 3%),
    0 6px 16px -6px rgb(16 18 28 / 10%),
    0 32px 64px -24px rgb(16 18 28 / 28%);
  /* Bayangan angkat untuk elemen interaktif saat hover. */
  --shadow-lift:
    0 0 0 1px rgb(16 18 28 / 3%),
    0 4px 10px -4px rgb(16 18 28 / 12%),
    0 16px 36px -18px rgb(16 18 28 / 22%);

  /* Lingkaran fokus lunak untuk kolom isian — outline tetap dipakai di luar. */
  --ring: 0 0 0 3px color-mix(in oklab, var(--accent) 20%, transparent);

  /* -- gerak ------------------------------------------------------------ */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1); /* outExpo — meluncur lalu mendarat */
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* outBack — pantulan kecil */

  /* -- jarak ----------------------------------------------------------- */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-8: 32px;
  --sp-10: 40px;
  --sp-12: 48px;

  /* -- huruf ----------------------------------------------------------- */
  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
    sans-serif;
  --mono: ui-monospace, 'SF Mono', 'Cascadia Mono', 'Roboto Mono', Menlo, monospace;

  /*
   * Tangga ukuran huruf. Sembilan langkah, semuanya bilangan bulat — ukuran
   * setengah piksel dirender berbeda-beda antar mesin dan membuat tangganya
   * tidak pernah benar-benar terbaca sebagai tangga.
   *
   *   11  label huruf besar, kepala tabel
   *   12  meta, chip, petunjuk
   *   13  isi tabel
   *   14  teks badan, tombol, kolom isian
   *   15  penegasan, judul kartu
   *   17  h2
   *   20  h3 besar / angka sedang
   *   26  h1
   *   32  angka statistik
   */
  /*
   * Skala diturunkan satu tingkat (D79) atas permintaan supervisor.
   *
   * Yang dijaga: 10 px tetap batas bawah untuk teks yang benar-benar dibaca,
   * dan `--fs-base` tidak turun di bawah 13 px — isi transkrip dan kolom
   * isian dibaca berjam-jam, dan itu ukuran terkecil yang masih nyaman untuk
   * itu. Yang mengecil paling banyak adalah label dan angka, bukan kalimat.
   */
  /*
   * Tangga dirapatkan 9 langkah -> LIMA ukuran nyata (D116).
   *
   * Brief meminta maksimal tiga. Tiga tidak bisa dipenuhi tanpa merugikan:
   * kepala tabel, isi tabel, dan meta chip akan seukuran, atau tabelnya
   * membengkak. Lima menyelesaikan keluhan yang sebenarnya -- "font terlalu
   * kecil untuk dipindai cepat" -- tanpa menukarnya dengan masalah lain.
   *
   *   12  meta, chip, kepala tabel, cap waktu
   *   14  teks badan, isi tabel, tombol, kolom isian
   *   17  kepala bagian
   *   21  judul halaman
   *   26  angka statistik
   *
   * Nama tokennya SENGAJA dipertahankan sembilan biji meski nilainya cuma
   * lima. Ada 109 pemakaian di berkas ini; mengganti namanya berarti menyentuh
   * 109 tempat untuk hasil yang persis sama. Yang berubah nilainya, bukan
   * kosakatanya.
   *
   * Batas bawah naik dari 10px ke 12px. Itu memang mengurangi kepadatan
   * sedikit, dan itu disengaja: 10px adalah keluhan yang tertulis di brief.
   */
  --fs-micro: 12px;
  --fs-xs: 12px;
  --fs-sm: 12px;
  --fs-base: 14px;
  --fs-md: 14px;
  --fs-lg: 17px;
  --fs-xl: 17px;
  --fs-2xl: 21px;
  --fs-3xl: 26px;

  --sidebar-w: 244px;
  --topbar-h: 56px;
  /* Tinggi kaki halaman; lihat `.footer` untuk alasannya. */
  --footer-h: 51px;
  --content-max: 1800px;
}

/*
 * Palet gelap ditulis dua kali dan itu disengaja.
 *
 * Blok pertama menjawab setelan sistem — dan tetap benar tanpa JavaScript.
 * `:not([data-theme='light'])` membuat pilihan manual TERANG menang atas
 * sistem gelap. Blok kedua menjawab pilihan manual GELAP di atas sistem
 * terang; CSS tidak punya cara memakai ulang satu blok token pada dua
 * kondisi berbeda, dan menaruhnya hanya di `[data-theme]` berarti tema
 * sistem berhenti bekerja begitu JavaScript gagal dimuat.
 *
 * Kalau salah satu nilai diubah, ubah keduanya.
 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) {
    --canvas: #0a0c10;
    --surface: #13161c;
    --surface-2: #1a1d25;
    --surface-3: #242832;
    --overlay: rgb(17 18 24 / 86%);
    --surface-baca: #0f1217;

    --border: #22262f;
    --border-strong: #393f4c;
    --border-control: #6d7481;

    --text: #ececf0;
    --text-2: #b4b4bf;
    --text-3: #8e8f99; /* kontras 5.3:1 di atas --surface-2, 4.6:1 di atas --surface-3 */

    --accent: #3fd0c0; /* 8.9:1 di atas --accent-soft — chip ai butuh AA penuh */
    --accent-2: #5ad9c9;
    --accent-hover: #63dccf;
    --accent-soft: #0d2a28;
    /* Aksen gelap terlalu terang untuk teks putih (1.6:1) — tintanya gelap. */
    --accent-text: #08110f;

    --ok: #4ade80;
    --ok-soft: #10241a;
    --warn: #fbbf24;
    --warn-soft: #2a2110;
    --danger: #f87171;
    --danger-soft: #2b1414;
    --info: #7aa5ff;
    --info-soft: #131f38;

    --sev-ringan: #9ca3af;
    --sev-sedang: #fbbf24;
    --sev-berat: #fb923c;
    --sev-fatal: #f87171;

    --shadow-sm:
      0 0 0 1px rgb(255 255 255 / 3%),
      0 1px 2px rgb(0 0 0 / 35%);
    --shadow:
      0 0 0 1px rgb(255 255 255 / 3%),
      0 2px 4px rgb(0 0 0 / 30%),
      0 12px 32px -16px rgb(0 0 0 / 60%);
    --shadow-lg:
      0 0 0 1px rgb(255 255 255 / 4%),
      0 6px 16px -6px rgb(0 0 0 / 40%),
      0 32px 64px -24px rgb(0 0 0 / 72%);
    --shadow-lift:
      0 0 0 1px rgb(255 255 255 / 4%),
      0 4px 10px -4px rgb(0 0 0 / 40%),
      0 16px 36px -18px rgb(0 0 0 / 60%);
  }
}

:root[data-theme='dark'] {
  --canvas: #0a0c10;
  --surface: #13161c;
  --surface-2: #1a1d25;
  --surface-3: #242832;
  --overlay: rgb(17 18 24 / 86%);
  --surface-baca: #0f1217;

  --border: #22262f;
  --border-strong: #393f4c;
  --border-control: #6d7481;

  --text: #ececf0;
  --text-2: #b4b4bf;
  --text-3: #8e8f99; /* kontras 5.3:1 di atas --surface-2, 4.6:1 di atas --surface-3 */

  --accent: #3fd0c0; /* 8.9:1 di atas --accent-soft — chip ai butuh AA penuh */
  --accent-2: #5ad9c9;
  --accent-hover: #63dccf;
  --accent-soft: #0d2a28;
  /* Aksen gelap terlalu terang untuk teks putih (1.6:1) — tintanya gelap. */
  --accent-text: #08110f;

  --ok: #4ade80;
  --ok-soft: #10241a;
  --warn: #fbbf24;
  --warn-soft: #2a2110;
  --danger: #f87171;
  --danger-soft: #2b1414;
  --info: #7aa5ff;
  --info-soft: #131f38;

  --sev-ringan: #9ca3af;
  --sev-sedang: #fbbf24;
  --sev-berat: #fb923c;
  --sev-fatal: #f87171;

  --shadow-sm:
    0 0 0 1px rgb(255 255 255 / 3%),
    0 1px 2px rgb(0 0 0 / 35%);
  --shadow:
    0 0 0 1px rgb(255 255 255 / 3%),
    0 2px 4px rgb(0 0 0 / 30%),
    0 12px 32px -16px rgb(0 0 0 / 60%);
  --shadow-lg:
    0 0 0 1px rgb(255 255 255 / 4%),
    0 6px 16px -6px rgb(0 0 0 / 40%),
    0 32px 64px -24px rgb(0 0 0 / 72%);
  --shadow-lift:
    0 0 0 1px rgb(255 255 255 / 4%),
    0 4px 10px -4px rgb(0 0 0 / 40%),
    0 16px 36px -18px rgb(0 0 0 / 60%);
}

/* Pemilih tema manual: atribut menang atas setelan sistem. */
:root[data-theme='dark'] {
  color-scheme: dark;
}
:root[data-theme='light'] {
  color-scheme: light;
}

/* -- tombol tema ------------------------------------------------------- */

.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-2);
  font: inherit;
  font-size: var(--fs-xs);
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
}
.theme-toggle:hover {
  border-color: var(--border-strong);
  color: var(--text);
}
.theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.theme-toggle .icon {
  font-size: var(--fs-md);
}
/* Hanya ikon mode yang sedang berlaku yang tampil. */
.theme-toggle [data-mode] {
  display: none;
}
:root:not([data-theme]) .theme-toggle [data-mode='sistem'],
:root[data-theme='light'] .theme-toggle [data-mode='terang'],
:root[data-theme='dark'] .theme-toggle [data-mode='gelap'] {
  display: inline-flex;
}
:root:not([data-theme]) .theme-toggle .theme-label::after {
  content: 'Sistem';
}
:root[data-theme='light'] .theme-toggle .theme-label::after {
  content: 'Terang';
}
:root[data-theme='dark'] .theme-toggle .theme-label::after {
  content: 'Gelap';
}

/* Di halaman masuk tidak ada topbar — tombolnya mengambang di pojok. */
.theme-floating {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 5;
}
.theme-floating .theme-toggle {
  background: color-mix(in oklab, var(--surface) 80%, transparent);
  backdrop-filter: blur(6px);
}

@media (max-width: 720px) {
  .theme-toggle .theme-label {
    display: none;
  }
}

/* ================================================================ 2. dasar */

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

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

body {
  margin: 0;
  /*
   * Kolom tegak setinggi layar supaya kaki halaman selalu turun ke dasar.
   * Sebelumnya yang mengisi tinggi layar cuma sidebar; begitu sidebar dilipat
   * di bawah 1080px, tidak ada lagi yang menahan, dan kaki halaman naik ke
   * tengah dengan kanvas kosong di bawahnya -- terukur di 820x1024: kaki
   * beranda berhenti di 348px pada halaman setinggi 1024px.
   */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* Kanvas polos. Latar bergradien membuat kartu putih di atasnya terlihat
     kotor di separuh layar — permukaan yang tenang justru menonjolkan isinya. */
  background: var(--canvas);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.25;
  text-wrap: balance;
}
h1 {
  font-size: var(--fs-2xl);
  font-weight: 650;
  letter-spacing: -0.022em;
}
h2 {
  font-size: var(--fs-lg);
  letter-spacing: -0.016em;
}
h3 {
  font-size: var(--fs-md);
}

p {
  margin: 0 0 var(--sp-3);
}
p:last-child {
  margin-bottom: 0;
}

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

code,
.mono {
  font-family: var(--mono);
  font-size: 0.92em;
  font-variant-ligatures: none;
}

hr {
  height: 1px;
  margin: var(--sp-5) 0;
  border: 0;
  background: var(--border);
}

/*
 * Fokus terlihat di mana pun, termasuk di atas latar berwarna. Menghapus
 * outline demi estetika membuat aplikasi ini tidak bisa dipakai lewat keyboard.
 *
 * Cincinnya diberi jarak dua piksel dari elemennya supaya tetap terbaca di
 * atas tombol berwarna penuh — outline yang menempel menyatu dengan isinya.
 */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}
/* Di atas permukaan beraksen, cincin aksen sendiri tidak cukup kontras. */
.btn:focus-visible,
.pagination .current:focus-visible,
.nav-item.is-active:focus-visible,
.queue-item.is-active:focus-visible {
  outline-color: var(--text);
}

::selection {
  background: var(--accent-soft);
  color: var(--text);
}

/* Scrollbar tipis yang mengikuti tema — detail kecil yang terasa di aplikasi
   penuh tabel panjang. */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-thumb {
  border: 3px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-3);
  background-clip: content-box;
}
::-webkit-scrollbar-track {
  background: transparent;
}

/* Lompat ke konten — tersembunyi sampai difokuskan lewat keyboard. */
.skip-link {
  position: absolute;
  top: -60px;
  left: var(--sp-3);
  z-index: 100;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--accent-text);
  font-size: var(--fs-sm);
  font-weight: 600;
  transition: top 0.15s ease;
}
.skip-link:focus {
  top: var(--sp-3);
  text-decoration: none;
}

/* Ikon SVG sebaris. Ukurannya mengikuti teks di sekitarnya. */
.icon {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  fill: none;
  stroke: currentcolor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-lg {
  width: 1.25em;
  height: 1.25em;
}

/* ============================================================== 2b. gerak */

/*
 * Gerak antarmuka dijalankan anime.js di `public/js/motion.js`, bukan di sini.
 *
 * Alasannya bukan selera: yang paling berguna di aplikasi ini justru hal yang
 * tidak bisa dilakukan CSS — angka statistik yang berhitung naik, dan blok di
 * bawah lipatan layar yang baru bergerak saat benar-benar digulir. Menjaga dua
 * sumber animasi sekaligus hanya membuat keduanya bertabrakan.
 *
 * Yang tersisa di CSS hanyalah gerak yang melekat pada interaksi — hover,
 * fokus, denyut status — karena itu urusan keadaan elemen, bukan koreografi.
 *
 * Keadaan tersembunyi TIDAK pernah dipasang di sini. Kalau `motion.js` gagal
 * dimuat, halaman harus tetap tampil utuh.
 */

/* Menu situs tetap memakai CSS: ia muncul dari interaksi, bukan dari muat. */
@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ============================================================ 3. kerangka */

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  height: var(--topbar-h);
  padding: 0 clamp(var(--sp-4), 3vw, var(--sp-6));
  border-bottom: 1px solid var(--border);
  background: var(--overlay);
  backdrop-filter: blur(18px) saturate(1.6);
  /* Hairline jatuh yang sangat tipis — memisahkan kaca dari isi di bawahnya
     saat halaman digulir, tanpa terlihat sebagai bayangan. */
  box-shadow: 0 1px 0 rgb(16 18 28 / 3%);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--text);
  font-size: var(--fs-md);
  font-weight: 650;
  letter-spacing: -0.015em;
  white-space: nowrap;
}
.brand:hover {
  text-decoration: none;
}
/* Satu-satunya tempat gradien aksen dipakai: tanda merek. */
.brand-mark {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: var(--accent-text);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.topbar-spacer {
  flex: 1;
}

.topbar-user {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--text-3);
  font-size: var(--fs-sm);
  white-space: nowrap;
}
.topbar-user strong {
  color: var(--text-2);
  font-weight: 600;
}

/* Inisial pengguna — jangkar visual di pojok kanan, pengganti foto profil. */
.avatar {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 700;
}

/* -- pemilih situs ----------------------------------------------------- */

.site-switch {
  position: relative;
}
.site-switch > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-2);
  font-size: var(--fs-sm);
  font-weight: 500;
  list-style: none;
  cursor: pointer;
  user-select: none;
}
.site-switch > summary::-webkit-details-marker {
  display: none;
}
.site-switch > summary::after {
  content: '';
  width: 6px;
  height: 6px;
  margin-left: 2px;
  border-right: 1.5px solid currentcolor;
  border-bottom: 1.5px solid currentcolor;
  transform: translateY(-2px) rotate(45deg);
}
.site-switch > summary:hover {
  border-color: var(--border-strong);
  color: var(--text);
}
.site-switch[open] > summary {
  border-color: var(--accent);
  color: var(--text);
}

.site-switch-menu {
  animation: rise-in 0.22s var(--ease-out) both;
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 50;
  min-width: 230px;
  max-height: 340px;
  padding: var(--sp-1);
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.site-switch-menu a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  color: var(--text-2);
  font-size: var(--fs-sm);
}
.site-switch-menu a:hover {
  background: var(--surface-2);
  color: var(--text);
  text-decoration: none;
}
.site-switch-menu a.is-current {
  color: var(--accent);
  font-weight: 600;
}
.site-switch-menu a.is-current::after {
  content: '✓';
}

.menu-label {
  padding: 6px 10px 4px;
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* -- kerangka isi ------------------------------------------------------ */

.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  align-items: start;
  /* Bagian yang memuai di antara kepala dan kaki halaman. */
  flex: 1 1 auto;
}

/*
 * Sidebar tanpa bingkai dan tanpa latar sendiri: yang memisahkannya dari area
 * kerja adalah kartu-kartu di sebelahnya, bukan sebuah garis. Item aktif
 * cukup ditandai satu pil beraksen — pil DAN garis tepi sekaligus menandai
 * hal yang sama dua kali.
 */
.sidebar {
  position: sticky;
  top: var(--topbar-h);
  /*
   * Kaki halaman berada di luar `.shell`, jadi sidebar setinggi
   * `100vh - topbar` membuat body selalu 51 px lebih tinggi daripada layar —
   * gulir hantu di setiap halaman yang isinya pendek, termasuk meja kerja
   * percakapan yang justru dirancang supaya tidak menggulir.
   */
  height: calc(100vh - var(--topbar-h) - var(--footer-h));
  padding: var(--sp-5) var(--sp-3) var(--sp-8);
  overflow-y: auto;
  background: transparent;
}

.nav-group + .nav-group {
  margin-top: var(--sp-6);
}
.nav-label {
  padding: 0 10px var(--sp-2);
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  color: var(--text-2);
  font-size: var(--fs-base);
  font-weight: 500;
  transition: background 0.12s ease, color 0.12s ease;
}
.nav-item + .nav-item {
  margin-top: 1px;
}
.nav-item:hover {
  background: var(--surface-2);
  color: var(--text);
  text-decoration: none;
}
.nav-item:hover .icon {
  opacity: 1;
  transform: translateX(1px);
}
.nav-item.is-active {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}
/*
 * Rel aksen di tepi kiri item aktif (D86). Pil berwarna saja mudah tenggelam
 * saat mata memindai kolom; garis vertikal 3px adalah penanda "Anda di sini"
 * yang terbaca bahkan dari sudut mata — pola yang sama dengan antrean.
 */
.nav-item.is-active::before {
  content: '';
  position: absolute;
  top: 7px;
  bottom: 7px;
  left: -8px;
  width: 3px;
  border-radius: 0 2px 2px 0;
  background: linear-gradient(180deg, var(--accent), var(--accent-2));
}
.nav-item .icon {
  opacity: 0.7;
  transition: opacity 0.12s ease, transform 0.15s var(--ease-out);
}
.nav-item.is-active .icon {
  opacity: 1;
}
/* Lencana angka di sisi kanan item nav (mis. temuan menunggu). */
.nav-badge {
  margin-left: auto;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
  color: var(--text-2);
  font-size: var(--fs-micro);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.nav-item.is-active .nav-badge {
  background: var(--accent);
  color: var(--accent-text);
}

.content {
  width: 100%;
  max-width: var(--content-max);
  min-width: 0;
  /* Sisa lebar dibagi rata kiri-kanan; padding ikut membesar di layar lebar
     supaya konten benar-benar memakai ruang yang ada. */
  margin-inline: auto;
  padding: clamp(24px, 2.4vw, 36px) clamp(20px, 3vw, 44px) var(--sp-12);
}
.content-narrow {
  max-width: 720px;
}

/*
 * Moto di kaki halaman. Tingginya diumumkan sebagai variabel karena satu
 * halaman perlu tahu: meja kerja percakapan (`.workbench`) setinggi layar, dan
 * kaki ini berada DI LUAR `.shell` — tanpa memperhitungkannya, satu-satunya
 * sisa yang membuat halaman itu masih bisa digulir adalah kaki ini.
 *
 * Berlaku selama motonya satu baris; di bawah 900 px meja kerja memang kembali
 * menggulir biasa, jadi angka ini tidak dipakai di sana.
 */
.footer {
  --footer-h: 51px;
  padding: 0 var(--sp-6) var(--sp-8);
  color: var(--text-3);
  font-size: var(--fs-xs);
  text-align: center;
}

/* -- kepala halaman ---------------------------------------------------- */

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: var(--sp-3);
  color: var(--text-3);
  font-size: var(--fs-xs);
}
.breadcrumb a {
  color: var(--text-3);
}
.breadcrumb a:hover {
  color: var(--text-2);
}
.breadcrumb-sep {
  opacity: 0.55;
}

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
}
.page-head-main {
  min-width: 0;
  flex: 1 1 420px;
}
.page-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}
.lede {
  margin: 8px 0 0;
  max-width: 74ch;
  color: var(--text-2);
  font-size: var(--fs-base);
  text-wrap: pretty;
}

/*
 * Baris meta ringkas di bawah judul: angka-angka kunci yang tidak layak
 * mendapat kartu statistik sendiri. Pengganti tile besar yang isinya sebaris.
 */
.page-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px;
  margin-top: 10px;
  color: var(--text-3);
  font-size: var(--fs-xs);
}
.page-meta strong {
  color: var(--text);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.meta-sep {
  opacity: 0.5;
}

/* -- bagian ------------------------------------------------------------ */

.section {
  margin-top: var(--sp-8);
}
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.section-head h2 {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* =========================================================== 4. komponen */

/* -- kartu ------------------------------------------------------------- */

/*
 * Kartu: satu hairline sebagai batas wadah, tidak ada garis lagi di dalamnya.
 * Kepala kartu dipisahkan dari isinya oleh jarak dan bobot huruf — sebuah
 * garis di sana hanya mengulang apa yang sudah dikatakan tata letaknya.
 */
.card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.2s var(--ease-out), border-color 0.2s ease;
}
/*
 * Kartu yang seluruhnya tautan ikut mengangkat saat hover (D86). Kartu diam
 * tidak: benda yang tidak bisa diklik tidak boleh berpura-pura bisa.
 */
a.card:hover,
a.stat:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lift);
  text-decoration: none;
}
.card + .card {
  margin-top: var(--sp-4);
}
.card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5) var(--sp-2);
}
.card-head h2,
.card-head h3 {
  margin: 0;
}
/* Kepala yang langsung diikuti tabel butuh sedikit lebih banyak nafas. */
.card-head + .table-wrap {
  margin-top: var(--sp-2);
}
.card-body {
  padding: var(--sp-5);
}
.card-head + .card-body {
  padding-top: var(--sp-3);
}
.card-note {
  padding: var(--sp-3) var(--sp-5);
  border-radius: 0 0 var(--radius) var(--radius);
  background: var(--surface-2);
  color: var(--text-3);
  font-size: var(--fs-xs);
}

/*
 * Zona berbahaya. Bingkainya merah supaya tindakan yang tidak bisa dibatalkan
 * tidak pernah terlihat sama seperti kartu biasa di halaman yang sama.
 */
.card.danger-zone {
  border-color: color-mix(in oklab, var(--danger) 55%, var(--border));
}
.card.danger-zone .card-head {
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--danger-soft);
  padding-bottom: var(--sp-4);
}
.card.danger-zone .card-head h3 {
  color: var(--danger);
}

/* -- statistik --------------------------------------------------------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-6);
}
.stat {
  position: relative;
  padding: var(--sp-5);
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
/* Garis kunci di puncak tile — hadir hanya saat tile membawa makna. */
.stat::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 2px;
  background: var(--border-strong);
  opacity: 0;
  transition: opacity 0.15s ease;
}
.stat.accent::before { background: var(--accent); opacity: 1; }
.stat.danger::before { background: var(--danger); opacity: 1; }
.stat.ok::before { background: var(--ok); opacity: 1; }
.stat.warn::before { background: var(--warn); opacity: 1; }
a.stat {
  color: inherit;
  transition:
    border-color 0.12s ease,
    box-shadow 0.15s ease,
    transform 0.15s ease;
}
a.stat:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
  text-decoration: none;
}
a.stat:hover::before {
  opacity: 1;
}
.stat-label {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.stat-value {
  margin-top: var(--sp-2);
  font-size: var(--fs-3xl);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
  line-height: 1.05;
}
/* Angka yang berdiri di kolom samping 380px — tetap angka utama di panelnya,
   tapi tidak boleh berteriak sekeras angka di tile beranda. */
.stat-value.sm {
  font-size: var(--fs-xl);
  letter-spacing: -0.02em;
}
.stat-meta {
  margin-top: 4px;
  color: var(--text-3);
  font-size: var(--fs-xs);
  /* Isinya kerap nama berkas atau tanggal bermonospace yang tidak punya spasi
     untuk dipatahkan; tanpa ini ia meluber keluar kartu di layar sempit
     (terukur 18px pada 820px). */
  overflow-wrap: anywhere;
}
.stat.accent .stat-value {
  color: var(--accent);
}
.stat.danger .stat-value {
  color: var(--danger);
}
.stat.ok .stat-value {
  color: var(--ok);
}
.stat.warn .stat-value {
  color: var(--warn);
}

/* -- tabel ------------------------------------------------------------- */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}

/* Tabel yang hidup di dalam kartu menempel tanpa bingkai ganda —
   dipakai bersama .toolbar untuk menyatukan filter dan hasilnya. */
.card > .table-wrap {
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.card > .table-wrap:first-child {
  border-radius: var(--radius) var(--radius) 0 0;
}
.card > .table-wrap:last-child {
  border-radius: 0 0 var(--radius) var(--radius);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
/*
 * Kepala tabel: latar sama dengan permukaannya (ia harus tetap buram karena
 * lengket), dibedakan oleh huruf kecil berspasi lebar dan satu hairline.
 */
thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--sp-3) var(--sp-4) var(--sp-2);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-weight: 650;
  letter-spacing: 0.07em;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}
tbody td {
  padding: 10px var(--sp-4);
  border-top: 1px solid var(--border);
  vertical-align: top;
}
tbody tr:first-child td {
  border-top: 0;
}
tbody tr:hover td {
  background: var(--surface-2);
}
td.num,
th.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
td.tight,
th.tight {
  white-space: nowrap;
}
/* Sel yang hanya berisi tombol aksi — jangan ikut melebar. */
td.actions {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}
tbody td.spacious {
  padding-top: var(--sp-3);
  padding-bottom: var(--sp-3);
}

/*
 * Tabel daftar kerja. Ribuan baris yang dipindai dari atas ke bawah:
 * padding rapat, zebra tipis sebagai pemandu baris, dan chip tanpa margin
 * ekstra. Kepadatan di sini adalah fitur, bukan kompromi.
 */
.worklist tbody td {
  padding-top: 7px;
  padding-bottom: 7px;
  /* Zebra sudah memandu mata baris demi baris — garis di antaranya jadi
     lapisan kedua yang mengerjakan pekerjaan yang sama. */
  border-top: 0;
  vertical-align: middle;
}
.worklist tbody tr:nth-child(even) td {
  background: color-mix(in oklab, var(--surface-2) 55%, var(--surface));
}
/*
 * Hover baris (D86): tint aksen + rel 2px di tepi kiri sel pertama. Rel-nya
 * yang membuat baris terasa "terpilih" dan bukan sekadar berubah warna — dan
 * ia meniru penanda aktif yang sama di sidebar dan antrean, jadi seluruh
 * aplikasi berbicara satu bahasa penanda.
 */
.worklist tbody tr:hover td {
  background: color-mix(in oklab, var(--accent-soft) 45%, var(--surface));
}
.worklist tbody tr:hover td:first-child {
  box-shadow: inset 2px 0 0 var(--accent);
}
.worklist .chip-row {
  max-width: none;
  gap: 3px;
}

/* -- keadaan kosong ---------------------------------------------------- */

/*
 * Keadaan kosong bukan pesan galat. Ia menjelaskan kenapa kosong dan apa
 * langkah berikutnya — halaman kosong tanpa arahan membuat pengguna berhenti.
 */
.empty {
  padding: var(--sp-10) var(--sp-5);
  color: var(--text-2);
  text-align: center;
}
.empty-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: 0 auto var(--sp-4);
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-3);
}
.empty-icon .icon {
  width: 20px;
  height: 20px;
}
.empty-title {
  color: var(--text);
  font-size: var(--fs-md);
  font-weight: 600;
}
.empty-body {
  max-width: 50ch;
  margin: 6px auto 0;
  font-size: var(--fs-sm);
  text-wrap: pretty;
}
.empty-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
}

/* -- chip -------------------------------------------------------------- */

/*
 * Chip: bentuknya dibawa latar, bukan garis. Bingkai hanya dipakai varian
 * yang latarnya sengaja dikosongkan (temuan yang sudah diputus).
 */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-2);
  font-size: var(--fs-xs);
  font-weight: 500;
  line-height: 1.35;
  white-space: nowrap;
  /* Cincin sewarna isi, sangat tipis — memberi chip tepi tanpa border
     yang menebalkan ukurannya (D86). */
  box-shadow: inset 0 0 0 1px color-mix(in oklab, currentcolor 14%, transparent);
}
.chip::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentcolor;
  opacity: 0.85;
}
.chip.plain::before {
  display: none;
}

.chip.ringan {
  color: var(--sev-ringan);
}
.chip.sedang {
  border-color: transparent;
  background: var(--warn-soft);
  color: var(--sev-sedang);
}
.chip.berat {
  border-color: transparent;
  background: var(--warn-soft);
  color: var(--sev-berat);
}
.chip.fatal {
  border-color: transparent;
  background: var(--danger-soft);
  color: var(--sev-fatal);
}

.chip.open {
  border-color: transparent;
  background: var(--info-soft);
  color: var(--info);
}
/* Empat status (D44): menunggu / pelanggaran / aman / temuan keliru. */
.chip.approve {
  border-color: transparent;
  background: var(--danger-soft);
  color: var(--danger);
}
.chip.aman {
  border-color: transparent;
  background: var(--ok-soft);
  color: var(--ok);
}
.chip.reject {
  color: var(--text-3);
}

/* -- tab status -------------------------------------------------------- */

/* Warna tab mengikuti arti statusnya; `.bubble` menyalin `currentcolor` saat
   aktif, jadi cukup mewarnai teksnya. */
.bubble.st-open { color: var(--info); }
.bubble.st-approve { color: var(--danger); }
.bubble.st-aman { color: var(--ok); }
.bubble.st-reject { color: var(--text-3); }

/* Tombol keputusan sebaris di dalam tabel (SystemLog). Tiga aksi berdampingan,
   satu klik — tanpa membuka halaman lain. */
/*
 * Tiga tombol putusan yang boleh bertumpuk (D116).
 *
 * `inline-flex` tanpa `flex-wrap` membuat approve/aman/reject selalu memaksa
 * satu baris. Terukur di 1280px pada baris SystemLog: isi 178px di dalam wadah
 * 117px — 61px meluber keluar, dan barisnya terpotong begitu `<details>`-nya
 * dibuka.
 */
.decide {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
}
.decide button {
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.decide button:hover {
  border-color: currentcolor;
}
.decide button.is-on {
  border-color: transparent;
  background: color-mix(in oklab, currentcolor 14%, var(--surface));
}
.decide .a-approve { color: var(--danger); }
.decide .a-aman { color: var(--ok); }
.decide .a-reject { color: var(--text-3); }

/* -- sub-navigasi ------------------------------------------------------ */

/*
 * Submenu: satu garis panduan tipis yang menyambung dua anak menu di bawah
 * induknya. Garisnya yang menandai kedalaman, jadi anaknya tidak perlu latar
 * penuh — kecuali yang sedang dibuka.
 */
.nav-sub {
  display: block;
  margin: 1px 0 0 22px;
  padding: 5px 12px;
  border-left: 1px solid var(--border-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  color: var(--text-3);
  font-size: var(--fs-sm);
}
.nav-sub:hover {
  border-left-color: var(--text-3);
  color: var(--text);
  text-decoration: none;
}
.nav-sub.is-active {
  border-left: 2px solid var(--accent);
  padding-left: 11px;
  color: var(--accent);
  font-weight: 600;
}
.nav-sub .nav-badge {
  margin-left: 6px;
}

.chip.completed {
  border-color: transparent;
  background: var(--ok-soft);
  color: var(--ok);
}
.chip.partial,
.chip.running,
.chip.pending {
  border-color: transparent;
  background: var(--warn-soft);
  color: var(--warn);
}
.chip.failed,
.chip.cancelled {
  border-color: transparent;
  background: var(--danger-soft);
  color: var(--danger);
}

.chip.engine {
  color: var(--text-2);
}
.chip.ai {
  border-color: transparent;
  background: var(--accent-soft);
  color: var(--accent);
}

/* Titik status yang berdenyut untuk pekerjaan yang benar-benar berjalan. */
.chip.running::before {
  animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse {
  0%,
  100% {
    opacity: 0.35;
  }
  50% {
    opacity: 1;
  }
}

/*
 * Beberapa chip berdampingan di dalam satu sel. Membungkus, bukan memaksa
 * barisnya melebar — satu percakapan bisa melanggar lima aturan sekaligus.
 */
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 400px;
}

a.chip.rule {
  font-family: var(--mono);
  font-size: var(--fs-micro);
  text-decoration: none;
}
a.chip.rule:hover {
  border-color: currentcolor;
}
/*
 * Temuan yang sudah diputuskan meredup: antrian adalah daftar kerja, dan mata
 * harus jatuh ke yang masih menunggu — bukan ke semua chip sekaligus.
 */
a.chip.rule.decided {
  background: transparent;
  border-color: var(--border);
  color: var(--text-3);
}
a.chip.rule.decided::before {
  opacity: 0.4;
}

/* Angka temuan yang menunggu keputusan pada satu percakapan. */
.count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--warn-soft);
  color: var(--warn);
  font-size: var(--fs-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* -- bubble filter ----------------------------------------------------- */

/*
 * Bentuknya sengaja sama dengan chip di dalam tabel: yang diklik di atas
 * adalah benda yang sama dengan yang dilihat di baris.
 */
.bubbles {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--sp-3);
}
.bubble {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text-2);
  font-size: var(--fs-sm);
  font-weight: 500;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.bubble:hover {
  border-color: var(--border-strong);
  text-decoration: none;
}
.bubble.active {
  border-color: currentcolor;
  background: color-mix(in oklab, currentcolor 10%, var(--surface));
  font-weight: 600;
}
.bubble.sedang,
.bubble.berat {
  color: var(--sev-berat);
}
.bubble.fatal {
  color: var(--sev-fatal);
}
.bubble-count {
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}
.bubble.active .bubble-count {
  background: var(--surface-3);
  color: inherit;
}

/*
 * Baris kedua (kode aturan) sengaja dibuat lebih ringan daripada baris
 * kategori: lebih kecil, tanpa warna keparahan, digeser masuk dan diberi garis
 * penghubung ke kiri. Dua baris chip dengan bobot yang sama akan saling
 * bersaing dan pengguna kehilangan tahu mana yang tingkat pertama.
 */
.bubbles-sub {
  margin: calc(var(--sp-3) * -1 + 2px) 0 var(--sp-4);
  padding-left: var(--sp-4);
  border-left: 2px solid var(--border);
}
.bubbles-sub .bubble {
  padding: 4px 10px;
  font-size: var(--fs-xs);
  font-weight: 400;
}
.bubbles-sub .bubble .mono {
  font-size: var(--fs-xs);
}
.bubble.ghost {
  background: transparent;
  border-style: dashed;
  color: var(--text-3);
}
.bubble.ghost.active {
  background: color-mix(in oklab, var(--accent) 10%, var(--surface));
  border-style: solid;
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

/* -- toolbar filter ---------------------------------------------------- */

/*
 * Baris filter yang menempel langsung di atas tabelnya. Filter dan hasil
 * adalah satu benda — memisahkannya ke kartu sendiri hanya menambah jarak
 * vertikal tanpa menambah makna.
 */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-4);
  border-radius: var(--radius) var(--radius) 0 0;
  background: color-mix(in oklab, var(--surface-2) 55%, var(--surface));
}
.toolbar .field {
  gap: 3px;
}
.toolbar label {
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-weight: 500;
}
/*
 * Baris filter lanjutan yang dilipat di dalam toolbar. Ia bukan benda kedua di
 * dalam toolbar, melainkan baris berikutnya dari toolbar yang sama — jadi
 * bingkai, latar, dan padding kirinya dilepas semua.
 */
.toolbar > .disclosure {
  flex-basis: 100%;
  border: 0;
  background: none;
}
.toolbar > .disclosure > summary {
  padding-left: 0;
}
.toolbar > .disclosure .disclosure-body {
  padding: var(--sp-3) 0 0;
}

/* Ringkasan hasil di ujung kanan toolbar. */
.toolbar-count {
  align-self: center;
  margin-left: auto;
  padding-left: var(--sp-2);
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.toolbar-count strong {
  color: var(--text);
  font-weight: 650;
}

/* -- rel langkah alur --------------------------------------------------- */

/*
 * Alur kerja situs berurutan: unduh → unggah SystemLog → audit → review.
 * Rel ini menunjukkan di mana pengguna berada dan apa yang menghalangi
 * langkah berikutnya — status disampaikan lewat angka, ikon ✓, dan teks,
 * bukan warna saja.
 */
.steps {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-5);
  padding: 0;
  list-style: none;
  counter-reset: step;
}
.step {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 190px;
  min-width: 0;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  color: var(--text-2);
  font-size: var(--fs-sm);
  font-weight: 550;
}
.step::before {
  counter-increment: step;
  content: counter(step);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--surface-3);
  color: var(--text-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.step-note {
  display: block;
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-weight: 400;
}
.step.done {
  border-color: color-mix(in oklab, var(--ok) 35%, var(--border));
}
.step.done::before {
  content: '✓';
  background: var(--ok-soft);
  color: var(--ok);
}
.step.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 14%, transparent), var(--shadow-sm);
  color: var(--text);
}
.step.active::before {
  background: var(--accent);
  color: var(--accent-text);
}
.step.blocked {
  border-style: dashed;
  background: transparent;
  box-shadow: none;
  color: var(--text-3);
}

/* -- dropzone unggah ---------------------------------------------------- */

/*
 * Area jatuh berkas. <label> membungkus <input type=file> yang disembunyikan;
 * fokus keyboard tetap terlihat lewat :focus-within.
 */
.dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: var(--sp-6) var(--sp-4);
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius);
  background: color-mix(in oklab, var(--accent-soft) 22%, var(--surface));
  color: var(--text-2);
  font-size: var(--fs-sm);
  text-align: center;
  cursor: pointer;
  transition:
    border-color 0.12s ease,
    background 0.12s ease;
}
.dropzone:hover,
.dropzone.dragover {
  border-color: var(--accent);
  background: color-mix(in oklab, var(--accent-soft) 50%, var(--surface));
}
.dropzone:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.dropzone strong {
  color: var(--text);
}
.dropzone .icon-lg {
  width: 26px;
  height: 26px;
  color: var(--accent);
}
.dropzone-hint {
  color: var(--text-3);
  font-size: var(--fs-xs);
}
.dropzone input[type='file'] {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

/* -- tombol ------------------------------------------------------------ */

/*
 * Satu aksi utama per halaman, jadi tombol utama boleh polos: warna aksen
 * rata tanpa gradien, tanpa kilau, tanpa cahaya. Yang membedakannya dari
 * sekitarnya adalah bahwa hanya ada satu.
 */
/*
 * Tombol utama (D86): gradien aksen sangat tipis + garis cahaya di tepi atas.
 * Bukan hiasan — permukaan datar satu warna tidak memberi tahu jari di mana
 * harus menekan; kurva cahaya kecil inilah yang membuat tombol terbaca
 * sebagai benda yang bisa ditekan bahkan sebelum kursor menyentuhnya.
 *
 * Hover mengangkat setengah piksel dengan bayangan; active menekannya balik.
 * Dua gerak kecil itu adalah seluruh "rasa" sebuah tombol.
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 36px;
  padding: 0 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background:
    linear-gradient(180deg, rgb(255 255 255 / 12%), transparent 45%),
    var(--accent);
  color: var(--accent-text);
  font-family: inherit;
  font-size: var(--fs-base);
  font-weight: 550;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 14%), var(--shadow-sm);
  transition:
    background 0.12s ease,
    border-color 0.12s ease,
    color 0.12s ease,
    box-shadow 0.15s var(--ease-out),
    transform 0.15s var(--ease-out);
}
.btn:hover {
  background:
    linear-gradient(180deg, rgb(255 255 255 / 14%), transparent 45%),
    var(--accent-hover);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 16%), var(--shadow-lift);
  transform: translateY(-0.5px);
  text-decoration: none;
}
.btn:active {
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 12%);
  transform: translateY(0.5px);
}
.btn:disabled,
.btn[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn.ghost {
  border-color: var(--border-control);
  background: var(--surface);
  color: var(--text-2);
}
.btn.ghost:hover {
  background: var(--surface-2);
  color: var(--text);
}

/*
 * Teks tombol bahaya memakai token, bukan putih tetap: merah mode gelap
 * terlalu terang untuk teks putih (2.8:1), jadi tintanya ikut menggelap
 * bersama --accent-text.
 */
.btn.danger {
  border-color: transparent;
  background: var(--danger);
  color: var(--accent-text);
}
.btn.danger:hover {
  background: color-mix(in oklab, var(--danger) 88%, black);
}

.btn.small {
  height: 30px;
  padding: 0 12px;
  font-size: var(--fs-sm);
}
.btn.block {
  width: 100%;
}
/* Isi tombol didorong ke dua ujung — label di kiri, keterangannya di kanan. */
.btn.spread {
  justify-content: space-between;
  gap: var(--sp-4);
}

.btn-link {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
}
.btn-link:hover {
  text-decoration: underline;
}
.btn-link.quiet {
  color: var(--text-3);
}
.btn-link.quiet:hover {
  color: var(--text);
}
.btn-link.danger {
  color: var(--danger);
}

.inline-form {
  display: inline;
}

/*
 * Legenda tanda — pengganti paragraf penjelasan di bawah tabel.
 *
 * Dipakai di halaman yang dibuka ratusan kali sehari, jadi ia harus bisa
 * DIPINDAI, bukan dibaca. Bentuknya deret pendek: contoh tandanya lebih dulu,
 * artinya menyusul — urutan itu yang membuat mata bisa mencocokkan tanda yang
 * baru dilihat di tabel tanpa membaca seluruh barisnya.
 *
 * `<ul>` dengan `aria-label`, bukan paragraf: bagi pembaca layar ini memang
 * daftar tanda, dan urutannya bisa dilompati kalau tidak dibutuhkan.
 */
.legenda {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-xs);
  color: var(--text-3);
}
.legenda li {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.legenda-tanda {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25em;
  font-family: var(--mono);
  font-size: var(--fs-micro);
  color: var(--text-2);
}

/* Contoh gradasi keparahan: empat titik, bukan empat kata. */
.legenda-warna {
  min-width: 34px;
  height: 8px;
  border-radius: 999px;
  background: linear-gradient(
    90deg,
    var(--sev-ringan) 0 25%,
    var(--sev-sedang) 25% 50%,
    var(--sev-berat) 50% 75%,
    var(--sev-fatal) 75% 100%
  );
}

/* Meniru tampilan kode yang sudah diputuskan, supaya cocoknya langsung terlihat. */
.legenda-redup {
  padding: 0 var(--sp-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  opacity: 0.45;
}

/* -- palet perintah (D134) --------------------------------------------- */

/*
 * Pemicu di topbar.
 *
 * Terlihat seperti kolom isian, bukan tombol — bentuk itu yang membuat orang
 * paham ia bisa mengetik, tanpa satu kata penjelasan. Pintasannya dicetak di
 * dalamnya supaya yang memakai tetikus hari ini belajar memakai keyboard besok.
 */
.palet-pemicu {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  height: 30px;
  padding: 0 var(--sp-2) 0 var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-3);
  font: inherit;
  font-size: var(--fs-xs);
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease, background 160ms ease;
}
.palet-pemicu:hover {
  border-color: var(--border-strong);
  background: var(--surface);
  color: var(--text-2);
}
.palet-pemicu .icon {
  width: 14px;
  height: 14px;
}
.palet-pemicu kbd {
  padding: 1px 5px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-family: var(--mono);
  font-size: var(--fs-micro);
  color: var(--text-3);
}

/* Di layar sempit tinggal ikonnya — labelnya yang pertama dikorbankan. */
@media (max-width: 900px) {
  .palet-pemicu-teks,
  .palet-pemicu kbd {
    display: none;
  }
  .palet-pemicu {
    padding: 0 var(--sp-2);
  }
}

.palet[hidden] {
  display: none;
}
.palet {
  position: fixed;
  inset: 0;
  z-index: 1000;
}
.palet-tirai {
  position: absolute;
  inset: 0;
  background: rgb(15 18 24 / 38%);
}

/*
 * Kotaknya diletakkan di sepertiga atas, bukan di tengah.
 *
 * Mata sudah berada di sana saat mengetik, dan daftar yang tumbuh ke bawah
 * tidak menggeser kolom isiannya — kotak yang terpusat sempurna akan bergerak
 * naik-turun setiap kali jumlah hasilnya berubah.
 */
.palet-kotak {
  position: relative;
  width: min(560px, calc(100vw - var(--sp-6)));
  margin: 12vh auto 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 24px 60px rgb(15 18 24 / 22%);
  overflow: hidden;
}

.palet-input {
  width: 100%;
  padding: var(--sp-4);
  border: 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: var(--fs-lg);
}
.palet-input:focus {
  outline: none;
}
.palet-input::placeholder {
  color: var(--text-3);
}

.palet-daftar {
  /* Kiblat bagi isinya (D113) — lihat pagar di tests/unit/css_kiblat.spec.ts. */
  position: relative;
  max-height: 46vh;
  margin: 0;
  padding: var(--sp-2);
  overflow-y: auto;
  list-style: none;
}
.palet-baris {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

/*
 * Penanda pilihan memakai LATAR, bukan hanya warna teks. Baris terpilih harus
 * terlihat saat mata sedang di kolom isian, bukan di daftarnya.
 */
.palet-baris.is-pilih {
  background: var(--surface-3);
}
.palet-label {
  color: var(--text);
  font-size: var(--fs-base);
}
.palet-label b {
  color: var(--accent);
  font-weight: 600;
}
.palet-bagian {
  flex: none;
  color: var(--text-3);
  font-size: var(--fs-micro);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.palet-kosong {
  padding: var(--sp-4);
  color: var(--text-3);
  font-size: var(--fs-sm);
  text-align: center;
}

.palet-kaki {
  display: flex;
  gap: var(--sp-4);
  padding: var(--sp-2) var(--sp-4);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-3);
  font-size: var(--fs-micro);
}
.palet-kaki kbd {
  margin-right: 2px;
  padding: 0 4px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface);
  font-family: var(--mono);
}

@media (prefers-reduced-motion: no-preference) {
  .palet-kotak {
    animation: palet-masuk 140ms ease-out;
  }
}
@keyframes palet-masuk {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
}

/* -- form -------------------------------------------------------------- */

.form-stack {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-3);
}
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
  gap: var(--sp-4);
}

.field {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.field.grow {
  flex: 1 1 200px;
}
.field.span-2 {
  /*
   * `1 / -1`, bukan `span 2` (D124).
   *
   * `span 2` MEMAKSA dua jalur ada. Di dalam grid `auto-fit` yang sempit,
   * jalur keduanya menyusut jadi 0px tapi celahnya tetap dihitung — terukur di
   * 375px: kotak isi 291px, kolomnya `291px 0px`, dan kolom yang merentang
   * keduanya jadi 311px. Meluber persis selebar satu celah.
   *
   * `1 / -1` merentang seluruh jalur yang BENAR-BENAR ada, berapa pun
   * jumlahnya.
   */
  grid-column: 1 / -1;
}

label {
  color: var(--text-2);
  font-size: var(--fs-xs);
  font-weight: 600;
}
.label-optional {
  margin-left: 4px;
  color: var(--text-3);
  font-weight: 500;
}

input[type='text'],
input[type='email'],
input[type='password'],
input[type='number'],
input[type='date'],
input[type='search'],
select,
textarea {
  width: 100%;
  min-width: 0;
  height: 36px;
  padding: 0 11px;
  /* Garis ini satu-satunya penanda bahwa kotak ini bisa diisi -- WCAG 1.4.11
     menuntut 3:1, dan `--border-strong` cuma 1,5:1 (D116). */
  border: 1px solid var(--border-control);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-base);
  transition: border-color 0.1s ease, box-shadow 0.12s ease;
}
textarea {
  height: auto;
  min-height: 88px;
  padding: 9px 11px;
  line-height: 1.55;
  resize: vertical;
}
textarea.mono {
  font-family: var(--mono);
  font-size: var(--fs-xs);
}
select {
  appearance: none;
  padding-right: 28px;
  background-image: linear-gradient(45deg, transparent 50%, currentcolor 50%),
    linear-gradient(135deg, currentcolor 50%, transparent 50%);
  background-position:
    calc(100% - 15px) 16px,
    calc(100% - 10px) 16px;
  background-size:
    5px 5px,
    5px 5px;
  background-repeat: no-repeat;
}

input:hover,
select:hover,
textarea:hover {
  border-color: var(--text-3);
}
/*
 * Fokus kolom isian: garis aksen plus halo lunak. Halo-nya bagian dari
 * penanda, bukan hiasan — garis satu piksel saja terlalu mudah terlewat pada
 * formulir yang panjang.
 */
input:focus,
select:focus,
textarea:focus {
  border-color: var(--accent);
  outline: none;
  box-shadow: var(--ring);
}
/* Windows kontras tinggi membuang box-shadow; outline-nya dikembalikan. */
@media (forced-colors: active) {
  input:focus,
  select:focus,
  textarea:focus {
    outline: 2px solid Highlight;
    outline-offset: 1px;
  }
}
input::placeholder,
textarea::placeholder {
  color: var(--text-3);
  opacity: 0.75;
}
input:disabled,
select:disabled,
textarea:disabled {
  background: var(--surface-2);
  color: var(--text-3);
  cursor: not-allowed;
}

.hint {
  color: var(--text-3);
  font-size: var(--fs-xs);
  line-height: 1.45;
}

/* Kolom yang ditolak: garis merah plus teks — bukan warna saja. */
input[aria-invalid='true'],
select[aria-invalid='true'],
textarea[aria-invalid='true'] {
  border-color: var(--danger);
}
.field-error {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  color: var(--danger);
  font-size: var(--fs-xs);
  font-weight: 500;
}

.check,
.check-list label {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  color: var(--text-2);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: 1.45;
  cursor: pointer;
}
.check input[type='checkbox'],
.check input[type='radio'],
.check-list input {
  width: 15px;
  height: 15px;
  margin: 2px 0 0;
  accent-color: var(--accent);
  flex: 0 0 auto;
  cursor: pointer;
}
.check-inline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}
.check-list {
  display: flex;
  flex-direction: column;
  gap: 7px;
  max-height: 260px;
  overflow-y: auto;
}

fieldset {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
legend {
  padding: 0 0 var(--sp-2);
  color: var(--text-2);
  font-size: var(--fs-xs);
  font-weight: 600;
}

/* -- alert ------------------------------------------------------------- */

/*
 * Alert: latar bertinta tipis, satu batang berwarna sebagai pembawa arti, dan
 * kalimatnya dalam tinta biasa. Seluruh paragraf berwarna merah terbaca
 * sebagai teriakan — yang perlu berwarna hanya penandanya.
 */
.alert {
  --tone: var(--text-3);
  display: flex;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--text);
  font-size: var(--fs-base);
}
.alert::before {
  content: '';
  flex: 0 0 3px;
  border-radius: 2px;
  background: var(--tone);
}
.alert.success {
  --tone: var(--ok);
  background: var(--ok-soft);
}
.alert.warning {
  --tone: var(--warn);
  background: var(--warn-soft);
}
.alert.error {
  --tone: var(--danger);
  background: var(--danger-soft);
}
.alert.info {
  --tone: var(--info);
  background: var(--info-soft);
}
.alert > div {
  min-width: 0;
  flex: 1;
}
.alert strong {
  font-weight: 650;
}
.alert-dismiss {
  align-self: flex-start;
  padding: 0 4px;
  border: 0;
  background: none;
  color: inherit;
  font-size: var(--fs-lg);
  line-height: 1;
  opacity: 0.6;
  cursor: pointer;
}
.alert-dismiss:hover {
  opacity: 1;
}

.error-list {
  margin: 6px 0 0;
  padding-left: 18px;
}
.error-list li {
  margin-top: 2px;
}

/* -- meter / progres --------------------------------------------------- */

.meter {
  position: relative;
  width: 100%;
  min-width: 90px;
  height: 6px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-3);
}
/* Ujung data membulat, pangkal siku — batang tumbuh dari garis dasar. */
.meter > span {
  display: block;
  height: 100%;
  border-radius: 0 999px 999px 0;
  background: linear-gradient(90deg, color-mix(in oklab, var(--accent) 82%, var(--surface)), var(--accent));
  transition: width 0.25s ease;
}
.meter.over > span,
.meter.exceeded > span {
  background: var(--danger);
}
.meter.ok > span {
  background: var(--ok);
}
.meter-label {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-2);
  margin-bottom: 4px;
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}

/* -- paginasi ---------------------------------------------------------- */

.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  margin-top: var(--sp-4);
}
.pagination a,
.pagination span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--text-2);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.pagination a:hover {
  background: var(--surface-2);
  color: var(--text);
  text-decoration: none;
}
.pagination .current {
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}
.pagination .gap {
  border-color: transparent;
  color: var(--text-3);
}

/* -- daftar definisi --------------------------------------------------- */

.deflist {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 7px var(--sp-4);
  margin: 0;
  font-size: var(--fs-base);
}
.deflist dt {
  color: var(--text-3);
  font-size: var(--fs-xs);
}
.deflist dd {
  margin: 0;
  min-width: 0;
  word-break: break-word;
}

/* -- blok kode --------------------------------------------------------- */

.code-block {
  margin: 0;
  padding: var(--sp-3);
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-2);
  font-family: var(--mono);
  font-size: var(--fs-xs);
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Pratinjau config JSON satu baris di sel tabel — detail lengkap di title. */
.config-preview {
  display: inline-block;
  max-width: 340px;
  overflow: hidden;
  padding: 2px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--text-3);
  font-family: var(--mono);
  font-size: var(--fs-xs);
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

/* -- disclosure -------------------------------------------------------- */

.disclosure {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.disclosure > summary {
  padding: 10px var(--sp-4);
  border-radius: var(--radius);
  color: var(--text-2);
  font-size: var(--fs-base);
  font-weight: 500;
  cursor: pointer;
  list-style: none;
}
.disclosure > summary:hover {
  color: var(--text);
}
.disclosure > summary::-webkit-details-marker {
  display: none;
}
.disclosure > summary::before {
  content: '▸';
  display: inline-block;
  width: 14px;
  color: var(--text-3);
}
.disclosure[open] > summary {
  color: var(--text);
}
.disclosure[open] > summary::before {
  content: '▾';
}
.disclosure-body {
  padding: 0 var(--sp-4) var(--sp-4);
}

/* -- transkrip --------------------------------------------------------- */

/*
 * Transkrip dibaca sebagai percakapan, bukan tabel log: member di kiri, CS di
 * kanan — susunan yang sama dengan aplikasi chat mana pun, jadi auditor tahu
 * siapa bicara tanpa membaca labelnya lebih dulu. Pesan sistem berdiri di
 * tengah karena ia bukan pihak yang bicara.
 *
 * Teks di dalam gelembung TIDAK pernah rata kanan, seberapa pun sisinya —
 * paragraf rata kanan menyulitkan mata menemukan awal baris berikutnya.
 */
/*
 * Transkrip duduk di BIDANG BACA, bukan di permukaan kendali (D116).
 *
 * Inilah satu-satunya tempat di aplikasi yang dibaca berjam-jam. Membedakan
 * materialnya dari panel putusan di sebelahnya membuat mata tahu ia sedang
 * membaca, bukan sedang mengoperasikan.
 */
.transcript {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-baca);
}

/*
 * Rel senyap (D116) — elemen tanda antarmuka ini.
 *
 * Hampir setiap aturan di rubrik mengukur JEDA: telat respon, tidak respon,
 * cek lebih 12/20 menit, cek tidak berujung. Yang sebenarnya dibaca auditor
 * adalah durasi — tapi sampai sekarang durasi cuma label abu-abu setinggi
 * 12 piksel, sama besarnya untuk 20 detik maupun 20 menit.
 *
 * Di sini jeda digambar sebagai PANJANG. Percakapan yang ditinggalkan
 * terlihat sebelum satu kata pun dibaca.
 *
 * Tingginya akar kuadrat detik, bukan logaritma: log memampatkan terlalu jauh
 * — 24 detik dan 6 menit jadi nyaris sama tinggi, padahal justru selisih itu
 * yang harus terlihat. Dibatasi 168px supaya satu jeda panjang tidak mendorong
 * seluruh percakapan keluar layar.
 */
.rel-senyap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 2px 0;
}
.rel-senyap::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  border-radius: 2px;
  background: var(--border-strong);
}
.rel-senyap > span {
  position: relative;
  z-index: 1;
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-baca);
  color: var(--text-3);
  font-family: var(--mono);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  /* Membungkus akan menggeser relnya dan merusak justru yang mau dibuktikan. */
  white-space: nowrap;
}
/* Melewati ambang: warna DAN tebal, supaya tidak bergantung warna saja. */
.rel-senyap.lewat::before {
  background: var(--warn);
}
.rel-senyap.lewat > span {
  color: var(--warn);
  font-weight: 600;
}
.rel-senyap.parah::before {
  background: var(--danger);
}
.rel-senyap.parah > span {
  color: var(--danger);
  font-weight: 600;
}
/*
 * Satu giliran bicara: avatar + nama + gelembung (D77).
 *
 * Bentuknya mengikuti arsip LiveChat yang dipakai supervisor sehari-hari —
 * auditor membandingkan dua layar berdampingan, dan dua tata letak berbeda
 * untuk percakapan yang sama membuat pekerjaan itu lebih berat daripada
 * perlunya.
 *
 * Yang TIDAK ikut ditiru: LiveChat menyembunyikan jam per pesan. Di sini jam
 * dan `jeda` adalah buktinya — seluruh kategori DURASI berdiri di atas angka
 * itu. Yang dilakukan hanya menurunkan bobotnya, bukan membuangnya.
 */
.turn {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  max-width: min(82%, 760px);
}
.turn.customer {
  align-self: flex-start;
}
.turn.agent {
  align-self: flex-end;
  flex-direction: row-reverse;
}
/* Pesan sistem bukan salah satu pihak, jadi tidak bergelembung dan tanpa avatar. */
.turn.system {
  align-self: center;
  max-width: 100%;
}
.turn.system .avatar {
  display: none;
}
.turn-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.turn.agent .turn-main {
  align-items: flex-end;
}
.turn.system .turn-main {
  align-items: center;
  width: 100%;
}
.avatar {
  display: grid;
  flex: 0 0 auto;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0;
  text-transform: uppercase;
}
.turn.customer .avatar {
  background: var(--surface-3);
  color: var(--text-2);
}
.turn.agent .avatar {
  background: var(--accent);
  color: #fff;
}
/* Nama pengirim di atas gelembung, seperti di arsip LiveChat. */
.turn-who {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  padding: 0 3px;
  color: var(--text-3);
  font-size: var(--fs-micro);
}
.turn-name {
  font-weight: 650;
  color: var(--text-2);
}
.turn .msg {
  max-width: 100%;
}

.msg {
  max-width: min(78%, 720px);
  padding: 8px 11px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--surface-2);
  transition: border-color 0.12s ease;
  /*
   * Tautan "lihat bukti" melompat ke pesan ini. Tanpa jarak ini pesannya
   * mendarat PERSIS di bawah topbar dan bar percakapan yang dua-duanya
   * lengket — jadi yang dituju justru tertutup oleh yang menutupinya.
   */
  scroll-margin-top: calc(var(--topbar-h) + 96px);
}
/* Sudut yang "menempel" ke sisinya — penanda arah tanpa ekor gelembung. */
.msg.customer {
  align-self: flex-start;
  border-bottom-left-radius: 4px;
}
.msg.agent {
  align-self: flex-end;
  border-bottom-right-radius: 4px;
  background: var(--accent-soft);
}
.msg.agent .msg-meta {
  justify-content: flex-end;
}
/* Pesan sistem: bukan salah satu pihak, jadi tidak bergelembung. */
.msg.system {
  align-self: center;
  max-width: 100%;
  padding: 2px 8px;
  background: transparent;
  text-align: center;
}
.msg.system .msg-meta {
  justify-content: center;
}
/*
 * Hover menandai baris tanpa mengubah warna dasarnya — latar gelembung sudah
 * memikul arti (siapa yang bicara), jadi menimpanya justru menghapus makna.
 */
.msg:hover {
  border-color: var(--border-strong);
}
.msg-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}
.msg-role {
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.msg.agent .msg-role {
  color: var(--accent);
}
.msg.customer .msg-role {
  color: var(--text-2);
}
.msg.system .msg-role {
  color: var(--text-3);
}
.msg-text {
  margin-top: 3px;
  font-size: var(--fs-base);
  white-space: pre-wrap;
  word-break: break-word;
}
.msg.system .msg-text {
  color: var(--text-3);
  font-style: italic;
}
/*
 * Lampiran. Kotaknya sengaja terlihat berbeda dari gelembung pesan: sebuah
 * gambar bukti transfer adalah benda, bukan kalimat, dan auditor harus tahu
 * ada sesuatu yang bisa dibuka.
 */
.attachment {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin-top: 4px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.attachment-icon {
  display: grid;
  place-items: center;
  min-width: 34px;
  height: 26px;
  padding: 0 6px;
  border-radius: 5px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.04em;
}
.attachment-body {
  min-width: 0;
}
.attachment-name {
  font-size: var(--fs-sm);
  font-weight: 600;
  word-break: break-word;
}

/*
 * Lampiran gambar ditampilkan langsung. Bukti transfer adalah barang bukti
 * utama kategori BUKTI_TRANSFER — memaksa auditor membuka tab baru berarti ia
 * menilai sambil kehilangan transkripnya.
 *
 * Perbesarnya memakai checkbox tersembunyi, bukan JavaScript: pratinjau harus
 * tetap bekerja di halaman mana pun yang memakai partial ini.
 */
.attachment-figure {
  /* Menyusut mengikuti isinya — keterangan tidak melebar sendirian melintasi
     seluruh gelembung pesan saat gambarnya sempit. */
  display: inline-block;
  max-width: 100%;
  margin: 6px 0 0;
}
.attachment-zoom {
  display: inline-block;
  max-width: 100%;
  cursor: zoom-in;
}
.attachment-image {
  display: block;
  max-width: 100%;
  max-height: 340px;
  width: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  transition: max-height 0.18s var(--ease-out);
}
.attachment-zoom-input:checked + .attachment-figure .attachment-zoom {
  cursor: zoom-out;
}
.attachment-zoom-input:checked + .attachment-figure .attachment-image {
  max-height: 78vh;
}
.attachment-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  max-width: 520px;
  margin-top: 5px;
  font-size: var(--fs-xs);
}
.attachment-caption .attachment-name {
  font-size: var(--fs-xs);
}
/* Petunjuk perbesar: kursor `zoom-in` saja tidak cukup terlihat. */
.attachment-hint {
  color: var(--text-3);
}
.attachment-zoom-input:checked + .attachment-figure .attachment-hint::after {
  content: 'klik gambar untuk mengecilkan';
}
.attachment-zoom-input:not(:checked) + .attachment-figure .attachment-hint::after {
  content: 'klik gambar untuk memperbesar';
}

/* Berkas kedaluwarsa di CDN LiveChat atau sudah dihapus retensi. */
.attachment-fallback {
  display: none;
  padding: 10px 12px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-3);
  font-size: var(--fs-xs);
}
.attachment-figure.is-broken .attachment-zoom {
  display: none;
}
.attachment-figure.is-broken .attachment-fallback {
  display: block;
}

/* Jawaban formulir pra-chat — user ID member biasanya ada di sini. */
.form-answers {
  margin-top: 4px;
  padding: 8px 10px;
  border-left: 2px solid var(--border-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--surface-2);
}
.form-answers-head {
  margin-bottom: 4px;
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/*
 * Pesan yang menjadi bukti temuan. Ditandai CINCIN, bukan garis kiri: separuh
 * gelembung kini berada di kanan, dan garis di sisi kiri tidak terbaca di sana.
 */
.msg.flagged {
  border-color: var(--danger);
  background: var(--danger-soft);
}
.msg.flagged:hover {
  border-color: var(--danger);
}

/*
 * Pita kode aturan di atas pesan yang dituduh. Sorotan saja hanya mengatakan
 * BAHWA pesan ini bermasalah; auditor perlu tahu aturan mana — satu pesan bisa
 * memicu lebih dari satu.
 */
.msg-flag {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 5px;
}
.msg-flag span {
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: var(--surface);
  font-size: var(--fs-micro);
  font-weight: 700;
  letter-spacing: 0.02em;
}
/*
 * Pesan yang temuannya sudah diputus. Tetap ditandai — auditor perlu tahu di
 * mana keputusan itu jatuh, dan keputusannya bisa dibalik — tapi tanpa merah.
 * Cincin merah pada sesuatu yang sudah dinyatakan aman mengatakan hal yang
 * salah, dan itu persis yang membuat sebuah temuan `aman` dikira masih
 * terdeteksi.
 */
.msg.flagged-done {
  border-color: var(--border-strong);
  border-style: dashed;
}
.msg.flagged-done .msg-flag span {
  background: transparent;
  border: 1px dashed var(--border-strong);
  color: var(--text-3);
}
/*
 * Pesan yang baru dilompati dari panel keputusan. Denyut sekali lalu diam —
 * pada transkrip 19 pesan yang separuhnya sudah bercincin merah, sorotan
 * permanen tidak memberi tahu mana yang BARU SAJA dituju.
 */
.msg:target {
  animation: msg-tuju 1.4s ease-out 1;
}
@keyframes msg-tuju {
  0%,
  40% {
    box-shadow: 0 0 0 3px var(--danger);
  }
  100% {
    box-shadow: 0 0 0 3px transparent;
  }
}
@media (prefers-reduced-motion: reduce) {
  .msg:target {
    animation: none;
    box-shadow: 0 0 0 3px var(--danger);
  }
}
/* Jeda balasan melewati ambang. */
.msg .gap-over {
  color: var(--danger);
  font-weight: 600;
}

/* -- antrean dugaan di samping percakapan ------------------------------ */

/*
 * Dua tombol yang boleh bertumpuk (D116).
 *
 * Sebelumnya `grid-template-columns: 1fr 1fr`. `1fr` punya `min-width: auto`,
 * jadi jalurnya TIDAK BOLEH menyusut di bawah lebar isinya — dan begitu
 * isinya lebih lebar dari separuh kolom, navnya meluber keluar diam-diam.
 * Terukur waktu tangga huruf dirapatkan: nav 206px di dalam kolom yang tepi
 * kanannya 491, tombol kedua berakhir di 505.
 *
 * Flex dengan `flex: 1 1 96px` menyelesaikan dua-duanya: berdampingan kalau
 * muat, bertumpuk kalau tidak — tanpa memotong kata "Berikutnya".
 */
.queue-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding: var(--sp-3);
}
.queue-nav > * {
  flex: 1 1 96px;
  min-width: 0;
  justify-content: center;
  text-align: center;
}
/* Ujung antrean memakai `.is-disabled` — tombolnya tetap ada supaya tata
   letaknya tidak melompat. Aturannya ada di bagian utilitas. */

/*
 * Daftar digulir di dalam kartunya sendiri. Panel samping sudah lengket dan
 * punya batas tinggi; tanpa batas kedua di sini, antrean panjang mendorong
 * panel keputusan keluar layar — padahal itu tujuan halaman ini dibuka.
 */
/*
 * Seluruh antrean tampil di sini (D79), seperti daftar Archives di LiveChat.
 * Tingginya mengikuti kolom; `max-height` hanya berlaku di luar meja kerja.
 */
.queue-list {
  display: flex;
  flex-direction: column;
  max-height: 420px;
  overflow-y: auto;
  overscroll-behavior: contain;
  border-top: 1px solid var(--border);
}
.queue-item {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-bottom: 1px solid var(--border);
  color: var(--text-2);
}
.queue-item:last-child {
  border-bottom: 0;
}
.queue-item:hover {
  background: var(--surface-2);
  color: var(--text);
  text-decoration: none;
}
.queue-item.is-active {
  position: relative;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}
.queue-item.is-active::before {
  content: '';
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  width: 3px;
  border-radius: 0 2px 2px 0;
  background: var(--accent);
}
.queue-num {
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.queue-item.is-active .queue-num {
  color: inherit;
}
.queue-main {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/*
 * Di dalam meja kerja, transkrip tidak lagi punya kotak gulir sendiri:
 * kolomnya sudah bergulir. Dua kotak gulir bersarang berarti roda mouse
 * berhenti di tengah tanpa alasan yang terlihat.
 */
.workbench .transcript.scrollable {
  max-height: none;
  overflow: visible;
}

/* -- meja kerja setinggi layar ----------------------------------------- */

/*
 * Halaman percakapan tidak bergulir; kolom-kolom di dalamnyalah yang bergulir.
 *
 * Bentuk sebelumnya bergulir sebagai satu lembar: menggulir transkrip ikut
 * menyeret remah roti, kepala percakapan, antrean, dan panel keputusan.
 * `position: sticky` pada kolom kiri-kanan tidak menyelesaikannya — lengket
 * hanya menahan SETELAH halamannya bergerak, bukan mencegahnya bergerak.
 *
 * Tingginya dihitung, bukan ditebak: satu layar dikurangi topbar dan padding
 * atas `.content`. Padding bawah `.content` ditarik balik karena ruang itu
 * disediakan untuk halaman yang menggulir — di sini ia justru satu-satunya
 * penyebab halaman ini masih bisa digulir.
 *
 * `100dvh`, bukan `100vh`: di peramban seluler bilah alamat yang menciut
 * membuat `100vh` melebihi layar yang benar-benar terlihat.
 */
.workbench {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  height: calc(
    100dvh - var(--topbar-h) - var(--footer-h) - clamp(24px, 2.4vw, 36px) - var(--sp-4)
  );
  min-height: 520px;
  margin-bottom: calc(var(--sp-4) - var(--sp-12));
}
.workbench > .breadcrumb {
  flex: 0 0 auto;
  margin-bottom: 0;
}
.workbench > .layout-3col,
.workbench > .layout-2col {
  flex: 1 1 auto;
  min-height: 0;
}

/*
 * Dua kolom di dalam meja kerja: keduanya bergulir sendiri, halaman diam.
 * `overscroll-behavior: contain` menghentikan guliran di ujung transkrip
 * supaya tidak melanjut ke halaman di belakangnya.
 */
.workbench > .layout-2col {
  grid-template-rows: minmax(0, 1fr);
  align-items: stretch;
}
.workbench > .layout-2col > * {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/*
 * Di dalam meja kerja kolom ini bergulir sendiri, jadi `sticky` dari aturan
 * umum tidak diperlukan. Dibatalkan pakai `relative`, bukan `static`: `static`
 * mencabut kolom ini dari daftar kiblat, dan lubang D113 yang sama terbuka lagi
 * khusus di sini.
 *
 * `top: auto` wajib menyertainya. Aturan umum memberi `top: calc(var(--topbar-h)
 * + var(--sp-4))` = 72px untuk keperluan sticky; `static` mengabaikan offset itu,
 * `relative` MEMATUHINYA. Tanpa dikembalikan ke auto, kolom ini turun 72px dan
 * meluber sebanyak itu ke bawah -- persis jebol yang hendak diperbaiki.
 */
.workbench > .layout-2col > .col-side {
  position: relative;
  top: auto;
  max-height: none;
}
/* Lompatan jangkar terjadi di dalam kolomnya, bukan di halaman. */
.workbench .msg {
  scroll-margin-top: var(--sp-4);
}

@media (max-width: 1080px) {
  /*
   * Di bawah titik ini kolom tidak bergulir sendiri lagi, jadi tinggi tetap
   * meja kerja kehilangan artinya -- isinya mengalir sepanjang apa pun. Kalau
   * tingginya dibiarkan terkunci, isi meluber keluar kotak sementara kaki
   * halaman tetap dipasang setinggi kotak: kaki terdampar di tengah dengan isi
   * tercecer di bawahnya.
   *
   * Terukur di 1024x768 pada halaman detail temuan: halaman setinggi 1434px,
   * kaki di 767px, 667px ruang mati sesudahnya.
   */
  .workbench {
    height: auto;
    min-height: 0;
    margin-bottom: 0;
  }
  .workbench > .layout-2col > * {
    overflow: visible;
  }
}

/* -- tab bagian (D124) ------------------------------------------------- */

/*
 * Pembagian tugas navigasi: sidebar mengatakan Anda ada di BAGIAN mana, tab ini
 * mengatakan apa ISI bagian itu.
 *
 * Bentuknya sengaja berbeda dari `.bubble` yang dipakai menyaring isi halaman
 * (status temuan, jenis kamus). Dua kendali yang bentuknya sama tapi akibatnya
 * berbeda -- satu berpindah halaman, satu menyaring daftar -- adalah sumber
 * kebingungan yang persis sedang diperbaiki. Tab berpindah halaman: ia duduk di
 * atas, digarisbawahi, dan tidak pernah berbentuk pil.
 */
.section-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1);
  margin-bottom: var(--sp-6);
  border-bottom: 1px solid var(--border);
}
.section-tab {
  padding: 10px var(--sp-3);
  border-bottom: 2px solid transparent;
  color: var(--text-3);
  font-size: var(--fs-base);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition:
    color 0.14s var(--ease-out),
    border-color 0.14s var(--ease-out);
  /* Garis bawahnya menindih garis wadah, bukan menambah tinggi barisnya. */
  margin-bottom: -1px;
}
.section-tab:hover {
  color: var(--text);
}
.section-tab.is-active {
  border-bottom-color: var(--accent);
  color: var(--text);
  font-weight: 600;
}

@media (max-width: 560px) {
  /*
   * Di ponsel tabnya menggulir mendatar alih-alih bertumpuk: enam tab yang
   * bertumpuk memakan seluruh layar sebelum isinya sempat terlihat.
   */
  .section-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
}

/* -- formulir aturan cara mudah (D119) --------------------------------- */

/*
 * Kartu pilihan resep. Radio-nya disembunyikan dan SELURUH kartu jadi sasaran
 * klik: sasaran sebesar itu yang membuat pilihan terasa jelas, bukan titik
 * radio selebar 16px di pojok.
 *
 * Fokus keyboard tetap terlihat lewat `:focus-visible` pada radionya, yang
 * diteruskan ke kartu — menyembunyikan kontrol tidak boleh berarti
 * menyembunyikan fokusnya.
 */
.resep-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}
.resep-kartu {
  position: relative;
  display: block;
  padding: var(--sp-4);
  border: 1px solid var(--border-control);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
  transition:
    border-color 0.14s var(--ease-out),
    background 0.14s var(--ease-out);
}
.resep-kartu:hover {
  border-color: var(--accent);
}
.resep-kartu input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}
.resep-kartu:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.resep-kartu:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.resep-isi {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.resep-judul {
  color: var(--text);
  font-size: var(--fs-base);
  line-height: 1.35;
}
.resep-ringkas {
  color: var(--text-2);
  font-size: var(--fs-sm);
}
.resep-contoh {
  color: var(--text-3);
  font-size: var(--fs-sm);
  font-style: italic;
}

/* Angka dengan satuannya, supaya "60" tidak pernah berdiri tanpa "detik". */
.input-satuan {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.input-satuan input {
  max-width: 140px;
}
.input-satuan .satuan {
  color: var(--text-3);
  font-size: var(--fs-sm);
}

/* -- halaman referensi sistem desain (D116) ---------------------------- */

/*
 * Dipakai HANYA oleh `/design`. Ditaruh di sini, bukan di berkas terpisah,
 * karena seluruh nilai yang dipamerkannya harus datang dari lembar gaya yang
 * sama dengan aplikasi — itu satu-satunya alasan halaman itu tidak bisa
 * menyimpang dari kenyataan.
 */
.swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(170px, 100%), 1fr));
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}
.swatch {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  font-size: var(--fs-sm);
}
.swatch-isi {
  height: 52px;
  margin-bottom: var(--sp-1);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.swatch code {
  overflow-wrap: anywhere;
}

.deret-chip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
  margin-top: var(--sp-3);
}

.deret-jarak {
  display: grid;
  grid-template-columns: max-content max-content;
  gap: var(--sp-2) var(--sp-3);
  align-items: center;
  margin-top: var(--sp-4);
}
.jarak-bar {
  height: 12px;
  border-radius: 2px;
  background: var(--accent);
}

/* -- bidang perubahan SystemLog ---------------------------------------- */

/*
 * Satu baris SystemLog kerap memuat beberapa perubahan sekaligus (D87).
 * Ditampilkan mentah, ketiganya jadi satu blok panjang yang harus dibaca kata
 * per kata. Di sini tiap bidang jadi barisnya sendiri: nama bidang, nilai
 * lama, panah, nilai baru.
 *
 * Nilai lama diberi coretan dan diredupkan — arah perubahannya harus terbaca
 * tanpa membaca panahnya.
 */
.ubah-list {
  display: grid;
  gap: 3px;
  min-width: 0;
}
.ubah-baris {
  display: grid;
  grid-template-columns: minmax(96px, auto) minmax(0, 1fr);
  align-items: baseline;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
}
.ubah-bidang {
  color: var(--text-3);
  font-weight: 600;
  white-space: nowrap;
}
.ubah-nilai {
  min-width: 0;
  font-family: var(--mono);
  word-break: break-word;
  /* Isinya sendiri boleh berpindah baris; tanpa ini panah dan nilai lama
     saling mendorong keluar wadah (terukur 11px di 1280px). */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px;
}
.ubah-dari {
  color: var(--text-3);
  text-decoration: line-through;
  text-decoration-color: color-mix(in oklab, var(--text-3) 55%, transparent);
}
.ubah-panah {
  margin: 0 4px;
  color: var(--text-3);
}
.ubah-ke {
  color: var(--text);
  font-weight: 550;
}
/* Bentuk `Tambah Pemain` tidak punya "sebelum" — nilainya berdiri sendiri. */
.ubah-baru .ubah-ke {
  font-weight: 500;
}
/*
 * Bidang yang tidak pernah dinilai auditor diredupkan seluruhnya: warna
 * pemain dan grup ikut terbawa di banyak baris, dan membiarkannya sama
 * mencoloknya dengan perubahan rekening membuat yang penting tenggelam.
 */
.ubah-baris.sepele .ubah-bidang,
.ubah-baris.sepele .ubah-nilai,
.ubah-baris.sepele .ubah-ke {
  color: var(--text-3);
  font-weight: 400;
}

/* -- panel detail ------------------------------------------------------ */

/*
 * Bentuknya mengikuti panel "Details" LiveChat (D81): kelompok bertajuk, tiap
 * baris satu pasang label/nilai dengan nilainya rata kanan.
 *
 * Rata kanan itu bukan hiasan — nilainya yang dicari mata, dan pada delapan
 * baris berturut-turut satu tepi yang lurus jauh lebih cepat dipindai
 * daripada delapan awalan yang berpindah-pindah mengikuti panjang label.
 */
.detail-list {
  margin: 0;
}
.detail-group + .detail-group {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
.detail-title {
  margin-bottom: 3px;
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.detail-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 2px 0;
  font-size: var(--fs-xs);
}
/* Bentuk rapat: tanpa tajuk kelompok, barisnya menempel (D82). */
.detail-list.rapat .detail-row {
  padding: 1px 0;
}

/*
 * Blok bawah kolom keputusan menempel ke dasar layar, bukan ke dasar isi
 * (D82). Begitu kolomnya lebih panjang dari layar — percakapan dengan
 * beberapa temuan — "Berikutnya" ikut tergulir keluar, padahal ia justru
 * tombol yang ditekan tepat setelah keputusan terakhir dibuat.
 */
.layout-3col > .col-side > .col-side-bawah,
.workbench > .layout-2col > .col-side > .col-side-bawah {
  position: sticky;
  bottom: 0;
  padding-top: var(--sp-2);
  background: var(--canvas);
}
.detail-row dt {
  flex: 0 0 auto;
  color: var(--text-3);
}
.detail-row dd {
  min-width: 0;
  margin: 0;
  color: var(--text);
  text-align: right;
  word-break: break-word;
}

/* -- kartu keputusan --------------------------------------------------- */

/*
 * Kolom kanan adalah PEKERJAANNYA, bukan ringkasannya. Sebelumnya isinya
 * barisan chip; memutuskan satu temuan berarti meninggalkan halaman dan
 * kehilangan posisi transkrip. Sekarang tiap temuan punya kartunya sendiri
 * dengan tombol keputusan di tempat.
 */
.verdict {
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
.verdict + .verdict {
  margin-top: var(--sp-2);
}
/*
 * Yang masih menunggu diberi tepi kiri tebal. Temuan yang sudah diputus tetap
 * tampil — auditor harus bisa membalik keputusannya — tapi tidak boleh ikut
 * meminta perhatian.
 */
.verdict.is-waiting {
  border-left: 3px solid var(--accent);
}
.verdict:not(.is-waiting) {
  background: var(--surface-2);
}
.verdict-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}
.verdict-head a {
  font-size: var(--fs-sm);
  font-weight: 600;
}
.verdict-name {
  margin: 3px 0 0;
  color: var(--text-2);
  font-size: var(--fs-xs);
  line-height: 1.4;
}
.verdict-jump {
  display: inline-block;
  margin-top: 3px;
  font-size: var(--fs-micro);
  font-weight: 500;
}
.verdict-jump::after {
  content: ' ↓';
}
.verdict-jump.is-none {
  color: var(--text-3);
  cursor: help;
}
.verdict-jump.is-none::after {
  content: '';
}
.verdict-act {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: var(--sp-2);
  padding-top: var(--sp-2);
  border-top: 1px dashed var(--border);
}
.verdict-act input[type='text'] {
  height: 26px;
  padding: 0 8px;
  font-size: var(--fs-xs);
}
.verdict-act .decide button {
  padding: 2px 6px;
}
.verdict-act .decide {
  justify-content: space-between;
}
.verdict-act .decide button {
  flex: 1;
}

/* Angka bukti di kepala transkrip — merah tanpa berteriak. */
.faint-danger {
  color: var(--danger);
}

/*
 * "ubah" di halaman hasil audit: tombol keputusan tetap ada, satu klik lebih
 * jauh. Halaman hasil menampilkan keputusan; menyodorkan tiga tombol pada
 * setiap baris membuatnya terbaca sebagai pekerjaan yang belum selesai.
 */
.ubah > summary {
  display: inline-block;
  margin-top: 4px;
  color: var(--text-3);
  font-size: var(--fs-xs);
  cursor: pointer;
  list-style: none;
}
.ubah > summary::-webkit-details-marker {
  display: none;
}
.ubah > summary:hover {
  color: var(--accent);
  text-decoration: underline;
}
.ubah[open] > summary {
  color: var(--accent);
}

/*
 * Penjelasan sebagai satu tanda seru (D79).
 *
 * Diminta supervisor: "untuk penjelasan cukup tanda seru saja." Paragraf
 * penjelas dibaca sekali seumur hidup lalu jadi hiasan yang mendorong
 * pekerjaan ke bawah — tapi menghapusnya berarti membuang alasan yang kadang
 * memang perlu dicari lagi.
 *
 * Tanda seru menyimpannya di `title`: tidak memakan ruang, tetap bisa dibuka.
 * `tabindex` supaya bisa dijangkau papan ketik, bukan cuma tetikus.
 */
.info-dot {
  display: inline-grid;
  place-items: center;
  width: 15px;
  height: 15px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: transparent;
  color: var(--text-3);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  vertical-align: middle;
  user-select: none;
}
.info-dot:hover,
.info-dot:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
  outline: none;
}

/*
 * Isinya muncul di sini saat tanda serunya diklik (D80). Diposisikan lewat
 * `left`/`top` yang disetel skrip, jadi `margin: 0` wajib — bawaan popover
 * memusatkannya dengan `margin: auto`.
 */
.info-pop {
  position: fixed;
  max-width: 320px;
  margin: 0;
  padding: 9px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-2);
  font-size: var(--fs-xs);
  line-height: 1.5;
  box-shadow: var(--shadow);
}
.info-pop::backdrop {
  background: transparent;
}

/* -- huruf kapital ----------------------------------------------------- */

/*
 * Usulan kata: kisi rapat, bukan tabel. Yang dikerjakan di sini adalah memilih
 * dua puluhan kata sekaligus, dan satu baris per kata memaksa menggulir untuk
 * pekerjaan yang seharusnya selesai dalam satu pandangan.
 */
.pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr));
  gap: 6px;
}
.pick {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.pick:hover {
  border-color: var(--border-strong);
}
.pick:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.pick input {
  flex: 0 0 auto;
  margin: 0;
}
.pick .mono {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  font-size: var(--fs-xs);
  text-overflow: ellipsis;
}

/*
 * Daftar yang berlaku sebagai dinding pil. Pertanyaannya selalu "apakah X
 * sudah ada", bukan "bacakan semuanya" — dan pilnya sekaligus tombol
 * nonaktif, jadi tidak perlu kolom aksi tersendiri.
 */
.chip-wall {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}
.word {
  padding: 3px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-2);
  font-family: var(--mono);
  font-size: var(--fs-xs);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease;
}
.word:hover {
  border-color: var(--danger);
  color: var(--danger);
}
.word.wide {
  border-radius: var(--radius-sm);
}
.word.is-off {
  border-style: dashed;
  background: transparent;
  color: var(--text-3);
  text-decoration: line-through;
}
.word.is-off:hover {
  border-color: var(--ok);
  color: var(--ok);
  text-decoration: none;
}
/* Titik kecil penanda entri yang berlaku untuk semua situs. */
.word-scope {
  color: var(--accent);
  font-weight: 700;
}

/* -- kalimat aman ------------------------------------------------------ */

/*
 * Satu kartu per kalimat, bukan satu baris tabel.
 *
 * Isi kamus ini adalah template beberapa paragraf — isi terpanjang di seluruh
 * aplikasi. Dipaksa masuk sel tabel ia jadi satu baris yang terpotong di tepi
 * layar: tidak terbaca, dan kolom lain terdorong keluar sampai hilang.
 */
.safe-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.safe-item {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}
/* Yang nonaktif tetap tampil — ia bisa dihidupkan lagi — tapi meredup. */
.safe-item.is-off {
  background: var(--surface-2);
}
.safe-item.is-off .safe-text {
  color: var(--text-3);
}
.safe-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-2);
}
.safe-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-left: auto;
}
/*
 * Teksnya utuh di dalam kotak gulirnya sendiri. Memotongnya menyembunyikan
 * justru bagian yang menentukan seberapa luas pengaman ini menjangkau;
 * membiarkannya penuh membuat satu entri memakan seluruh halaman.
 */
.safe-text {
  max-height: 7.5em;
  padding: var(--sp-2) var(--sp-3);
  border-left: 2px solid var(--border-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--surface-2);
  color: var(--text-2);
  font-size: var(--fs-sm);
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-word;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.safe-item.is-off .safe-text {
  background: var(--surface);
}
.safe-note {
  margin: var(--sp-2) 0 0;
  color: var(--text-3);
  font-size: var(--fs-xs);
}
/*
 * Temuan lama yang belum gugur. Bukan galat — audit berikutnya memang sudah
 * benar — tapi juga bukan catatan pinggir: inilah yang membuat orang mengira
 * pengamannya tidak bekerja.
 */
.safe-warn {
  margin: var(--sp-2) 0 0;
  padding: 6px 10px;
  border-left: 2px solid var(--warn);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--warn-soft);
  color: var(--text-2);
  font-size: var(--fs-xs);
  line-height: 1.5;
}
.safe-edit {
  margin-top: var(--sp-2);
}
.safe-edit > summary {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.safe-edit > summary::-webkit-details-marker {
  display: none;
}
.safe-edit > summary::before {
  content: '›';
  display: inline-block;
  transition: transform 0.12s ease;
}
.safe-edit[open] > summary::before {
  transform: rotate(90deg);
}
.safe-edit > .disclosure-body {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px dashed var(--border);
}

/* -- linimasa ---------------------------------------------------------- */

.timeline {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.timeline li {
  position: relative;
  padding-left: var(--sp-5);
}
.timeline li::before {
  content: '';
  position: absolute;
  top: 6px;
  left: 3px;
  width: 7px;
  height: 7px;
  border: 2px solid var(--accent);
  border-radius: 50%;
  background: var(--surface);
}
.timeline li::after {
  content: '';
  position: absolute;
  top: 17px;
  bottom: -14px;
  left: 6px;
  width: 1px;
  background: var(--border);
}
.timeline li:last-child::after {
  display: none;
}

/* ============================================================ 5. halaman */

/* -- masuk ------------------------------------------------------------- */

/*
 * Kartu masuk berdiri sejajar mata pada kira-kira dua pertiga lebar layar —
 * tidak menempel tepi kanan, tidak menutupi kata AUDIT di kiri bawah.
 * Digesernya lewat padding kanan yang ikut melebar bersama layarnya, bukan
 * angka mati, supaya proporsinya bertahan dari laptop sampai monitor lebar.
 */
.auth-wrap {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center end;
  min-height: 100vh;
  padding: clamp(20px, 5vw, 72px);
  padding-right: clamp(24px, 16vw, 280px);
}

/*
 * Latar halaman masuk. Kata AUDIT dibuat sebesar mungkin tapi setipis mungkin:
 * ia tekstur, bukan pesaing formulir. Hurufnya dipecah dan dianimasikan
 * anime.js (`motion.js`); tanpa JavaScript ia hanya diam dan samar.
 */
.auth-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: grid;
  /* Kiri bawah — diagonal berlawanan dengan kartu masuk. */
  place-items: end start;
  padding: clamp(16px, 4vw, 56px);
  overflow: hidden;
  pointer-events: none;
  user-select: none;
}

/*
 * Orb cahaya — tiga bulatan besar yang diburamkan jauh melewati tepinya
 * sehingga terbaca sebagai pendaran, bukan lingkaran. JavaScript hanya
 * menggerakkan transform; warna dan blur diam di sini.
 */
.auth-orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.55;
  will-change: transform;
}
.auth-orb.a {
  top: -12%;
  left: -8%;
  width: 46vw;
  height: 46vw;
  background: radial-gradient(circle, color-mix(in oklab, var(--accent) 34%, transparent), transparent 70%);
}
.auth-orb.b {
  top: 4%;
  right: -12%;
  width: 38vw;
  height: 38vw;
  background: radial-gradient(circle, color-mix(in oklab, var(--accent-2) 30%, transparent), transparent 70%);
}
.auth-orb.c {
  bottom: -18%;
  left: 26%;
  width: 42vw;
  height: 42vw;
  background: radial-gradient(circle, color-mix(in oklab, var(--info) 22%, transparent), transparent 70%);
}

/* Taburan titik data, memudar ke tepi supaya tidak berebut dengan formulir. */
.auth-dots {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(circle, var(--border-strong) 1px, transparent 1.5px);
  background-size: 30px 30px;
  opacity: 0.5;
  mask-image: radial-gradient(90% 80% at 35% 65%, black 20%, transparent 75%);
}

.auth-word-wrap {
  position: relative;
  overflow: hidden;
  padding: 0.08em 0.06em;
  perspective: 900px;
  will-change: transform;
}

/*
 * Moto di atas kata AUDIT — alasan sistem ini ada, tertulis di tempat yang
 * dilihat setiap orang sebelum masuk. Ikut tersapu sorot pemindai dan ikut
 * berayun paralaks karena tinggal satu wadah dengan katanya.
 */
.auth-motto {
  margin: 0 0 10px 4px;
  color: var(--text-2);
  font-size: clamp(12px, 1.5vw, 17px);
  font-weight: 650;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.auth-motto .word {
  display: inline-block;
  will-change: transform, opacity;
}
.auth-motto .terang {
  color: var(--accent);
}

/*
 * Hujan partikel: centang, silang, dan persentase acak yang mengapung naik
 * pelan — vonis-vonis kecil yang setiap hari lewat di sistem ini. Elemennya
 * dibuat JavaScript; tanpa JS lapisan ini kosong dan halaman tetap utuh.
 */
.auth-float {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.auth-float span {
  position: absolute;
  top: 100%;
  font-family: var(--mono);
  font-weight: 700;
  line-height: 1;
  will-change: transform, opacity;
}
.auth-float .f-ok { color: var(--ok); }
.auth-float .f-danger { color: var(--danger); }
.auth-float .f-warn { color: var(--warn); }
.auth-float .f-info { color: var(--info); }
.auth-float .f-accent { color: var(--accent); }
.auth-float .f-accent2 { color: var(--accent-2); }

.auth-bg-word {
  /* Warna dasar ini fallback tanpa JavaScript; gradien per huruf dipasang JS. */
  color: color-mix(in oklab, var(--accent) 26%, transparent);
  /* Batasnya menyisakan ruang untuk ayunan paralaks ±30px di bawah kartu. */
  font-size: clamp(84px, 17.5vw, 236px);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 0.92;
  white-space: nowrap;
  -webkit-text-stroke: 1px color-mix(in oklab, var(--accent) 32%, transparent);
}

/* Huruf hasil pecahan harus bisa ditransformasi satu per satu. */
.auth-bg-word .char {
  display: inline-block;
  will-change: transform, opacity, filter;
}

/*
 * Sorot pemindai yang menyapu kata — sistem ini memang memeriksa, dan
 * latarnya memperagakan itu. Digerakkan anime.js; diam tersembunyi tanpa JS.
 */
.auth-scan {
  position: absolute;
  top: -6%;
  left: -18%;
  width: 8%;
  height: 112%;
  background: linear-gradient(
    100deg,
    transparent,
    color-mix(in oklab, var(--accent) 38%, transparent) 45%,
    color-mix(in oklab, var(--accent-2) 46%, transparent) 55%,
    transparent
  );
  filter: blur(6px);
  opacity: 0;
}
/*
 * Kartu kaca minimalis. Bingkainya nyaris hilang — yang menahan formulir tetap
 * terbaca di atas latar yang bergerak adalah blur dan scrim-nya, bukan garis.
 * Satu-satunya aksen adalah garis cahaya tipis di puncak.
 */
/*
 * Kartu kaca. Isinya tinggal empat baris — merek, judul, dua kolom, satu
 * tombol — jadi kartunya tidak perlu hiasan apa pun: sebuah hairline, blur,
 * dan satu bayangan yang mengangkatnya dari latar yang bergerak.
 */
.auth-card {
  position: relative;
  width: 100%;
  max-width: 360px;
  padding: var(--sp-8) var(--sp-8) var(--sp-6);
  border: 1px solid color-mix(in oklab, var(--border) 60%, transparent);
  border-radius: var(--radius-lg);
  background: color-mix(in oklab, var(--surface) 78%, transparent);
  backdrop-filter: blur(20px) saturate(1.3);
  box-shadow: var(--shadow-lg);
}

/* -- isi kartu: sesedikit mungkin tinta -------------------------------- */

.auth-card .auth-brand {
  margin-bottom: var(--sp-6);
  color: var(--text-3);
  font-size: var(--fs-sm);
  font-weight: 600;
}
.auth-card .auth-brand .brand-mark {
  width: 24px;
  height: 24px;
  font-size: var(--fs-sm);
}
.auth-card h1 {
  font-size: var(--fs-2xl);
}
.auth-card .form-stack {
  gap: var(--sp-4);
}
/*
 * Kolom isian memakai bentuk yang sama dengan seluruh aplikasi — halaman
 * masuk bukan tempat memperkenalkan gaya kolom kedua. Yang membedakannya
 * cuma latar setengah tembus supaya kaca kartunya tetap terasa.
 */
.auth-card label {
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-weight: 650;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.auth-card input[type='email'],
.auth-card input[type='password'] {
  height: 40px;
  background: color-mix(in oklab, var(--surface) 60%, transparent);
  font-size: var(--fs-md);
}
/* Autofill Chrome menimpa latar transparan dengan kuning — tahan selamanya. */
.auth-card input:-webkit-autofill {
  transition: background-color 999999s ease-in-out 0s;
  -webkit-text-fill-color: var(--text);
}

.auth-card .btn.block {
  height: 40px;
  margin-top: var(--sp-1);
}
.auth-card .hint {
  color: var(--text-3);
}
.auth-brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-5);
  font-size: var(--fs-lg);
  font-weight: 650;
}

/*
 * Halaman galat memakai kartu yang sama tanpa latar beranimasi: tidak ada
 * kata AUDIT di kiri yang harus dihindari, jadi kartunya kembali ke tengah
 * dan permukaannya rata — kaca tanpa apa pun di belakangnya hanya kabur.
 */
.auth-wrap.plain {
  place-items: center;
  padding-right: clamp(20px, 5vw, 72px);
}
.auth-wrap.plain .auth-card {
  max-width: 440px;
  background: var(--surface);
  backdrop-filter: none;
  border-color: var(--border);
  box-shadow: var(--shadow);
}
.auth-card.center .auth-brand,
.auth-card.center .lede {
  justify-content: center;
  margin-inline: auto;
}

/* -- tata letak dua kolom ---------------------------------------------- */

/*
 * Transkrip di kiri, panel keputusan di kanan. Panelnya lengket supaya tombol
 * keputusan tetap terlihat sepanjang auditor menggulir percakapan yang panjang
 * — itu satu-satunya alasan halaman ini dibuka.
 */
.layout-2col {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 380px;
  align-items: start;
  gap: var(--sp-5);
}
.layout-2col > .col-side {
  position: sticky;
  top: calc(var(--topbar-h) + var(--sp-4));
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-height: calc(100vh - var(--topbar-h) - var(--sp-6));
  overflow-y: auto;
}

@media (max-width: 1080px) {
  .layout-2col {
    grid-template-columns: minmax(0, 1fr);
  }
  .layout-2col > .col-side {
    position: static;
    max-height: none;
    overflow: visible;
  }
}

/* -- tata letak tiga kolom (halaman percakapan) ------------------------- */

/*
 * PILIH (kiri) → BACA (tengah) → PUTUSKAN (kanan).
 *
 * Antrean berada di kiri karena itulah titik mulai setiap putaran kerja; mata
 * membaca dari kiri, dan menaruh daftar pilihan di kanan berarti melawan arah
 * kerjanya sendiri. Kolom kiri dan kanan sama-sama lengket — menggulir
 * transkrip panjang tidak boleh menghilangkan navigasi maupun tombol putusan.
 */
.layout-3col {
  display: grid;
  grid-template-columns: 268px minmax(0, 1fr) 360px;
  grid-template-areas: 'antrean utama samping';
  /* Satu baris setinggi wadahnya; `minmax(0, …)` yang membuat anak-anaknya
     boleh lebih pendek dari isinya — tanpa itu tidak ada yang bisa bergulir. */
  grid-template-rows: minmax(0, 1fr);
  align-items: stretch;
  gap: var(--sp-5);
}
.layout-3col > .col-queue {
  grid-area: antrean;
}
.layout-3col > .col-main {
  grid-area: utama;
  min-width: 0;
}
.layout-3col > .col-side {
  grid-area: samping;
}
/*
 * Ketiganya bergulir sendiri-sendiri. `overscroll-behavior: contain` itu inti
 * keluhannya: tanpa itu, mencapai ujung transkrip melanjutkan guliran ke
 * halaman di belakangnya — dan seluruh tampilan ikut bergerak lagi.
 */
.layout-3col > .col-queue,
.layout-3col > .col-main,
.layout-3col > .col-side {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
/*
 * Kolom antrean tidak bergulir sendiri; DAFTARNYA yang bergulir, dan daftar
 * itu mengisi seluruh tinggi kolom (D78). Sebelumnya daftarnya dibatasi 420 px
 * sementara kolomnya setinggi layar — sisanya kosong, padahal yang muat di
 * situ adalah percakapan berikutnya yang memang mau dikerjakan.
 */
.layout-3col > .col-queue {
  overflow: hidden;
}
.layout-3col > .col-queue > .card {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}
.layout-3col > .col-queue .queue-list {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}
.layout-3col > .col-queue,
.layout-3col > .col-side {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
/*
 * Blok sekunder panel keputusan — "putuskan sekaligus" dan "tambah temuan
 * manual" — didorong ke dasar kolom (D78). Dibiarkan mengikuti kartu di
 * atasnya, keduanya meninggalkan ruang kosong sepertiga layar yang terbaca
 * seperti panel yang terpotong.
 *
 * Hanya berlaku kalau memang ada ruang lebih; pada percakapan berisi banyak
 * temuan, `margin-top: auto` tidak berpengaruh apa pun.
 */
.layout-3col > .col-side > .col-side-bawah {
  margin-top: auto;
}
/*
 * Lompatan "lihat bukti" kini terjadi DI DALAM kolom transkrip, bukan di
 * halaman. Jarak amannya ikut mengecil — yang harus dihindari hanya tepi atas
 * kolomnya sendiri, bukan dua bar lengket yang sudah tidak ada.
 */
.layout-3col > .col-main .msg {
  scroll-margin-top: var(--sp-4);
}

/*
 * Layar sedang: ketiganya bertahan, hanya menyempit.
 *
 * Yang menyingkir terakhir adalah panel keputusan, bukan yang pertama.
 * Memutuskan menuntut membaca transkrip dan menekan tombol pada saat yang
 * sama; begitu keduanya tidak lagi bersebelahan, tiap keputusan berubah jadi
 * gulir-turun–putuskan–gulir-naik. Antrean tidak menuntut itu — ia dipakai
 * sekali di awal dan sekali di akhir tiap percakapan.
 */
@media (max-width: 1400px) {
  .layout-3col {
    grid-template-columns: 208px minmax(0, 1fr) 320px;
    gap: var(--sp-4);
  }
}

/*
 * Layar sempit: antrean naik jadi pita selebar halaman, keputusan TETAP di
 * samping transkrip. Antrean yang mendatar kehilangan sedikit keterbacaan;
 * keputusan yang terlempar ke bawah kehilangan gunanya.
 */
@media (max-width: 1180px) {
  .layout-3col {
    grid-template-columns: minmax(0, 1fr) 300px;
    grid-template-areas:
      'antrean antrean'
      'utama samping';
    grid-template-rows: auto minmax(0, 1fr);
  }
  .layout-3col > .col-queue {
    overflow: visible;
  }
  /*
   * Pitanya harus TIPIS. Antrean yang mendatar setinggi kartu penuh memakan
   * ruang yang seharusnya jadi transkrip — dan transkriplah yang dibaca.
   * Judul bagian, tombol, dan daftar dijejer satu baris; catatan penjelas
   * disembunyikan karena isinya sudah dibaca sekali dan tidak berubah.
   */
  .layout-3col > .col-queue {
    flex-direction: row;
    align-items: center;
    gap: var(--sp-3);
  }
  .layout-3col > .col-queue .section-head {
    flex: 0 0 auto;
    margin: 0;
  }
  .layout-3col > .col-queue .card {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
    padding-right: var(--sp-3);
  }
  .layout-3col > .col-queue .queue-nav {
    flex: 0 0 auto;
    padding: var(--sp-2) 0 var(--sp-2) var(--sp-3);
  }
  .layout-3col > .col-queue .card-note {
    display: none;
  }
  .layout-3col > .col-queue .queue-list {
    flex: 1 1 auto;
    flex-direction: row;
    min-width: 0;
    max-height: none;
    overflow-x: auto;
    border-top: 0;
    border-left: 1px solid var(--border);
  }
  .layout-3col > .col-queue .queue-item {
    flex: 0 0 auto;
    border-right: 1px solid var(--border);
    border-bottom: 0;
  }
}

/*
 * Layar sempit: meja kerja dibubarkan dan halaman kembali menggulir biasa.
 * Tiga wadah gulir bersarang di layar setinggi 600 px bukan kepadatan, itu
 * jebakan — tidak ada satu pun yang punya ruang untuk digulir.
 */
@media (max-width: 900px) {
  .workbench {
    height: auto;
    min-height: 0;
    margin-bottom: 0;
  }
  .layout-3col {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'antrean' 'utama' 'samping';
    grid-template-rows: auto;
  }
  .layout-3col > .col-queue,
  .layout-3col > .col-main,
  .layout-3col > .col-side {
    overflow: visible;
  }
  .layout-3col > .col-main .msg {
    scroll-margin-top: calc(var(--topbar-h) + var(--sp-4));
  }
}

/* Tombol pintasan papan ketik di catatan antrean. */
kbd {
  padding: 1px 5px;
  border: 1px solid var(--border-strong);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--text-2);
  font-family: var(--mono);
  font-size: var(--fs-micro);
  line-height: 1.5;
}

/* Transkrip panjang digulir di dalam kotaknya sendiri. */
.transcript.scrollable {
  max-height: 640px;
  overflow-y: auto;
}

/* ----------------------------------------------- galeri gambar CS (D92) */

/*
 * Petak persegi dengan `object-fit: cover`.
 *
 * Rasio aslinya liar — arsip memuat 512x188 sampai 381x848 — dan membiarkan
 * tiap petak setinggi gambarnya menghasilkan grid bergerigi yang tidak bisa
 * dipindai matanya. Yang dicari auditor pada tahap ini adalah "gambar apa
 * ini", bukan proporsinya; proporsinya utuh di popup.
 */
.galeri-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr));
  gap: var(--sp-3);
}

.galeri-item {
  position: relative;
  display: flex;
  overflow: hidden;
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  color: inherit;
  text-decoration: none;
  transition:
    transform 0.16s var(--ease-out),
    box-shadow 0.16s var(--ease-out),
    border-color 0.16s var(--ease-out);
}
.galeri-item:hover {
  transform: translateY(-2px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lift);
}
.galeri-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.galeri-bingkai {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  background: var(--surface-3);
}
.galeri-bingkai img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Keterangan berkas hilang hanya muncul saat `onerror` menandai petaknya.
 * Tanpa ini, berkas yang kedaluwarsa di CDN LiveChat menghasilkan petak abu
 * kosong yang terbaca seperti halaman rusak.
 */
.galeri-rusak {
  position: absolute;
  display: none;
  padding: 0 var(--sp-3);
  inset: 0;
  align-items: center;
  justify-content: center;
  color: var(--text-3);
  font-size: var(--fs-xs);
  text-align: center;
}
.galeri-item.is-broken .galeri-bingkai img {
  visibility: hidden;
}
.galeri-item.is-broken .galeri-rusak {
  display: flex;
}

.galeri-kaki {
  display: flex;
  min-width: 0;
  flex-direction: column;
  padding: 6px var(--sp-2) 7px;
  gap: 1px;
}
.galeri-agen {
  overflow: hidden;
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: 500;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.galeri-waktu {
  color: var(--text-3);
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
}

/* Penanda bahwa percakapannya sudah punya dugaan yang menunggu. */
.galeri-tanda {
  position: absolute;
  top: 6px;
  right: 6px;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: #fff;
  font-size: var(--fs-micro);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  text-align: center;
}

/* -- popup -------------------------------------------------------------- */

.galeri-popup {
  width: min(920px, 94vw);
  max-height: 92vh;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lg);
}
.galeri-popup::backdrop {
  background: rgb(10 11 16 / 62%);
}
.galeri-popup[open] {
  display: flex;
  flex-direction: column;
  animation: galeri-masuk 0.18s var(--ease-out) 1;
}
@keyframes galeri-masuk {
  from {
    transform: translateY(6px) scale(0.99);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .galeri-popup[open] {
    animation: none;
  }
}

.galeri-popup-kepala,
.galeri-popup-kaki {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-3) var(--sp-4);
  gap: var(--sp-3);
}
.galeri-popup-kepala {
  border-bottom: 1px solid var(--border);
}
.galeri-popup-kaki {
  border-top: 1px solid var(--border);
}
.galeri-popup-judul {
  font-size: var(--fs-sm);
  font-weight: 600;
}
.galeri-popup-sub {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  color: var(--text-3);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}

/*
 * Gambar penuh dipasang di atas permukaan gelap tetap, bukan mengikuti tema.
 * Tangkapan layar CS hampir selalu berlatar terang; menaruhnya di atas putih
 * membuat tepinya lenyap sehingga tidak terlihat di mana gambarnya berakhir.
 */
.galeri-popup-isi {
  display: flex;
  min-height: 0;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  background: #14151b;
  /* Pada tema gelap pelat ini nyaris sewarna dinding dialog; garis rambut
     bagian dalam menjaga batasnya tetap terbaca tanpa mengubah warnanya. */
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 6%);
}
.galeri-popup-isi img {
  max-width: 100%;
  max-height: 68vh;
  border-radius: var(--radius-sm);
  object-fit: contain;
}

.galeri-popup-meta,
.galeri-popup-aksi {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}

@media (max-width: 640px) {
  .galeri-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(120px, 100%), 1fr));
    gap: var(--sp-2);
  }
  .galeri-popup-isi img {
    max-height: 52vh;
  }
}

/* -------------------------------------------- hasil cari percakapan (D94) */

.hasil-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.hasil-item {
  display: block;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  color: inherit;
  text-decoration: none;
  transition:
    transform 0.14s var(--ease-out),
    box-shadow 0.14s var(--ease-out),
    border-color 0.14s var(--ease-out);
}
.hasil-item:hover {
  transform: translateY(-1px);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-lift);
}
.hasil-item:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.hasil-kepala {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 5px;
  gap: var(--sp-2);
  font-size: var(--fs-xs);
}
.hasil-siapa {
  font-weight: 600;
}
/*
 * Siapa yang bicara dibedakan warna DAN kata — chip perannya selalu ikut
 * tertulis. Warna sendirian tidak pernah jadi satu-satunya pembawa makna.
 */
.hasil-siapa.agent {
  color: var(--accent);
}
.hasil-siapa.customer {
  color: var(--text);
}
.hasil-siapa.system {
  color: var(--text-3);
}
.hasil-waktu {
  margin-left: auto;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

.hasil-teks {
  color: var(--text);
  font-size: var(--fs-sm);
  line-height: 1.55;
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}
.hasil-teks mark {
  padding: 0 2px;
  border-radius: 3px;
  background: color-mix(in oklab, var(--warn) 28%, transparent);
  color: inherit;
  font-weight: 600;
}

/*
 * Contoh kalimat pada halaman Typo terdeteksi (D128).
 *
 * Sorotannya memakai `mark` dengan warna sistem, bukan kuning bawaan browser:
 * kuning bawaan tidak punya versi gelap dan akan menyala di tema gelap.
 *
 * Kalimatnya sengaja diredupkan dan katanya tidak — yang dinilai supervisor
 * kata itu, kalimatnya cuma konteks. Membuat keduanya sama pekat berarti
 * memaksa mata mencari ulang di tiap baris, dan halaman ini dibaca puluhan
 * baris sekali duduk.
 */
.typo-contoh {
  max-width: 52ch;
}
.typo-contoh mark {
  padding: 0 3px;
  border-radius: 3px;
  background: color-mix(in oklab, var(--warn) 30%, transparent);
  color: var(--text);
  font-weight: 600;
}

/* Dua tombol keputusan berdampingan butuh jarak; `.inline-form` tidak punya. */
td.actions .inline-form + .inline-form {
  margin-left: var(--sp-2);
}

.hasil-kaki {
  display: flex;
  flex-wrap: wrap;
  margin-top: 6px;
  gap: var(--sp-3);
  color: var(--text-3);
  font-size: var(--fs-micro);
}

/* ---------------------------------- hapus entri kamus di chip-wall (D106) */

/*
 * Kata dan tombol hapusnya menempel jadi satu benda, bukan dua chip
 * bersebelahan: yang kedua akan terbaca sebagai entri tersendiri.
 */
.word-group {
  display: inline-flex;
  overflow: hidden;
  align-items: stretch;
  border-radius: var(--radius-pill);
}
/*
 * Formulir pembungkusnya ikut jadi flex. Tanpa ini `align-items: stretch`
 * hanya meregangkan formulirnya, sementara tombol di dalamnya tetap setinggi
 * isinya — dan tanda silangnya duduk 3px lebih tinggi dari katanya.
 */
.word-group form {
  display: flex;
}
.word-group .word {
  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
}

/*
 * Tanda silangnya sengaja pucat sampai disentuh. Menghapus tidak bisa
 * dibatalkan, jadi ia tidak boleh sama menariknya dengan menonaktifkan — tapi
 * juga tidak boleh tersembunyi, karena itu yang membuat orang mengira daftarnya
 * tidak bisa dirapikan.
 */
.word-hapus {
  padding: 0 8px;
  border: 1px solid var(--border);
  border-left: 0;
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
  background: var(--surface-2);
  color: var(--text-3);
  cursor: pointer;
  font-size: var(--fs-sm);
  line-height: 1;
  transition: background 0.12s ease, color 0.12s ease;
}
.word-hapus:hover {
  background: var(--danger-soft);
  color: var(--danger);
}
.word-hapus:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* ========================================================== 6. utilitas */

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.stack-sm {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  /*
   * Kolom flex mengambil lebar dari anak TERLEBAR, dan `.code-block` di
   * dalamnya diukur dari isi JSON-nya sebelum `overflow-x` sempat berlaku.
   * Tanpa dua baris ini, kartu rubrik meluber 21px keluar bantalannya di
   * lebar ponsel.
   */
  min-width: 0;
  max-width: 100%;
}
.row-gap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}
.split {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}

/*
 * Anak flex/grid punya `min-width: auto`, jadi ia menolak menyusut di bawah
 * lebar isinya. Satu tabel lebar di dalamnya cukup untuk mendorong seluruh
 * halaman melebar di layar sempit — padahal tabelnya sendiri sudah punya
 * pembungkus yang bisa digulir. Ini yang membuat pembungkus itu benar-benar
 * bekerja.
 */
.split > *,
.layout-2col > *,
.row-gap > *,
.form-row > * {
  min-width: 0;
}

/* Dua kolom setara yang menumpuk sendiri di layar sempit. */
.cols-2 {
  display: grid;
  /*
   * `min(320px, 100%)`, bukan `320px` (D116).
   *
   * `minmax(320px, 1fr)` memaksa jalurnya minimal 320px APA PUN lebar
   * wadahnya. Begitu kotak isi lebih sempit dari itu, gridnya meluber diam-
   * diam -- terukur di 375px: kotak isi kartu 291px, jalurnya tetap 320px,
   * 29px keluar bantalan.
   *
   * `min(320px, 100%)` membuat batas bawahnya menyerah pada wadah yang lebih
   * sempit, tanpa mengubah apa pun di layar lebar.
   */
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: var(--sp-5);
  align-items: start;
}
.cols-2 > * {
  min-width: 0;
}

.small {
  font-size: var(--fs-xs);
}
.sm {
  font-size: var(--fs-xs);
}
.muted {
  color: var(--text-2);
}
.faint {
  color: var(--text-3);
}
.nowrap {
  white-space: nowrap;
}
.num {
  font-variant-numeric: tabular-nums;
}
.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.center {
  text-align: center;
}

.ok-text {
  color: var(--ok);
}
.warn-text {
  color: var(--warn);
}
.danger-text {
  color: var(--danger);
}

.mt-0 {
  margin-top: 0;
}
.mt-2 {
  margin-top: var(--sp-2);
}
.mt-3 {
  margin-top: var(--sp-3);
}
.mt-4 {
  margin-top: var(--sp-4);
}
.mt-5 {
  margin-top: var(--sp-5);
}
.mt-6 {
  margin-top: var(--sp-6);
}
.mb-0 {
  margin-bottom: 0;
}
.mb-4 {
  margin-bottom: var(--sp-4);
}
.mb-5 {
  margin-bottom: var(--sp-5);
}

/*
 * Kendali yang sengaja dimatikan tapi tetap ditampilkan supaya tata letaknya
 * tidak melompat (mis. tombol "berikutnya" di ujung antrean).
 */
.is-disabled {
  opacity: 0.45;
  pointer-events: none;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/*
 * Panel yang menggulir sendiri tidak boleh MELEBAR melewati wadahnya (D116).
 *
 * `overflow-x: auto` mengatur apa yang terjadi pada isi yang kepanjangan, tapi
 * tidak menahan lebar kotaknya sendiri. Terukur di 375px: `.queue-list`
 * selebar 499px di dalam kartu 202px -- isinya memang bisa digulir, tapi
 * kotaknya sendiri sudah mendorong halaman ke samping lebih dulu.
 */
.queue-list,
.check-list,
.safe-text,
.transcript.scrollable {
  max-width: 100%;
}

/*
 * Panel yang bergulir wajib jadi kiblat bagi isinya (D113).
 *
 * `.visually-hidden` di atas absolut TANPA `top`/`left`, jadi ia memakai posisi
 * alirannya. Kalau di dalam panel yang bergulir tidak ada satu pun leluhur yang
 * berposisi, kiblatnya melompat ke halaman -- padahal posisi aliran itu diukur
 * dalam koordinat ISI panel, yang jauh lebih panjang dari layar. Halaman ikut
 * memanjang sepanjang itu.
 *
 * Terukur di halaman detail temuan: tiga input zoom lampiran menaruh titik
 * terjauh di y=1190, sehingga `body` setinggi 1191px pada layar 950px. Footer
 * terdampar di tengah dengan 241px ruang kosong di bawahnya -- tanpa satu pun
 * elemen yang kelihatan menjebol, karena yang menjebol lebarnya 1px dan
 * tersembunyi.
 *
 * Yang `position: sticky` (`.sidebar`, `.col-side`) sudah jadi kiblat sendiri,
 * jadi tidak ikut didaftar di sini.
 */
.check-list,
.queue-list,
.safe-text,
.transcript.scrollable,
.layout-3col > .col-queue,
.layout-3col > .col-main,
.layout-3col > .col-side,
.workbench > .layout-2col > *,
.workbench > .layout-3col > * {
  position: relative;
}

/* ========================================================== 7. responsif */

/* Tombol menu hanya muncul saat sidebar dilipat. */
.nav-toggle {
  display: none;
}

@media (max-width: 900px) {
  .shell {
    grid-template-columns: minmax(0, 1fr);
  }

  .nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-2);
    cursor: pointer;
  }

  /*
   * Sidebar menjadi panel yang dibuka dari topbar. Memakai `:has()` supaya
   * tidak butuh JavaScript untuk membuka-tutupnya.
   */
  .sidebar {
    position: static;
    display: none;
    height: auto;
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border);
    background: var(--surface);
  }
  body:has(.nav-toggle-input:checked) .sidebar {
    display: block;
  }

  .content {
    padding: var(--sp-5) var(--sp-4) var(--sp-8);
  }
  .topbar-user span:not(.avatar) {
    display: none;
  }
  .stats {
    grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  }
  .stat-value {
    font-size: var(--fs-2xl);
  }
  h1 {
    font-size: var(--fs-xl);
  }
  .config-preview {
    max-width: 220px;
  }
  .chip-row {
    max-width: none;
  }
  .deflist {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
  }
  .deflist dd + dt {
    margin-top: var(--sp-2);
  }
}

/*
 * Di layar sempit, komposisi diagonal itu tidak muat: kartu yang dipaksa ke
 * kanan atas hanya menyisakan sisa ruang yang tidak berguna. Kembali ke tengah,
 * dan kata latar mengecil supaya tidak berdesakan di belakang formulir.
 */
@media (max-width: 760px) {
  .auth-wrap {
    place-items: center;
    padding-right: clamp(20px, 5vw, 72px);
  }
  .auth-bg {
    place-items: end center;
  }
  .auth-bg-word {
    font-size: clamp(64px, 22vw, 140px);
    opacity: 0.7;
  }
}

@media (max-width: 560px) {
  /*
   * Ponsel harus TETAP JALAN (D116).
   *
   * Prioritasnya desktop — di situlah auditor bekerja berjam-jam — tapi
   * "tidak dioptimalkan" berbeda dari "rusak", dan gulir mendatar adalah
   * rusak. Terukur di 375px sebelum perbaikan ini: halaman transkrip
   * menggulir 116px ke samping.
   *
   * Label tombol di sini kerap kalimat penuh ("Hapus semua percakapan dan
   * lampirannya"). Di desktop tombol memang tidak boleh berpindah baris; di
   * lebar ponsel, tidak berpindah baris berarti keluar layar.
   */
  .btn {
    max-width: 100%;
    white-space: normal;
  }

  /*
   * Kata panjang tanpa spasi -- kode aturan (`SR_ABAI_MINTA_DAFTAR`), nama
   * berkas, alamat -- tidak punya tempat patah di lebar ponsel. `anywhere`
   * hanya di sini: di desktop ia akan memotong kata di tengah tanpa alasan.
   */
  .card-body,
  .check strong,
  .stack-sm {
    overflow-wrap: anywhere;
  }

  .form-row {
    flex-direction: column;
    align-items: stretch;
  }
  .form-row .btn {
    width: 100%;
  }
  .page-head {
    flex-direction: column;
  }
  /*
   * Di kontainer kolom, flex-basis menjadi TINGGI — basis 420px dari tata
   * letak baris berubah jadi 420px ruang kosong di bawah judul.
   */
  .page-head-main {
    flex-basis: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

@media print {
  .topbar,
  .sidebar,
  .footer,
  .page-actions,
  .pagination {
    display: none;
  }
  .shell {
    grid-template-columns: minmax(0, 1fr);
  }
  .card,
  .table-wrap {
    break-inside: avoid;
  }
}
