/* ============================================================
   AUDITOR — design system  ·  "Ruang Malam"

   Sidebar selalu gelap, isi halaman terang. Kontras itu yang
   membuat isi halaman terasa maju ke depan, dan membuat menu
   tetap jadi tepi yang tenang — bukan ikut ramai.
   Tim ini bekerja malam; layarnya ikut bekerja malam.

   Arah sebelumnya sengaja sunyi dan lapang. abby memakainya
   sebulan dan bilang "terlalu datar" — jadi sekarang warna dan
   kedalaman dipakai lebih berani, tapi tetap hanya untuk
   sesuatu yang berarti. Yang boleh berteriak tetap datanya.
   ============================================================ */

:root {
  color-scheme: light;

  /* surfaces */
  --page:        #eef0f4;
  --surface-1:   #ffffff;
  --surface-2:   #f5f6f9;
  --surface-sunk:#eff1f5;

  /* Sidebar punya tokennya sendiri, tidak menumpang --surface-2.
     Sempat menumpang, dan badge "waiting" ikut jadi hitam — token yang
     dipakai dua maksud akan selalu menabrak salah satunya. */
  --side-bg:     #0f1420;
  --side-ink:    rgba(255,255,255,0.70);
  --side-ink-on: #ffffff;
  --side-dim:    rgba(255,255,255,0.36);
  --side-accent: #38bdf8;

  /* Warna per modul. Satu aksen untuk seluruh aplikasi terlihat rapi selama
     lima menit lalu jadi rata; warna yang berganti tiap wilayah membuat mata
     punya penanda, dan itu yang bikin tidak cepat bosan. */
  --m-jobdesk: #38bdf8;
  --m-work:    #34d399;
  --m-tools:   #fb923c;
  --m-master:  #a78bfa;

  /* ink */
  --ink:     #0f1626;
  --ink-2:   #414c63;
  --muted:   #7b869c;

  /* lines */
  --border:  rgba(15,22,38,0.11);
  --grid:    rgba(15,22,38,0.08);
  --axis:    rgba(15,22,38,0.18);

  /* accents (validated categorical ramp) */
  --s1:#2563eb; --s2:#f97316; --s3:#10b981; --s4:#f59e0b;
  --good:#10b981; --warn:#f59e0b; --crit:#e11d48;
  --wash:      rgba(37,99,235,0.08);
  --wash-firm: rgba(37,99,235,0.15);

  /* elevation — kartu benar-benar terangkat dari halaman, bukan sekadar
     berbeda sedikit. Inilah yang dulu bikin tampilannya terasa datar. */
  --sh-1: 0 1px 2px rgba(15,22,38,0.05), 0 2px 8px rgba(15,22,38,0.06);
  --sh-2: 0 4px 12px rgba(15,22,38,0.10), 0 16px 40px rgba(15,22,38,0.12);

  --r-lg: 16px;
  --r:    10px;
  --r-sm: 8px;
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --page:        #080b12;
  --surface-1:   #121826;
  --surface-2:   #171e2c;
  --surface-sunk:#0e1320;

  /* Di mode gelap sidebar dibuat LEBIH gelap dari halaman, bukan sama —
     kalau sama, batas antara menu dan isi hilang sama sekali. */
  --side-bg:     #060910;
  --side-ink:    rgba(255,255,255,0.66);
  --side-ink-on: #ffffff;
  --side-dim:    rgba(255,255,255,0.32);
  --side-accent: #38bdf8;

  --ink:   #eef2f8;
  --ink-2: #aab6ca;
  --muted: #74819a;

  --border: rgba(255,255,255,0.09);
  --grid:   rgba(255,255,255,0.06);
  --axis:   rgba(255,255,255,0.18);

  --s1:#3b82f6; --s2:#fb923c; --s3:#34d399; --s4:#fbbf24;
  --good:#34d399; --warn:#fbbf24; --crit:#fb7185;
  --wash:      rgba(59,130,246,0.13);
  --wash-firm: rgba(59,130,246,0.22);

  --sh-1: 0 1px 2px rgba(0,0,0,0.55);
  --sh-2: 0 4px 12px rgba(0,0,0,0.5), 0 18px 44px rgba(0,0,0,0.42);
}

/* Hurufnya dimuat lewat <link> di layout, bukan @import di sini.
   @import hanya sah di baris paling atas berkas CSS, dan berkas ini diawali
   blok token — jadi kalau ditaruh di sini browser mengabaikannya diam-diam.
   Lewat <link> juga lebih cepat: bisa didahului preconnect. */

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

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 14px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: var(--s1); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { letter-spacing: -0.02em; font-weight: 700; }
.mono  {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.muted { color: var(--muted); font-size: 12px; }
.spacer{ flex: 1; }
[hidden]{ display: none !important; }

/* ---------------------------------------------------------- layout */
.layout { display: flex; min-height: 100vh; }

/* Sidebar gelap di kedua tema. Warnanya tidak ikut berganti waktu tema
   ditukar — itu yang membuat tepinya tetap tenang dan isinya maju. */
.side {
  width: 246px; flex: 0 0 246px;
  background: var(--side-bg);
  border-right: 0;
  padding: 22px 12px 24px;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.brand {
  display: flex; align-items: center; gap: 11px;
  padding: 2px 8px 24px;
  font-weight: 800; letter-spacing: .18em; font-size: 13px;
  color: var(--side-ink-on);
}
/* Titik kecil terbaca sebagai lampu indikator, bukan identitas. Tanda persegi
   membulat dengan huruf A memberi sesuatu untuk dikenali. */
.brand .dot {
  width: 30px; height: 30px; border-radius: 9px;
  /* Terang ke gelap dari SATU warna, bukan sian ke nila. Gradien dua warna
     berbeda terbaca sebagai dua identitas — dan aplikasi ini cuma punya satu. */
  background: linear-gradient(145deg, var(--side-accent),
              color-mix(in srgb, var(--side-accent) 45%, #123a63) 88%);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--side-accent) 30%, transparent),
              inset 0 1px 0 rgba(255,255,255,.35);
  display: grid; place-items: center; flex: 0 0 auto;
}
.brand .dot::after {
  content: 'A';
  font-size: 15px; font-weight: 800; letter-spacing: 0; color: #06121f;
}
.navgrp {
  font-size: 9.5px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--side-dim); font-weight: 700;
  padding: 18px 10px 7px;
}
.nav {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; margin-bottom: 1px;
  border-radius: 9px;
  color: var(--side-ink); font-size: 13.5px; font-weight: 500;
  text-decoration: none;
  transition: background .12s, color .12s;
}
.nav:hover { background: rgba(255,255,255,.07); color: var(--side-ink-on); text-decoration: none; }
.nav.on {
  background: linear-gradient(90deg, rgba(56,189,248,.16), rgba(56,189,248,.02));
  color: var(--side-ink-on); font-weight: 700;
  box-shadow: inset 3px 0 0 var(--side-accent);
}
/* Ikon diberi alas sendiri. Ini yang mengubah deretan menu dari daftar tautan
   biasa jadi sesuatu yang terasa dirancang. */
.nav .ic {
  width: 26px; height: 26px; flex: 0 0 auto;
  display: grid; place-items: center;
  border-radius: 8px;
  background: rgba(255,255,255,.055);
  transition: background .12s, color .12s;
  color: var(--side-dim);
}
/* Ikonnya SVG segaris, satu warna, mengikuti warna barisnya. Sebelumnya emoji
   berwarna — 14 palet berbeda dalam satu kolom, dan tidak satu pun warnanya
   berarti apa-apa. Lihat catatan di Core\Nav::ic(). */
.nav .ic svg { width: 15px; height: 15px; display: block; }
.nav:hover .ic { color: var(--side-ink-on); }
.nav:hover .ic { background: rgba(255,255,255,.11); }
.nav.on .ic {
  background: color-mix(in srgb, var(--side-accent) 18%, transparent);
  color: var(--side-accent);
}
.soon {
  margin-left: auto; font-size: 8.5px; letter-spacing: .08em; font-weight: 700;
  background: rgba(255,255,255,.08); color: rgba(255,255,255,.45);
  padding: 2px 6px; border-radius: 99px;
}

.main { flex: 1; min-width: 0; }

.top {
  display: flex; align-items: center; gap: 12px;
  padding: 15px 30px;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--s1) 5%, transparent), transparent 70%),
    color-mix(in srgb, var(--surface-1) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 30;
}
.top h1 { font-size: 20px; margin: 0; font-weight: 800; letter-spacing: -.03em; }

.datepick {
  display: flex; align-items: center; gap: 9px;
  border: 1px solid var(--border); border-radius: 99px;
  padding: 4px 6px 4px 14px; background: var(--surface-1);
}
.datepick .lbl { color: var(--muted); font-size: 11.5px; font-weight: 550; }
.datenav { display: flex; align-items: center; gap: 4px; }
.datenav input[type=date] {
  padding: 3px 7px; border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--ink);
  font-family: inherit; font-size: 12.5px; font-variant-numeric: tabular-nums;
}
.datenav input[type=date]:hover { background: var(--surface-sunk); }
.chip {
  font-size: 9.5px; letter-spacing: .07em; font-weight: 700;
  background: var(--wash-firm); color: var(--s1);
  padding: 3px 9px; border-radius: 99px;
}
.chip-link { text-decoration: none; }
.chip-link:hover { filter: brightness(1.06); text-decoration: none; }

/* Pil "🔒 N slot held".
   DULU kuning — dan itu melanggar aturan warna proyek ini sendiri, cuma tidak
   pernah ketahuan karena fixture pemeriksa warna tidak punya satu pun slot
   yang dipegang, jadi pilnya tidak pernah tergambar sama sekali. Baru
   terlihat 30 Agustus waktu fixture-nya diisi untuk menggambar panel
   "lepas satu-satu".

   Aturannya (AUDITOR_Aturan_Warna.md): warna cuma untuk MAKNA. Dan menurut
   dokumen proyek sendiri, pil ini KEADAAN — "muncul sejak detik pertama";
   yang berbentuk PERINGATAN adalah loncengnya, sesudah lewat 90 menit. Jadi
   keadaan biasa tidak berhak atas warna: netral.

   MERAH baru waktu salah satunya slot ADMIN, dan di situ warnanya benar-benar
   berarti sesuatu: slot admin cuma boleh dipegang satu orang, jadi yang
   memegangnya sedang memblokir seluruh situs itu untuk semua orang. */
.held-pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; font-weight: 620; text-decoration: none;
  color: var(--ink-2); background: var(--surface-2);
  border: 1px solid var(--border);
  padding: 4px 11px; border-radius: 99px;
}
.held-pill:hover { filter: brightness(1.04); text-decoration: none; }
.hb-adm > .held-pill {
  color: var(--crit);
  background: color-mix(in srgb, var(--crit) 10%, transparent);
  border-color: color-mix(in srgb, var(--crit) 40%, transparent);
}

/* ============================================================
   Lonceng peringatan — abby: "peringatan untuk staf gtu"
   ============================================================
   Dipakai <details>, bukan JS: loncengnya terpasang di header SETIAP halaman,
   jadi kalau berkas JS-nya gagal dimuat, peringatan yang berhak dilihat orang
   akan hilang tanpa jejak. Dengan <details> panelnya tetap bisa dibuka —
   isinya sudah digambar server sejak halaman dimuat. */

.bell { position: relative; }
.bell > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  width: 34px; height: 30px; border-radius: var(--r-sm); font-size: 15px;
  border: 1px solid var(--border); background: var(--surface-1);
}
.bell > summary::-webkit-details-marker { display: none; }
.bell > summary:hover { background: var(--surface-2); }

/* Angkanya, bukan titik. "3" langsung memberi tahu seberapa besar masalahnya;
   titik cuma bilang "ada sesuatu" lalu menyuruh orang mengklik untuk tahu. */
.bell-n {
  position: absolute; top: -6px; right: -6px; min-width: 17px; height: 17px;
  padding: 0 4px; border-radius: 99px;
  font-size: 10.5px; font-weight: 700; line-height: 17px; text-align: center;
  color: #fff; background: var(--muted);
  border: 2px solid var(--surface-1); box-sizing: content-box;
}
.bell.warn .bell-n { background: var(--warn); color: #4a3200; }
.bell.crit .bell-n { background: var(--crit); }
/* Cuma yang crit yang berkedip. Kalau semuanya berkedip, tidak ada yang
   berkedip — matanya berhenti membedakan dalam satu malam. */
.bell.crit > summary { animation: pulse 1.8s ease-in-out infinite; }

.bell-panel {
  position: absolute; z-index: 60; top: calc(100% + 6px); right: 0;
  width: 320px; max-height: 60vh; overflow-y: auto; padding: 6px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: 0 14px 34px rgba(0,0,0,.18);
}
.bell-none { padding: 14px 12px; font-size: 12.5px; color: var(--muted); text-align: center; }

.bell-item {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 10px; border-radius: var(--r-sm);
  text-decoration: none; color: var(--ink);
}
.bell-item:hover { background: var(--surface-2); text-decoration: none; }
.bell-item + .bell-item { border-top: 1px solid var(--grid); }
.bell-item > i { font-style: normal; font-size: 14px; line-height: 1.35; flex: none; }
.bell-item > span { min-width: 0; }
.bell-item b  { display: block; font-size: 12.5px; font-weight: 640; }
/* Keterangannya boleh membungkus. Memotongnya dengan ellipsis justru membuang
   bagian yang menjelaskan KENAPA ini penting — "orang lain tidak bisa masuk". */
.bell-item em {
  display: block; font-style: normal; font-size: 11.5px; color: var(--muted);
  margin-top: 2px; overflow-wrap: break-word;
}
.bell-item.crit b { color: var(--crit); }
.bell-item.warn b { color: var(--ink); }

.who { display: flex; align-items: center; gap: 10px; padding-left: 14px; border-left: 1px solid var(--border); }
.av {
  width: 34px; height: 34px; border-radius: 11px;
  /* Dulu biru -> ungu. Ungunya tidak berarti apa-apa, dan dia satu-satunya
     ungu di seluruh layar — jadi matanya berhenti di situ tanpa alasan.
     Sekarang satu warna yang sama dengan lambang, cuma digelapkan. */
  background: linear-gradient(140deg, var(--s1),
              color-mix(in srgb, var(--s1) 45%, #0a2246));
  color: #fff; display: grid; place-items: center;
  font-weight: 800; font-size: 12px; letter-spacing: .02em;
  box-shadow: 0 3px 10px color-mix(in srgb, var(--s1) 35%, transparent),
              inset 0 1px 0 rgba(255,255,255,.3);
}
.who-txt  { line-height: 1.25; }
.who-name { font-weight: 620; font-size: 13px; }
.who-role { font-size: 11px; color: var(--muted); }

/* ------------------------------------------ tab di bawah judul halaman
   Dipakai menu gabungan (Cuti, Roster, Buku Dosa, Channel Review). Kelasnya
   sengaja BUKAN .tab — nama itu sudah dipakai tab-dalam-halaman yang digerakkan
   JavaScript, dan yang ini cuma tautan biasa antar halaman. */
.pgtabs {
  display: flex; flex-wrap: wrap; gap: 2px;
  padding: 0 28px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.pgtab {
  --m: var(--s1);
  position: relative;
  padding: 11px 15px 10px;
  font-size: 13px; font-weight: 560;
  color: var(--muted);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
.pgtab:hover { color: var(--ink-2); text-decoration: none; background: var(--wash); }
.pgtab.on {
  color: var(--ink); font-weight: 700;
  border-bottom-color: var(--m);
  background: var(--surface-1);
}
.pgtabs[data-mod="jobdesk"] .pgtab { --m: var(--m-jobdesk); }
.pgtabs[data-mod="work"]    .pgtab { --m: var(--m-work); }
.pgtabs[data-mod="tools"]   .pgtab { --m: var(--m-tools); }
.pgtabs[data-mod="master"]  .pgtab { --m: var(--m-master); }
.pgtab.on { color: color-mix(in srgb, var(--m) 72%, var(--ink)); }

.wrap { padding: 26px 28px 72px; max-width: 1280px; }   /* lihat blok lebar layar di bawah */

/* ---------------------------------------------------------- cards */
.card {
  position: relative;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  padding: 18px 20px;
  margin-bottom: 16px;
}
/* Sehelai cahaya di tepi atas. Nyaris tak disadari satu per satu, tapi
   inilah yang membuat kartu terbaca sebagai benda, bukan kotak. */
.card::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 1px;
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, var(--s1) 22%, transparent) 22%,
              color-mix(in srgb, var(--s1) 22%, transparent) 78%, transparent);
  pointer-events: none;
}
.card h3 { margin: 0 0 2px; font-size: 15px; font-weight: 680; }
.card p  { margin: 6px 0 0; color: var(--ink-2); font-size: 13px; }
.card-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 16px; flex-wrap: wrap; }
.card-head h3 { margin-bottom: 3px; }
.sub { color: var(--muted); font-size: 12px; margin: 0; }
.hr  { height: 1px; background: var(--grid); margin: 16px 0; }

.empty { text-align: center; padding: 56px 24px; }
.empty-ic { font-size: 32px; margin-bottom: 10px; opacity: .8; }
.empty h3 { font-size: 16px; }
.empty p  { max-width: 460px; margin: 8px auto 0; }

/* ---------------------------------------------------------- buttons */
.btn {
  font-family: inherit; font-size: 13px; font-weight: 550;
  border: 1px solid var(--border);
  background: var(--surface-1); color: var(--ink);
  padding: 7px 14px; border-radius: var(--r-sm);
  cursor: pointer; text-decoration: none; display: inline-block;
  box-shadow: var(--sh-1);
  transition: background .12s, border-color .12s, transform .06s;
}
.btn:hover { background: var(--surface-2); border-color: var(--axis); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn.pri {
  background: var(--s1); border-color: transparent; color: #fff;
  box-shadow: 0 1px 2px rgba(42,120,214,.3), 0 2px 8px rgba(42,120,214,.22);
}
.btn.pri:hover { background: color-mix(in srgb, var(--s1) 88%, #000); }
.btn.dan { background: var(--crit); border-color: transparent; color: #fff; }
.btn.dan:hover { background: color-mix(in srgb, var(--crit) 88%, #000); }
.btn.sm { padding: 5px 11px; font-size: 12px; }
.btn.xs { padding: 3px 8px; font-size: 11px; border-radius: 6px; line-height: 1.55; box-shadow: none; }
.btn.block { width: 100%; padding: 11px; font-size: 14px; margin-top: 8px; }
.btn:disabled { opacity: .42; cursor: not-allowed; box-shadow: none; }
.btn:disabled:hover { background: var(--surface-1); border-color: var(--border); }
.btn.pri:disabled:hover { background: var(--s1); }

/* ---------------------------------------------------------- badges */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 640; letter-spacing: .01em;
  padding: 3px 9px; border-radius: 99px;
  border: 1px solid var(--border); background: var(--surface-1);
  white-space: nowrap;
}
.b-info { color: var(--s1);  border-color: color-mix(in srgb, var(--s1) 34%, transparent);  background: var(--wash); }
.b-good { color: var(--good); border-color: color-mix(in srgb, var(--good) 34%, transparent); background: color-mix(in srgb, var(--good) 8%, transparent); }
.b-crit { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 36%, transparent); background: color-mix(in srgb, var(--crit) 7%, transparent); }
.b-warn { color: #7d5800;     border-color: color-mix(in srgb, var(--warn) 50%, transparent); background: color-mix(in srgb, var(--warn) 13%, transparent); }
:root[data-theme="dark"] .b-warn { color: var(--warn); }
.b-mut  { color: var(--muted); background: var(--surface-2); }

/* ---------------------------------------------------------- tables */
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th {
  text-align: left; font-weight: 620; color: var(--muted);
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  padding: 8px 12px; border-bottom: 1px solid var(--border);
}
td { padding: 9px 12px; border-bottom: 1px solid var(--grid); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tbody tr { transition: background .1s; }
tbody tr:hover { background: var(--surface-2); }
tr.grp td {
  background: var(--surface-sunk); font-weight: 660; font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2);
  padding: 7px 12px;
}
tr.grp:hover td { background: var(--surface-sunk); }
tr.sub-tot td { font-weight: 620; background: var(--surface-2); }
tr.tot td { font-weight: 700; border-top: 2px solid var(--axis); border-bottom: 0; }
td.pct.over { color: var(--crit); font-weight: 660; }

/* ---------------------------------------------------------- forms */
.inp {
  width: 92px; padding: 6px 9px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--ink);
  font-family: inherit; font-size: 13px; text-align: right;
  font-variant-numeric: tabular-nums;
  transition: border-color .12s, background .12s;
}
.inp:hover { border-color: var(--axis); }
.inp:focus { outline: none; border-color: var(--s1); background: var(--surface-1); box-shadow: 0 0 0 3px var(--wash); }
.inp.pending { opacity: .5; }
.inp.err { border-color: var(--crit); background: color-mix(in srgb, var(--crit) 7%, transparent); }

/* Daftar tipe ditulis lengkap. Sebelumnya number, search, time dan url
   terlewat — di mode terang bedanya nyaris tak terlihat, tapi di mode gelap
   kotaknya jadi putih terang di tengah halaman gelap. */
input[type=text], input[type=password], input[type=email], input[type=date],
input[type=number], input[type=search], input[type=time], input[type=url],
input[type=tel], input[type=datetime-local], input[type=month], select, textarea {
  font-family: inherit; font-size: 13.5px; color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 8px 11px;
  transition: border-color .12s, box-shadow .12s;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--s1); background: var(--surface-1);
  box-shadow: 0 0 0 3px var(--wash);
}
::placeholder { color: var(--muted); opacity: 1; }

/* Tombol "Choose File" bawaan browser tidak ikut tema mana pun. */
input[type=file] {
  font-family: inherit; font-size: 12.5px; color: var(--ink-2);
  background: var(--surface-2); border: 1px dashed var(--border);
  border-radius: var(--r-sm); padding: 9px 11px; width: 100%;
}
input[type=file]::file-selector-button {
  font: inherit; font-size: 12px; font-weight: 600;
  margin-right: 11px; padding: 5px 12px; cursor: pointer;
  color: var(--ink); background: var(--surface-1);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  transition: border-color .12s;
}
input[type=file]::file-selector-button:hover {
  border-color: color-mix(in srgb, var(--s1) 55%, var(--border));
}
.field { display: block; margin-bottom: 15px; }
.field > span {
  display: block; font-size: 11.5px; color: var(--ink-2);
  margin-bottom: 6px; font-weight: 570;
}
.field input, .field select, .field textarea { width: 100%; padding: 9px 12px; font-size: 14px; }
.field textarea {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px; line-height: 1.65; resize: vertical;
}
.hint { font-size: 11.5px; color: var(--muted); margin: 5px 0 0; display: block; }
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px;
  background: var(--surface-sunk); padding: 1px 5px; border-radius: 4px;
}

.save-state { font-size: 11.5px; margin-left: 8px; font-weight: 620; }
.save-state.saving { color: var(--muted); }
.save-state.ok  { color: var(--good); }
.save-state.err { color: var(--crit); }

/* ---------------------------------------------------------- alerts */
.alert {
  padding: 12px 15px; border-radius: var(--r);
  font-size: 13px; margin-bottom: 16px; border: 1px solid;
}
.alert-error { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 32%, transparent); background: color-mix(in srgb, var(--crit) 7%, transparent); }
.alert-warn  { color: var(--ink);  border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.alert-good  { color: var(--good); border-color: color-mix(in srgb, var(--good) 32%, transparent); background: color-mix(in srgb, var(--good) 7%, transparent); }

.note {
  background: var(--wash);
  border: 1px solid color-mix(in srgb, var(--s1) 20%, transparent);
  border-radius: var(--r);
  padding: 11px 14px; font-size: 12.5px; color: var(--ink-2);
  margin-top: 14px;
}

/* ---------------------------------------------------------- auth */
body.centered {
  display: grid; place-items: center; min-height: 100vh; padding: 24px;
  background:
    radial-gradient(1100px 620px at 50% -12%, var(--wash), transparent 68%),
    var(--page);
}
.auth-card {
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: 18px; box-shadow: var(--sh-2);
  padding: 34px 32px; width: 100%; max-width: 388px;
}
.auth-foot { text-align: center; font-size: 12px; color: var(--muted); margin: 20px 0 0; }

/* ============================================================
   Halaman login — layar terbagi dua
   ============================================================
   abby: "cuman bisa lebih premium ga ya?"

   Yang membuat sebuah halaman terasa mahal bukan hiasan yang lebih banyak —
   justru sebaliknya. Empat hal yang dikerjakan di sini, dan semuanya soal
   kehalusan, bukan penambahan:

   1. TIPOGRAFI PUNYA JARAK. Judul besar dengan tracking rapat (-.035em) dan
      baris rapat (1.05), berdampingan dengan label kecil ber-tracking lebar.
      Kontras antara keduanya yang bikin terbaca sengaja, bukan sekadar besar.
   2. GELAPNYA PUNYA KEDALAMAN. Aurora dua lapis + butiran halus + vignette.
      Gradien polos terlihat seperti gambar stok; butiran membuatnya terbaca
      sebagai permukaan.
   3. GARIS RAMBUT, BUKAN KOTAK. Pemisah setipis mungkin, sudut yang lebih
      besar, bayangan yang lebar dan lembut — bukan tebal dan dekat.
   4. GERAKAN YANG NYARIS TIDAK TERLIHAT. Naik 10px sekali waktu dibuka, dan
      berhenti sepenuhnya kalau orangnya minta gerakan dikurangi.

   Warnanya tetap token sidebar yang sudah ada. Halaman login dan aplikasinya
   harus terlihat sebagai satu benda. */

body.login {
  min-height: 100vh; margin: 0; padding: 0;
  background: var(--page); display: block;
}
.lg-wrap { display: grid; grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr); min-height: 100vh; }

/* ---------------------------------------------------------- kiri */
.lg-side {
  position: relative; isolation: isolate; overflow: hidden;
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 54px 56px;
  background: #070a11;
  color: var(--side-ink);
}

/* Aurora. Dua noda besar yang saling tumpang tindih, sangat lembut — sumber
   "kedalaman"-nya. Ditaruh di lapisan sendiri supaya butiran di atasnya bisa
   mencampur tanpa ikut mewarnai tulisan. */
.lg-side::before {
  content: ''; position: absolute; inset: -20%; z-index: -2; pointer-events: none;
  background:
    radial-gradient(46% 40% at 18% 12%, rgba(56,189,248,.30), transparent 68%),
    radial-gradient(42% 44% at 84% 88%, rgba(99,102,241,.30), transparent 70%),
    radial-gradient(60% 50% at 50% 50%, rgba(14,26,48,.85), transparent 74%);
  filter: blur(6px);
}

/* Butiran. Inilah bedanya "gradien" dan "permukaan". Sangat samar dengan
   sengaja: kalau kelihatan sebagai bintik, dia berebut perhatian. SVG
   ditempel sebagai data URI supaya tidak ada permintaan berkas tambahan —
   halaman login tidak boleh menunggu apa pun. */
.lg-side::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  opacity: .5; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.30'/%3E%3C/svg%3E");
}

.lg-side-in, .lg-side-foot { position: relative; }

/* ---- lambang ---- */
.lg-mark { display: flex; align-items: center; gap: 14px; }
.lg-mark .dot {
  width: 44px; height: 44px; border-radius: 14px; position: relative;
  /* Terang -> gelap dari satu warna. Dulu sian -> nila; tiga warna untuk satu
     lambang membuatnya terbaca sebagai logo perusahaan lain. */
  background: linear-gradient(150deg,
              color-mix(in srgb, var(--side-accent) 65%, #ffffff),
              var(--side-accent) 45%,
              color-mix(in srgb, var(--side-accent) 45%, #123a63) 100%);
  display: grid; place-items: center; flex: 0 0 auto;
  box-shadow:
    0 10px 30px rgba(56,189,248,.30),
    0 2px 6px rgba(0,0,0,.4),
    inset 0 1px 0 rgba(255,255,255,.45);
}
/* Cahaya lembut di belakang lambang. Satu-satunya yang bersinar di halaman
   ini, jadi mata punya satu titik masuk yang jelas. */
.lg-mark .dot::before {
  content: ''; position: absolute; inset: -18px; border-radius: 50%; z-index: -1;
  background: radial-gradient(circle, rgba(56,189,248,.32), transparent 68%);
}
.lg-mark .dot::after {
  content: 'A'; font-size: 21px; font-weight: 800; letter-spacing: 0; color: #04121f;
}
.lg-mark-t {
  font-weight: 700; letter-spacing: .30em; font-size: 12.5px;
  color: var(--side-ink-on); text-transform: uppercase;
}

/* ---- tulisan ---- */
.lg-eyebrow {
  display: block; margin: 52px 0 18px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .26em; text-transform: uppercase;
  color: var(--side-accent);
}
.lg-h {
  margin: 0 0 18px; max-width: 16ch;
  font-size: clamp(30px, 3.3vw, 46px);
  line-height: 1.05; font-weight: 600; letter-spacing: -.035em;
  color: #fff;
}
/* Baris kedua diredupkan. Judul dua baris yang sama terangnya terbaca sebagai
   satu blok; yang kedua lebih redup memberi arah baca. */
.lg-h em { font-style: normal; color: rgba(255,255,255,.52); }
.lg-p {
  margin: 0; max-width: 40ch;
  font-size: 14.5px; line-height: 1.7; color: rgba(255,255,255,.56);
}

/* ---- tanggal audit ---- */
/* Satu-satunya angka di sisi ini, jadi dia yang paling menonjol sesudah
   judulnya. Garis tipis di kiri, bukan kotak penuh — kotak membuatnya terbaca
   seperti tombol. */
.lg-date {
  margin: 40px 0 0; padding: 2px 0 2px 18px;
  border-left: 2px solid var(--side-accent);
}
.lg-date-k {
  display: block; margin-bottom: 6px;
  font-size: 10px; letter-spacing: .22em; text-transform: uppercase;
  color: rgba(255,255,255,.42); font-weight: 700;
}
.lg-date b {
  font-size: 26px; font-weight: 600; letter-spacing: -.02em; color: #fff;
  font-variant-numeric: tabular-nums;
}

/* ---- urutan kerja ---- */
/* Garis rambut, bukan nomor berkotak. Nomor berkotak terbaca seperti panduan
   pemula; garis tipis terbaca seperti daftar isi. */
.lg-steps {
  margin: 44px 0 0; padding: 0; list-style: none; counter-reset: s;
  max-width: 38ch; border-top: 1px solid rgba(255,255,255,.08);
}
.lg-steps li {
  counter-increment: s; display: flex; align-items: baseline; gap: 16px;
  padding: 13px 0; border-bottom: 1px solid rgba(255,255,255,.08);
  font-size: 13.5px; color: rgba(255,255,255,.62);
}
.lg-steps li::before {
  content: '0' counter(s);
  font-size: 10.5px; font-weight: 700; letter-spacing: .1em; flex: none;
  color: var(--side-accent); font-variant-numeric: tabular-nums;
}
.lg-side-foot {
  margin: 0; font-size: 11px; letter-spacing: .04em;
  color: rgba(255,255,255,.30);
}

/* ---------------------------------------------------------- kanan */
.lg-main {
  display: grid; place-items: center; padding: 40px 24px;
  background: var(--surface-1);
  position: relative;
}
/* Cahaya yang sangat samar di belakang form. Latar rata terbaca datar; ini
   memberi form-nya tempat tanpa menggambar kotak di sekelilingnya. */
.lg-main::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(600px 420px at 50% 34%, var(--wash), transparent 70%);
}
.lg-form { width: 100%; max-width: 356px; position: relative; }

.lg-mark-sm { display: none; margin-bottom: 30px; }
.lg-mark-sm .dot { width: 38px; height: 38px; border-radius: 12px; }
.lg-mark-sm .dot::after { font-size: 18px; }
.lg-mark-sm .lg-mark-t { color: var(--ink); }

.lg-title {
  margin: 0 0 7px; font-size: 27px; font-weight: 650; letter-spacing: -.025em;
}
.lg-sub { margin: 0 0 30px; font-size: 13.5px; color: var(--muted); }

/* ---- kotak isian ----
   Dilingkupi .lg-form supaya tidak menyentuh form lain di seluruh aplikasi. */
.lg-form .field { margin-bottom: 17px; }
.lg-form .field > span {
  font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 700; color: var(--muted); margin-bottom: 7px;
}
.lg-form .field input {
  height: 46px; padding: 0 14px; font-size: 14.5px;
  border-radius: 11px; background: var(--surface-2);
  border: 1px solid var(--border);
  transition: border-color .14s, box-shadow .14s, background .14s;
}
.lg-form .field input:hover { border-color: color-mix(in srgb, var(--s1) 28%, var(--border)); }
/* Cincin sorot yang lebar dan lembut, bukan garis tebal. */
.lg-form .field input:focus {
  background: var(--surface-1); border-color: var(--s1); outline: 0;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--s1) 14%, transparent);
}

/* ---- tombol ---- */
.lg-form .btn.pri {
  height: 48px; border-radius: 11px; font-size: 14.5px; font-weight: 650;
  letter-spacing: .01em; margin-top: 4px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--s1) 92%, #fff), var(--s1));
  box-shadow: 0 8px 22px color-mix(in srgb, var(--s1) 30%, transparent),
              inset 0 1px 0 rgba(255,255,255,.22);
  transition: transform .12s, box-shadow .16s, filter .16s;
}
.lg-form .btn.pri:hover {
  filter: brightness(1.05);
  box-shadow: 0 10px 28px color-mix(in srgb, var(--s1) 38%, transparent),
              inset 0 1px 0 rgba(255,255,255,.22);
}
.lg-form .btn.pri:active { transform: translateY(1px); }

/* ---- lihat password ---- */
.lg-pw { position: relative; }
.lg-pw input { padding-right: 46px; }
.lg-eye {
  position: absolute; right: 7px; bottom: 7px;
  width: 32px; height: 32px; border: 0; border-radius: 9px;
  background: none; cursor: pointer; font-size: 14px; line-height: 1;
  opacity: .38; filter: grayscale(1); transition: opacity .14s, background .14s;
}
.lg-eye:hover { opacity: .85; background: var(--surface-sunk); }
.lg-eye.on { opacity: 1; filter: none; }

/* ---- Caps Lock ---- */
.lg-caps {
  margin: -8px 0 17px; font-size: 12px; font-weight: 600;
  color: #7d5800; background: color-mix(in srgb, var(--warn) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 38%, transparent);
  border-radius: 9px; padding: 8px 12px;
}
:root[data-theme="dark"] .lg-caps { color: var(--warn); }

.lg-form .alert { border-radius: 11px; margin-bottom: 20px; }
.lg-form .auth-foot { margin-top: 24px; font-size: 11.5px; }

/* ---- gerakan ----
   Sekali, 10px, lalu diam. Halaman yang terus bergerak bikin lelah di malam
   kelima. Dan kalau orangnya minta gerakan dikurangi lewat setelan sistem,
   tidak ada gerakan sama sekali — itu setelan aksesibilitas, bukan selera. */
@keyframes lgUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.lg-side-in > *, .lg-form > * { animation: lgUp .5s cubic-bezier(.22,.7,.3,1) both; }
.lg-side-in > *:nth-child(2) { animation-delay: .05s; }
.lg-side-in > *:nth-child(3) { animation-delay: .09s; }
.lg-side-in > *:nth-child(4) { animation-delay: .13s; }
.lg-side-in > *:nth-child(5) { animation-delay: .17s; }
@media (prefers-reduced-motion: reduce) {
  .lg-side-in > *, .lg-form > * { animation: none; }
}

/* ---- layar sempit ----
   Di bawah 900px sisi kirinya dilepas seluruhnya, bukan ditumpuk di atas
   form. Ditumpuk berarti orangnya harus menggulir melewati hiasan untuk
   sampai ke kotak isian — di ponsel, tiap malam. */
@media (max-width: 900px) {
  .lg-wrap { grid-template-columns: 1fr; }
  .lg-side { display: none; }
  .lg-mark-sm { display: flex; }
  .lg-main { padding: 32px 20px; min-height: 100vh; }
}

/* ---------------------------------------------------------- tabs */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 20px; flex-wrap: wrap; }
.tab {
  background: none; border: 0; padding: 9px 14px;
  font-family: inherit; font-size: 13px; color: var(--muted);
  cursor: pointer; border-bottom: 2px solid transparent; font-weight: 560;
  transition: color .12s;
}
.tab:hover { color: var(--ink-2); }
.tab.on { color: var(--ink); border-bottom-color: var(--s1); font-weight: 640; }
.tabpane { display: none; }
.tabpane.on { display: block; }

/* ---------------------------------------------------------- stat tiles */
.tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 18px; }
@media (max-width: 1040px) { .tiles { grid-template-columns: repeat(2, 1fr); } }

/* Baris yang isinya BUKAN kelipatan empat. Dengan grid empat kolom yang kaku,
   kotak kelima jatuh sendirian di baris kedua dan barisnya terlihat rusak —
   bukan karena isinya salah, tapi karena kotaknya lima. Yang ini menyesuaikan
   diri: lima berjajar kalau muat, kalau tidak dibagi rata, bukan 4 + 1. */
.tiles.t5 { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 1420px) { .tiles.t5 { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 780px)  { .tiles.t5 { grid-template-columns: repeat(2, 1fr); } }
.tile .lbl { color: var(--muted); font-size: 10px; font-weight: 700;
             text-transform: uppercase; letter-spacing: .1em; margin-bottom: 8px; }
.tile .val { font-size: 30px; font-weight: 700; line-height: 1.05; letter-spacing: -.03em; }
.tile .dlt { font-size: 11.5px; color: var(--muted); margin-top: 6px; }

.meter { height: 6px; border-radius: 99px; background: var(--surface-sunk); overflow: hidden; }
.meter i { display: block; height: 100%; border-radius: 99px; background: var(--s1); transition: width .3s; }

/* ---------------------------------------------------------- dashboard */
.dash-hero {
  display: flex; align-items: flex-end; gap: 24px; flex-wrap: wrap;
  padding: 4px 2px 22px;
}
.dash-hello { font-size: 24px; font-weight: 640; letter-spacing: -.02em; margin: 0; }
.dash-sub   { color: var(--muted); font-size: 13px; margin: 5px 0 0; }

.ann-list { display: grid; gap: 14px; }
.announce { position: relative; padding: 20px 22px; margin: 0; }
/* Dulu kartu yang dipin diberi latar gradien biru penuh. Di mode gelap dia jadi
   blok biru besar yang menarik mata lebih kuat daripada angka merah "lewat
   ambang" di atasnya — padahal yang mendesak justru angka itu.
   Sekarang cukup satu garis tegak di kiri: tetap ketahuan dipin, tanpa
   berebut. */
.announce.pinned { border-color: var(--border); }
.announce.pinned::after {
  content: ''; position: absolute; left: 0; top: 12px; bottom: 12px; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--s1);
}
.announce-meta { display: flex; gap: 8px; align-items: center; margin-bottom: 9px; flex-wrap: wrap; }
.announce h3 { font-size: 16px; font-weight: 640; letter-spacing: -.012em; }
.announce p  { font-size: 13.5px; line-height: 1.65; color: var(--ink-2); }
/* Kaki pengumuman jadi BARIS, bukan tumpukan.
   Sebelum ada tombol "Sudah saya baca" isinya cuma satu kalimat, jadi bentuk
   blok sudah cukup. Begitu tombolnya masuk, dia jatuh ke bawah nama penulisnya
   dan menempel di kiri — kelihatan seperti tombol yang salah taruh. Ketahuan
   dari GAMBARNYA, bukan dari membaca CSS-nya. */
.announce-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 13px; padding-top: 11px; border-top: 1px solid var(--grid);
  font-size: 11.5px; color: var(--muted);
}

.quick { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 22px; }
@media (max-width: 900px) { .quick { grid-template-columns: 1fr; } }
.quick a {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 16px; border-radius: var(--r);
  border: 1px solid var(--border); background: var(--surface-1);
  box-shadow: var(--sh-1); text-decoration: none; color: var(--ink);
  transition: border-color .12s, transform .1s, box-shadow .12s;
}
.quick a:hover { border-color: var(--s1); transform: translateY(-1px); box-shadow: var(--sh-2); text-decoration: none; }
/* Ikon pintasan ikut jadi SVG satu warna — dulu emoji berwarna (jam, grafik,
   gembok), tiga palet lagi di layar yang sama. Alasnya persegi membulat supaya
   sekeluarga dengan ikon sidebar. */
.quick .qi {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 10px;
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--border);
  color: var(--muted); transition: color .12s, background .12s, border-color .12s;
}
.quick .qi svg { width: 17px; height: 17px; display: block; }
.quick a:hover .qi {
  color: var(--s1);
  background: color-mix(in srgb, var(--s1) 10%, var(--surface-2));
  border-color: color-mix(in srgb, var(--s1) 28%, var(--border));
}
.quick > a > span:nth-child(2) { min-width: 0; }
.quick .qt { display: block; font-weight: 600; font-size: 13.5px; }
.quick .qd { display: block; font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.quick .qa { margin-left: auto; color: var(--muted); font-size: 15px; }

/* ---------------------------------------------------------- jobdesk */
.entry-head { display: flex; gap: 12px; align-items: center; margin-bottom: 18px; flex-wrap: wrap; }
.entry-head select { min-width: 250px; padding: 8px 12px; font-size: 13.5px; background: var(--surface-1); }
.entry-grid { display: grid; grid-template-columns: 1fr 288px; gap: 16px; align-items: start; }
@media (max-width: 1120px) { .entry-grid { grid-template-columns: 1fr; } }
.side-col { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 84px; }
.side-panel { position: sticky; top: 84px; }

.website-totals {
  display: flex; gap: 16px; align-items: flex-end;
  background: var(--surface-sunk); border: 1px solid var(--border);
  border-radius: var(--r); padding: 10px 14px;
}
.website-totals label { display: block; }
.website-totals label span, .wt-pct span {
  display: block; font-size: 9.5px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted); font-weight: 700; margin-bottom: 5px;
}
.website-totals .inp { width: 90px; }
.wt-pct b { font-size: 17px; font-weight: 660; display: block; padding-bottom: 5px; }
.wt-pct b.over { color: var(--crit); }
@media (max-width: 900px) { .website-totals { width: 100%; margin-top: 10px; } }

/* ============================================================
   Tabel CS — abby: "tmpilan yang disini sngat berantakan"
   ============================================================
   Tiga hal yang membuat tabel ini ramai, dan cara masing-masing dibereskan:

   1. TINGGI BARIS BERUBAH-UBAH. Baris yang sedang berjalan punya dua tombol,
      baris "diserahkan" punya catatan baris kedua, baris kosong cuma satu
      tanda hubung. Akibatnya 19 baris itu naik-turun waktu ada satu tombol
      ditekan, dan mata kehilangan tempatnya. Sekarang tingginya dipatok.

   2. KATA YANG MENGULANG WARNA. "✓ Audited" di sebelah baris yang sudah
      berwarna hijau tidak menambah apa pun. Yang tersisa: ikon + nama.

   3. TOMBOL MILIK ATASAN IKUT TERGAMBAR DI TIAP BARIS. Tiga tombol dikali 19
      baris. Sekarang masuk ke menu ⋯ — masih satu klik, tapi tidak ikut
      dilihat waktu tidak dicari. */

.cs-table td { padding: 0 12px; height: 40px; }
.cs-table th { height: 34px; }
.cs-table .inp { width: 100px; }

/* Pita warna tipis di kiri. Inilah pengganti semua kata yang dibuang:
   status baris terbaca dari jauh tanpa satu pun huruf. */
.cs-table tbody tr > td:first-child { box-shadow: inset 3px 0 0 transparent; }
tr.st-me   > td:first-child { box-shadow: inset 3px 0 0 var(--s1) !important; }
tr.st-run  > td:first-child { box-shadow: inset 3px 0 0 var(--warn) !important; }
tr.st-hand > td:first-child { box-shadow: inset 3px 0 0 var(--m-jobdesk) !important; }
tr.st-done > td:first-child { box-shadow: inset 3px 0 0 var(--good) !important; }
/* DITUTUP SISTEM: pita --crit.
   Versi pertama memakai --warn, dan tesnya langsung merah dengan alasan yang
   benar: --warn sudah dipakai "sedang dibaca orang lain", dan dua keadaan
   yang sewarna berarti pitanya berhenti membedakan apa pun.

   Yang dipilih menggantikannya MERAH, bukan warna baru yang netral. Ini
   satu-satunya keadaan di tabel ini di mana barisnya SEBELUMNYA BERBOHONG:
   dia terlihat selesai padahal chat-nya tidak pernah dibaca siapa pun. Di
   antara lima belas baris jam tiga pagi, itu yang harus paling cepat
   ketahuan. Dan dia jarang — nol atau satu baris per situs — jadi merahnya
   tidak membanjiri apa pun.

   Baris ini juga SENGAJA tidak diberi latar seperti tr.st-done: latar hijau
   di halaman ini berarti "selesai", dan baris yang tombolnya masih Start
   tidak boleh memakainya. */
tr.st-auto > td:first-child { box-shadow: inset 3px 0 0 var(--crit) !important; }

/* Baris selesai diredupkan sedikit. Yang belum dikerjakan yang harus menonjol,
   bukan sebaliknya — di layar penuh baris hijau, yang dicari justru yang bukan
   hijau. */
tr.st-done { background: color-mix(in srgb, var(--good) 5%, transparent); }

/* Diredupkan dengan WARNA, bukan `opacity`.
 *
 * `opacity` di bawah 1 membuat elemennya jadi stacking context sendiri. Waktu
 * itu dipasang di `<td>`, menu ⋯ yang position:absolute di dalamnya jadi
 * TERKURUNG di situ — z-index 40-nya cuma berlaku di dalam sel itu, tidak
 * terhadap seluruh halaman. Akibatnya sel-sel baris di BAWAHNYA (yang juga
 * hijau, jadi juga punya stacking context sendiri) tergambar sesudahnya dan
 * merebut kliknya.
 *
 * Yang bikin ini jahat: sel-sel itu TRANSPARAN — latar hijaunya ada di <tr>,
 * bukan di <td>. Jadi menunya kelihatan utuh, tidak ada yang menutupinya di
 * layar, dan kliknya jatuh ke sel tak terlihat milik baris lain.
 *
 * abby: "kok ga bisa tekan beb?" — tombolnya kelihatan sempurna.
 *
 * Warna tidak membuat stacking context, jadi masalahnya hilang di akarnya,
 * bukan cuma dihindari untuk satu sel. */
tr.st-done td:first-child b,
tr.st-done .cs-dur,
tr.st-done .inp { color: var(--muted); }
tr.cs-running { background: var(--wash) !important; }

/* Satu bentuk untuk semua status: ikon lalu nama. Tidak ada kotak, tidak ada
   garis tepi — 19 badge bertepi dalam satu kolom itu sendiri yang ramai. */
.cst { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px;
       line-height: 1; white-space: nowrap; max-width: 100%; }
.cst > i { font-style: normal; font-size: 12px; line-height: 1; flex: none; }
.cst > b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.cst.done { color: var(--good); }
.cst.run  { color: var(--warn); }
.cst.hand { color: var(--m-jobdesk); }
.cst.me   { color: var(--s1); }
.cst.none { color: var(--muted); font-weight: 500; }
/* Sesi yang DITUTUP SISTEM. Merah, sewarna dengan pita barisnya — keadaan ini
   berarti ada chat yang TIDAK PERNAH DIBACA sementara barisnya terlihat
   selesai. Itu satu-satunya baris di tabel ini yang pernah berbohong, dan
   warnanya harus mengatakannya. */
.cst.auto { color: var(--crit); }
.cst.live > i { animation: pulse 1.8s ease-in-out infinite; }
/* Gemboknya emoji berwarna; di baris yang sudah hijau, kuningnya berebut
   perhatian dengan hal yang justru harus menonjol — baris yang BELUM selesai. */
.cst.lock { filter: grayscale(1); opacity: .5; }
.badge.live { animation: pulse 1.8s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .5 } }
.live-clock { font-variant-numeric: tabular-nums; font-weight: 700; margin-left: 3px; }

.totals-row { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.totals-row b { font-size: 17px; }

.stat-line { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--grid); }
.stat-line:last-of-type { border-bottom: 0; }
.stat-line b { font-size: 17px; font-weight: 640; }

.check-list { display: grid; gap: 10px; }
.ck {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 15px; border: 1px solid var(--border);
  border-radius: var(--r); cursor: pointer; background: var(--surface-1);
  transition: border-color .12s, background .12s;
}
.ck:hover { background: var(--surface-2); border-color: var(--axis); }
.ck:has(input:disabled) { opacity: .5; cursor: not-allowed; }
.ck input { margin-top: 2px; width: 17px; height: 17px; accent-color: var(--s1); cursor: pointer; flex: 0 0 auto; }
.ck.done { border-color: color-mix(in srgb, var(--good) 40%, transparent); background: color-mix(in srgb, var(--good) 6%, transparent); }
.ck b { display: block; font-size: 13.5px; font-weight: 620; }
.ck span { font-size: 12px; color: var(--muted); }
.clear-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.toast {
  position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 20px);
  background: var(--crit); color: #fff; padding: 12px 20px;
  border-radius: var(--r); font-size: 13px; font-weight: 550;
  opacity: 0; pointer-events: none; transition: opacity .18s, transform .18s;
  z-index: 200; max-width: 560px; text-align: center; box-shadow: var(--sh-2);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ---------------------------------------------------------- credential vault */
.site-toolbar { display: flex; align-items: center; gap: 8px; padding: 0 0 12px; font-size: 12px; }
.cred-site {
  border: 1px solid var(--border); border-radius: var(--r);
  margin-bottom: 8px; background: var(--surface-1); overflow: hidden;
}
.cred-site summary {
  cursor: pointer; padding: 10px 14px; display: flex; align-items: center; gap: 10px;
  list-style: none; user-select: none; transition: background .12s;
}
.cred-site summary::-webkit-details-marker { display: none; }
.cred-site summary::before { content: '▸'; color: var(--muted); font-size: 11px; transition: transform .14s; }
.cred-site[open] summary::before { transform: rotate(90deg); }
.cred-site[open] summary { border-bottom: 1px solid var(--grid); background: var(--surface-2); }
.cred-site summary:hover { background: var(--wash); }
.cs-code { font-weight: 650; font-size: 13px; }
.cs-counts { display: flex; gap: 5px; flex-wrap: wrap; margin-left: auto; }
.cred-table { margin: 0; }
.cred-table td { padding: 7px 14px; border-bottom: 1px solid var(--grid); font-size: 12.5px; vertical-align: middle; }
.cred-table tr:last-child td { border-bottom: 0; }
.cred-table tr.mine { background: var(--wash); }
.ct-label { font-weight: 600; }
.ct-type   { width: 92px; }
/* nowrap: "7 · tanpa batas" tidak muat di 56px dan pecah jadi TIGA baris,
   membuat barisnya dua kali lebih tinggi dari tetangganya. 56px itu lebar
   minimum, bukan lebar pasti — jadi kolomnya cuma melebar di tabel yang
   memang berisi kredensial seamless, dan tabel lain tetap 56px. */
.ct-slots  { width: 56px; white-space: nowrap; text-align: right; color: var(--muted); font-weight: 640; }
.ct-slots.full { color: var(--crit); }
.ct-holder { width: 200px; font-size: 11.5px; }
.ct-holder .hold { margin-right: 3px; }
.ct-holder form, .ct-actions form { display: inline; margin: 0; }
.ct-actions { width: 300px; text-align: right; white-space: nowrap; }
.ct-actions .btn { margin-left: 3px; }

/* side panel groups */
.cred-group { margin-bottom: 18px; }
.cred-group:last-child { margin-bottom: 0; }
.cg-title {
  font-size: 9.5px; letter-spacing: .12em; font-weight: 700; color: var(--muted);
  padding-bottom: 8px; border-bottom: 1px solid var(--grid); margin-bottom: 11px;
}
.cred-item { border-left: 3px solid var(--grid); padding: 9px 0 10px 12px; margin-bottom: 10px; }
.cred-item:last-child { margin-bottom: 0; }
.g-lc   .cred-item { border-left-color: var(--s1); }
.g-adm  .cred-item { border-left-color: var(--crit); }
/* Hijau, bukan merah. Merah di panel ini berarti "langka, antre" — dan
   seamless adalah satu-satunya kelompok yang tidak pernah perlu diantre. */
.g-sl   .cred-item { border-left-color: var(--good); }
.g-priv .cred-item { border-left-color: var(--s4); }
.cred-item.mine { background: var(--wash); border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.ci-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.ci-name { font-weight: 650; font-size: 13px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ci-slots { font-size: 11px; font-variant-numeric: tabular-nums; color: var(--muted); font-weight: 620; }
.ci-slots.full { color: var(--crit); }
.ci-type { font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
.ci-holders { font-size: 11px; color: var(--ink-2); margin-bottom: 8px; line-height: 1.5; }
.ci-btns { display: flex; gap: 5px; }
.ci-btns .btn { flex: 1; padding: 5px 6px; font-size: 11.5px; white-space: nowrap; }
.ci-btns .ci-open, .ci-btns .ci-release { flex: 0 0 32px; text-align: center; }
.ci-blocked {
  font-size: 11px; color: var(--crit);
  background: color-mix(in srgb, var(--crit) 8%, transparent);
  border-radius: var(--r-sm); padding: 7px 9px;
}
.ci-note { font-size: 11px; color: var(--muted); margin-top: 6px; }

/* ---------------------------------------------------------- misc */
.cs-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
@media (max-width: 760px) { .cs-grid { grid-template-columns: repeat(2, 1fr); } }
.cs-cell { display: flex; align-items: center; gap: 10px; }
.cs-cell span { width: 58px; font-size: 12.5px; color: var(--ink-2); }
.cs-cell .inp { flex: 1; min-width: 0; }
.hero { font-size: 50px; font-weight: 660; line-height: 1.05; letter-spacing: -.025em; color: var(--s1); }
.timer-row  { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.timer-btns { display: flex; gap: 9px; }
.timer-speed { margin-left: auto; text-align: right; }
.speed-val { font-size: 17px; font-weight: 640; }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--axis); border-radius: 99px; border: 3px solid var(--page); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* ---------------------------------------------------------- violations */
.frow { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
@media (max-width: 820px) { .frow { grid-template-columns: 1fr; } }
.vio-desc { color: var(--ink-2); font-size: 12.5px; }

.ev-grid { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.ev-item { width: 148px; }
.ev-item img {
  width: 148px; height: 104px; object-fit: cover;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-sunk); display: block;
}
.ev-item form { margin-top: 6px; }

.vshow { display: grid; grid-template-columns: 1fr 320px; gap: 16px; align-items: start; }
@media (max-width: 1120px) { .vshow { grid-template-columns: 1fr; } }
table.kv td { padding: 9px 0; border-bottom: 1px solid var(--grid); vertical-align: top; }
table.kv tr:last-child td { border-bottom: 0; }
table.kv td:first-child { width: 165px; color: var(--muted); font-size: 12px; }
table.kv tr:hover { background: transparent; }

.cmt-list { display: grid; gap: 10px; margin-top: 12px; }
.cmt { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 10px 13px; font-size: 13px; }
.cmt-head { display: flex; gap: 8px; align-items: baseline; margin-bottom: 3px; font-size: 12px; }

.ckin { font-size: 12px; color: var(--ink-2); display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.ckin input { accent-color: var(--s1); }

/* ---------------------------------------------------------- violation flow */
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 900px) { .grid2 { grid-template-columns: 1fr; } }
ul.plain { margin: 10px 0 0; padding-left: 18px; font-size: 13px; color: var(--ink-2); line-height: 1.9; }

.vstatus { display: flex; align-items: stretch; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.vs-step {
  flex: 1; min-width: 220px; display: flex; gap: 12px; align-items: flex-start;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r); padding: 14px 16px; box-shadow: var(--sh-1);
}
.vs-step .vs-n {
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 12px; font-weight: 700;
  background: var(--surface-sunk); color: var(--muted);
}
.vs-step b { display: block; font-size: 13.5px; }
.vs-step span { display: block; font-size: 12px; color: var(--ink-2); }
.vs-step.now  { border-color: color-mix(in srgb, var(--s1) 40%, transparent); background: var(--wash); }
.vs-step.now  .vs-n { background: var(--s1); color: #fff; }
.vs-step.done { border-color: color-mix(in srgb, var(--good) 35%, transparent); }
.vs-step.done .vs-n { background: var(--good); color: #fff; }
.vs-arrow { display: grid; place-items: center; color: var(--muted); font-size: 18px; }
@media (max-width: 700px) { .vs-arrow { display: none; } }

/* ---------------------------------------------------------- violation grid */
.gridwrap { overflow-x: auto; margin: 0 -20px; padding: 0 20px; }

/* The channel board freezes its first column, and `left: 0` lands on the
   scroll container's PADDING edge — so a 20px gutter stays uncovered and the
   scrolled-away channels peek through it. Bleed the padding out instead. */
.gridcard .gridwrap { padding-left: 0; }
.gridcard .gridwrap table.wagrid .wg-lbl { padding-left: 20px; }
table.vgrid { min-width: 640px; }
table.vgrid th {
  padding: 8px 10px; font-size: 9.5px; letter-spacing: .09em; white-space: nowrap;
  border-bottom: 1px solid var(--border); vertical-align: bottom;
}
table.vgrid th.vg-fatal { color: var(--crit); }
table.vgrid .vg-pt { display: block; font-size: 9px; font-weight: 500; opacity: .7; letter-spacing: 0; }
table.vgrid td { padding: 4px 6px; border-bottom: 1px solid var(--grid); vertical-align: top; }
table.vgrid td.vg-case, table.vgrid th.vg-case { width: 84px; padding-left: 10px; font-size: 12.5px; vertical-align: middle; color: var(--ink-2); }
table.vgrid td.vg-x, table.vgrid th.vg-x { width: 40px; text-align: right; padding-right: 10px; vertical-align: middle; }
table.vgrid td.vg-x .btn { opacity: 0; transition: opacity .12s; }
table.vgrid tr:hover td.vg-x .btn, table.vgrid td.vg-x .btn:focus { opacity: 1; }
.vg-in[readonly] { background: var(--surface-sunk); color: var(--muted); cursor: not-allowed; }
table.vgrid tbody tr:hover { background: var(--surface-2); }
.vg-in {
  width: 100%; min-width: 92px; padding: 6px 8px;
  border: 1px solid transparent; border-radius: 6px;
  background: var(--surface-2); color: var(--ink);
  font-family: inherit; font-size: 12.5px;
  transition: border-color .1s, background .1s;
}
.vg-in::placeholder { color: var(--muted); opacity: .5; }
.vg-in:hover  { border-color: var(--border); }
.vg-in:focus  { outline: none; border-color: var(--s1); background: var(--surface-1); box-shadow: 0 0 0 3px var(--wash); }
.vg-in.pending{ opacity: .5; }
.vg-in.err    { border-color: var(--crit); background: color-mix(in srgb, var(--crit) 8%, transparent); }
.vg-chips { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 3px; }
.vg-chip {
  font-size: 10px; padding: 1px 6px; border-radius: 99px; text-decoration: none;
  border: 1px solid var(--border); white-space: nowrap;
}
.vg-chip.need { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 40%, transparent);
                background: color-mix(in srgb, var(--crit) 8%, transparent); }
.vg-chip.lock { color: var(--muted); background: var(--surface-sunk); }
.vg-chip:hover { text-decoration: none; filter: brightness(1.05); }
td.vg-tot, th.vg-tot { width: 68px; padding-right: 12px; color: var(--muted); vertical-align: middle; }
td.vg-tot.has { color: var(--ink); font-weight: 660; }
.grid-legend { display: flex; align-items: baseline; gap: 6px; font-size: 12px; }
.grid-legend b { font-size: 16px; }
.gridbar { display: flex; align-items: center; gap: 12px; padding: 0 0 12px; flex-wrap: wrap; }
.gb-add { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--ink-2); }
.gb-add select { padding: 5px 9px; font-size: 12.5px; min-width: 120px; }
.vg-flash { animation: vgflash 0.9s ease-out; }
@keyframes vgflash { from { background: var(--wash-firm); } to { background: transparent; } }

/* ---- inline crosscheck ---- */
.cclist td { vertical-align: middle; }
.cclist .cc-act { text-align: right; white-space: nowrap; }
.cclist .cc-act .btn { margin-left: 4px; }
.cclist tr.cc-done { opacity: .62; }
.cc-whybox { display: flex; gap: 7px; align-items: center; padding: 4px 0 8px; flex-wrap: wrap; }
.cc-whybox .cc-note { flex: 1; min-width: 220px; padding: 6px 10px; font-size: 12.5px; }

/* ============================================================
   Channel Review — WhatsApp SOP checklist
   ============================================================ */

.wa-rule {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 13px 15px 14px;
  margin-bottom: 11px;
  background: var(--surface-1);
}
.wa-rule.overdue { border-color: color-mix(in srgb, var(--crit) 42%, transparent); }
.wa-rule.extra   { border-style: dashed; }

.wa-rule-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.wa-rule-name { flex: 1; min-width: 190px; }
.wa-rule-name b { font-size: 14px; font-weight: 640; display: block; }
.wa-freq {
  display: inline-block; margin-top: 2px;
  font-size: 11.5px; color: var(--muted);
  letter-spacing: .02em;
}
.wa-desc { margin: 6px 0 0 !important; font-size: 12.5px; color: var(--ink-2); }
.wa-last { display: flex; align-items: center; gap: 8px; font-size: 11.5px; }

.wa-slots { display: flex; flex-direction: column; gap: 8px; margin-top: 11px; }
.wa-slot  { border-left: 3px solid var(--border); padding-left: 11px; }
.wa-slot.s-ok       { border-left-color: var(--good); }
.wa-slot.s-missing,
.wa-slot.s-wrong    { border-left-color: var(--crit); }
.wa-slot.s-revision { border-left-color: var(--warn); }
.wa-slot.busy       { opacity: .55; pointer-events: none; }
.wa-slot.needs-note .wa-note-in {
  border-color: var(--warn);
  background: color-mix(in srgb, var(--warn) 9%, transparent);
}
.wa-slot-n {
  display: block; font-size: 11px; font-weight: 640;
  color: var(--muted); text-transform: uppercase; letter-spacing: .08em;
  margin-bottom: 5px;
}

.wa-btns { display: flex; gap: 6px; flex-wrap: wrap; }
.wa-b {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px;
  font: inherit; font-size: 12.5px; font-weight: 560;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.wa-b:hover:not(:disabled) { border-color: var(--muted); }

/* On dark, surface-2 sits almost on top of the card, so an unmarked button
   reads as disabled. Lift it and use full ink instead. */
:root[data-theme="dark"] .wa-b {
  color: var(--ink);
  background: rgba(255,255,255,0.05);
  border-color: rgba(255,255,255,0.14);
}
:root[data-theme="dark"] .wa-b:disabled { color: var(--ink-2); }
.wa-b:disabled { opacity: .45; cursor: not-allowed; }
.wa-b.on.wa-ok   { color: #fff; background: var(--good); border-color: transparent; }
.wa-b.on.wa-bad  { color: #fff; background: var(--crit); border-color: transparent; }
.wa-b.on.wa-warn { color: #3d2b00; background: var(--warn); border-color: transparent; }

/* On narrow screens the words go and only the mark stays. */
@media (max-width: 720px) { .wa-b span { display: none; } .wa-b { padding: 7px 12px; } }

.wa-note { margin-top: 8px; }
.wa-note-in { width: 100%; max-width: 520px; padding: 7px 11px; font-size: 12.5px; }

.wa-extra-head {
  margin: 20px 0 9px;
  font-size: 11.5px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.wa-rm { margin: 0; }

.wa-add { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.wa-add input[type=text] { flex: 1; min-width: 240px; }
.wa-add-hint { margin-top: 8px !important; font-size: 12px; color: var(--muted); }

.wa-foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.wa-foot .sub { margin-top: 3px; }
.wa-cc { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.wa-cc input[type=text] { min-width: 220px; }

/* day picker on the SOP settings page */
.daypick { display: flex; gap: 7px; flex-wrap: wrap; }
.daychip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 11px; font-size: 12.5px;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--surface-2); cursor: pointer;
}
.daychip input { accent-color: var(--s1); }
.daychip:has(input:checked) {
  border-color: var(--s1);
  background: color-mix(in srgb, var(--s1) 11%, transparent);
  color: var(--s1);
  font-weight: 600;
}
.inline-form { margin: 0; }

/* ============================================================
   Channel Review — grid, laid out like the team's spreadsheet:
   SOP down the side, channels across the top
   ============================================================ */

.wagrid-hint { font-size: 12.5px; color: var(--muted); margin-bottom: 12px; }

table.wagrid { border-collapse: separate; border-spacing: 0; }
table.wagrid th, table.wagrid td {
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
}

/* ---- the label column, frozen while channels scroll sideways ---- */
table.wagrid .wg-lbl {
  position: sticky; left: 0; z-index: 5;
  background: var(--surface-1);
  text-align: left;
  padding: 8px 14px 8px 12px;
  min-width: 158px; max-width: 158px;
  font-size: 11.5px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-2);
  border-right: 2px solid var(--border);
  box-shadow: 3px 0 6px -3px rgba(20,20,26,0.12);
  vertical-align: middle;
}
:root[data-theme="dark"] table.wagrid .wg-lbl { box-shadow: 3px 0 6px -3px rgba(0,0,0,0.5); }
table.wagrid thead .wg-lbl { z-index: 6; text-transform: none; letter-spacing: 0; }
.wg-date { display: block; font-size: 13px; font-weight: 700; color: var(--ink); }
.wg-sub  { display: block; font-size: 10.5px; font-weight: 600; color: var(--muted);
           letter-spacing: .09em; text-transform: uppercase; margin-top: 1px; }

/* ---- channel headers ---- */
table.wagrid thead th.wg-head {
  position: sticky; top: 0; z-index: 4;
  background: var(--surface-1);
  padding: 9px 8px;
  /* one fixed width for every channel — uneven columns were most of what
     made the board look untidy */
  width: 150px; min-width: 150px; max-width: 150px;
  text-align: center;
  vertical-align: middle;
  /* the generic table header rule shouts everything in caps; a site code and
     a person's name should not */
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}
.wg-head.submitted .wg-name { color: var(--ink-2); }
.wg-owner { display: block; font-size: 10.5px; font-weight: 400; color: var(--muted); margin-top: 1px; }

/* ---- rows ---- */
table.wagrid td.wg-cell {
  padding: 6px 8px;
  width: 150px; min-width: 150px; max-width: 150px;
  height: 52px;                 /* keeps rows level whether or not there is a note */
  text-align: center;
  vertical-align: middle;
  background: var(--surface-1);
}
tr.wg-r:hover td.wg-cell,
tr.wg-r:hover .wg-lbl { background: var(--surface-2); }

/* Link alternatif gets its own band, the way the sheet marks it out.
   The band MUST be opaque: the label column is sticky, and a translucent
   background lets the channels scroll visibly straight through it. */
tr.wg-r.weekly .wg-lbl {
  color: var(--s1);
  background: color-mix(in srgb, var(--s1) 7%, var(--surface-1));
}
tr.wg-r.weekly td.wg-cell { background: color-mix(in srgb, var(--s1) 7%, var(--surface-1)); }
tr.wg-r.weekly:hover td.wg-cell,
tr.wg-r.weekly:hover .wg-lbl { background: color-mix(in srgb, var(--s1) 13%, var(--surface-1)); }

tr.wg-r.extra .wg-lbl { font-style: italic; text-transform: none; letter-spacing: 0; }
/* needs the table prefix to outrank `table.wagrid td.wg-cell` above */
table.wagrid td.wg-cell.empty { background: var(--surface-sunk); }

/* A submitted channel is read-only. Without a mark it looks exactly like an
   open one and people click at it until they give up. */
table.wagrid td.wg-cell.locked { background: var(--surface-sunk); }
.wg-cell.locked .wg-box { cursor: default; opacity: .8; border-style: dashed; }
.wg-cell.locked .wg-box:hover { border-color: var(--border); }
.wg-head.submitted { background: var(--surface-sunk); }
.wg-head.submitted .wg-name::after {
  content: ' \1F512';
  font-size: 9px;
  opacity: .5;
}

/* remove a broadcast straight from its row label */
.wg-rmrow { display: inline; }
.wg-rmbtn {
  border: 0; background: transparent; cursor: pointer;
  color: var(--muted); font-size: 14px; line-height: 1; padding: 0 3px;
}
.wg-rmbtn:hover { color: var(--crit); }

/* ---- the box you click ---- */
.wg-box {
  width: 40px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface-2);
  cursor: pointer;
  padding: 0 7px;
  font: inherit; font-size: 12px;
  /* border only — animating the background makes the whole grid flash
     when the light/dark toggle flips */
  transition: border-color .1s;
}
.wg-box:hover { border-color: var(--muted); }
.wg-mark { width: 13px; height: 13px; display: block; }

.wg-cell.s-ok       .wg-box { background: color-mix(in srgb, var(--good) 15%, transparent); border-color: color-mix(in srgb, var(--good) 45%, transparent); }
.wg-cell.s-missing  .wg-box,
.wg-cell.s-wrong    .wg-box { background: color-mix(in srgb, var(--crit) 15%, transparent); border-color: color-mix(in srgb, var(--crit) 48%, transparent); }
.wg-cell.s-revision .wg-box { background: color-mix(in srgb, var(--warn) 20%, transparent); border-color: color-mix(in srgb, var(--warn) 55%, transparent); }
.wg-cell.overdue    .wg-box { border-color: color-mix(in srgb, var(--crit) 45%, transparent); }

/* The mark is drawn in CSS so the cell never reflows when it changes. */
.wg-cell.s-ok       .wg-mark::before { content: '\2713'; color: var(--good); font-weight: 800; }
.wg-cell.s-missing  .wg-mark::before { content: '\2715'; color: var(--crit); font-weight: 800; }
.wg-cell.s-wrong    .wg-mark::before { content: '!';      color: var(--crit); font-weight: 800; }
.wg-cell.s-revision .wg-mark::before { content: '\270E';  color: #7d5800; font-weight: 700; }
:root[data-theme="dark"] .wg-cell.s-revision .wg-mark::before { color: var(--warn); }

/* ---- Link alternatif: answers with a date, not a tick ---- */
.wg-weekly { flex-direction: column; gap: 0; line-height: 1.15; width: 66px; }
.wg-ada {
  font-size: 11.5px; font-weight: 750; letter-spacing: .04em;
  text-transform: lowercase; color: var(--good);
}
.wg-cell.s-ok .wg-weekly { background: color-mix(in srgb, var(--good) 22%, transparent); }
.wg-when { font-size: 11.5px; font-weight: 600; }
.wg-ago  { font-size: 9.5px; color: var(--muted); }
.wg-cell.overdue .wg-when,
.wg-cell.overdue .wg-ago { color: var(--crit); }

/* ---- the note, inside the cell like the spreadsheet ---- */
.wg-cnote {
  margin-top: 4px;
  font-size: 10.5px; line-height: 1.3;
  color: var(--crit);
  text-align: center;
  /* clamp to two lines; the full text is on the cell's title attribute */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.ev-chip {
  display: inline-block;
  margin-top: 1px;
  padding: 1px 6px;
  font-size: 10px; font-weight: 600;
  color: var(--s1);
  background: var(--wash);
  border-radius: 20px;
  white-space: nowrap;
}
.ev-chip:hover { background: var(--wash-firm); }

/* ---- add + submit rows ---- */
tr.wg-addrow td.wg-cell { padding: 3px 8px; height: auto; }
tr.wg-addrow .wg-lbl { font-size: 10px; color: var(--muted); font-weight: 600; }
.wg-add {
  width: 24px; height: 24px; padding: 0;
  font: inherit; font-size: 14px; line-height: 1;
  color: var(--muted);
  border: 1px dashed var(--border); border-radius: 50%;
  background: transparent; cursor: pointer;
}
.wg-add:hover { color: var(--s1); border-color: var(--s1); }
.wg-dash { color: var(--border); font-size: 12px; }

tr.wg-donerow td.wg-cell,
tr.wg-donerow .wg-lbl { border-bottom: 0; padding-top: 9px; padding-bottom: 4px; height: auto; }
tr.wg-donerow .wg-lbl { color: var(--muted); }

/* ---- the popover editor ---- */
.wg-pop {
  position: absolute; z-index: 60;
  width: 340px; max-width: calc(100vw - 28px);
  padding: 12px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--sh-2);
}
.wg-pop-kind { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 9px; }
.wg-k {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 9px;
  font: inherit; font-size: 11.5px; font-weight: 560;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 6px;
  cursor: pointer;
}
.wg-k.on.wg-ok   { color: #fff; background: var(--good); border-color: transparent; }
.wg-k.on.wg-bad  { color: #fff; background: var(--crit); border-color: transparent; }
.wg-k.on.wg-warn { color: #3d2b00; background: var(--warn); border-color: transparent; }
:root[data-theme="dark"] .wg-k { color: var(--ink); background: rgba(255,255,255,0.05); border-color: rgba(255,255,255,0.14); }

.wg-pop input[type=text] { width: 100%; padding: 7px 10px; font-size: 12.5px; }
.wg-pop-act { display: flex; gap: 6px; align-items: center; margin-top: 9px; }

/* leader broadcast — one extra check across many channels */
.cast-card { margin-bottom: 16px; }
.cast-form { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.cast-form input[type=text] { flex: 1; min-width: 250px; }
.cast-form select { min-width: 230px; }
.cast-list { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.cast-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0; font-size: 13px;
  border-bottom: 1px solid var(--border);
}
.cast-row:last-child { border-bottom: 0; }

/* Channel Review — recap & report */
pre.report {
  margin: 0;
  padding: 16px 18px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 460px;
  overflow: auto;
  /* selecting the block is the clipboard fallback, so make that readable */
  user-select: text;
}

.rc-site { padding: 13px 0; border-bottom: 1px solid var(--border); }
.rc-site:last-child { border-bottom: 0; padding-bottom: 0; }
.rc-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.rc-head b { font-size: 14px; }

.rc-lines { list-style: none; margin: 9px 0 0; padding: 0; }
.rc-lines li {
  padding: 5px 0 5px 11px;
  border-left: 3px solid color-mix(in srgb, var(--crit) 45%, transparent);
  margin-bottom: 5px;
}
.rc-txt { font-size: 13px; font-weight: 560; }
.rc-note { margin-top: 2px; font-size: 12.5px; color: var(--ink-2); word-break: break-word; }

/* Channel Review — grid header link + the channel-links editor */
.wg-name { font-size: 13px; font-weight: 680; letter-spacing: -.01em; }
.wg-name.nolink { color: var(--ink); cursor: default; }
.wg-ext { font-size: 9px; opacity: .5; }
.wg-meta {
  display: block; margin-top: 1px;
  font-size: 10px; font-weight: 400; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wg-meta a { color: var(--muted); text-decoration: none; }
.wg-meta a:hover { color: var(--s1); text-decoration: underline; }

.linkgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; }
@media (max-width: 900px) { .linkgrid { grid-template-columns: 1fr; } }
.linkrow { display: flex; align-items: center; gap: 10px; }
.linkcode {
  flex: 0 0 128px;
  font-size: 12.5px; font-weight: 620;
}
.linkcode i {
  display: block; font-style: normal;
  font-size: 10.5px; font-weight: 400; color: var(--muted);
}
.linkrow input { flex: 1; min-width: 0; padding: 7px 10px; font-size: 12.5px; }

/* reopen, sitting quietly next to the status badge */
.wg-reopen {
  border: 0; background: transparent; cursor: pointer;
  font-size: 12px; line-height: 1; padding: 2px 3px;
  opacity: .5;
}
.wg-reopen:hover { opacity: 1; }

/* ============================================================
   Leave — request form, approvals, ADM report
   ============================================================ */

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

.lvrows { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.lvrow {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr 92px;
  gap: 12px; align-items: end;
}
.lvrow .field { margin-bottom: 0; }
.lvrow.extra { padding-top: 10px; border-top: 1px dashed var(--border); }
@media (max-width: 780px) {
  .lvrow { grid-template-columns: 1fr 1fr; }
  .lvrow .field span.sr { position: static; width: auto; height: auto; clip: auto; }
}

.lvdays {
  font-size: 12.5px; font-weight: 620; color: var(--muted);
  padding-bottom: 10px; white-space: nowrap;
}
.lvdays.on  { color: var(--s1); }
.lvdays.bad { color: var(--crit); }

.lvpreview {
  margin: 14px 0;
  padding: 9px 13px;
  font-size: 12.5px; font-weight: 560;
  color: var(--s1);
  background: var(--wash);
  border-radius: var(--r-sm);
}

.lvline { font-size: 13px; padding: 2px 0; }
.lvline b { margin-right: 7px; }

.lvcard { margin-bottom: 14px; }
.lvgrid { display: grid; grid-template-columns: 1fr 260px; gap: 22px; }
@media (max-width: 820px) { .lvgrid { grid-template-columns: 1fr; } }

.lvstanding {
  padding: 13px 15px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.lvstat {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 5px 0; font-size: 12.5px;
  border-bottom: 1px solid var(--border);
}
.lvstat:last-of-type { border-bottom: 0; }
.lvstat span { color: var(--muted); }
.lvstat b.over { color: var(--crit); }

.lvact { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.lvdecide { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.lvdecide input[type=text] { flex: 1; min-width: 220px; }

.lvdecided {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 12px;
  font-size: 12.5px;
  border-top: 1px solid var(--border);
}

/* Membatalkan persetujuan: kotak alasan + tombolnya satu baris, dan sengaja
   tidak merah. Ini bukan tindakan berbahaya yang harus dihindari, cuma
   tindakan yang jarang — yang menahan salah tekan konfirmasinya, bukan
   warnanya. */
.lvrevoke { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.lvrevoke-why { width: 190px; padding: 5px 9px; font-size: 12px; }
@media (max-width: 760px) { .lvrevoke-why { width: 100%; } }

/* ---------- menyunting cuti yang sudah disetujui (2 Okt 2026) ----------
   Jejaknya baris biasa, BUKAN peringatan: pengajuan yang pernah diubah
   bukan pengajuan yang bermasalah, dan mewarnainya kuning akan membuat
   setiap koreksi tanggal terlihat seperti kesalahan yang perlu diperiksa. */
.lvedited {
  margin-top: 10px; font-size: 12.5px;
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
}

.lvedit { margin-top: 12px; }
.lvedit > summary {
  cursor: pointer; font-size: 12.5px; font-weight: 620;
  color: var(--muted); padding: 5px 0; width: fit-content;
}
.lvedit > summary:hover { color: var(--ink); }
/* TIDAK ADA `.lvedit:not([open]) > *:not(summary) { display: none }` di sini,
   dan itu disengaja. Aturan itu memang pernah wajib di dua panel lain (revisi
   14, 25, 26) karena aturan bawaan peramban untuk <details> tertutup kalah
   dari aturan author mana pun — tapi di sini tidak ada aturan author yang
   menyentuh `display` anaknya, jadi bawaan peramban menang dan salinannya
   tidak menjaga apa pun. Terbukti: mencabutnya tidak membuat satu tes pun
   merah. Yang menjaga perilakunya adalah /tmp/cutisunting.js, yang mengukur
   tinggi formulir waktu panelnya tertutup — dan itu akan merah apa pun
   sebabnya, termasuk kalau suatu hari ada yang memberi .lvedit-form display
   sendiri. Kalau itu terjadi, aturan di atas yang jadi obatnya. */
.lvedit-form {
  margin-top: 10px; padding: 14px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface-sunk);
}
.lvedit-teks { margin-bottom: 12px; }
.lvedit-why { max-width: 460px; }
.lvedit-nb { font-size: 12px; margin: 10px 0 12px; }
.lvedit-belumsql, .lvedit-sendiri { margin-top: 10px; font-size: 12.5px; }

.lvdel {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--muted); padding-bottom: 10px;
  white-space: nowrap;
}
.lvdel input { margin: 0; }
/* Petak `1fr` TIDAK mengecil di bawah lebar min-content isinya, dan
   min-content sebuah <input type=date> di Chromium 158px. Di panel ini
   barisnya sudah tersarang tiga lapis (kartu 16px + formulir 10px + tepi),
   jadi dua kolom 158px meluber 8px keluar layar 360px — dan melubernya
   lewat, bukan lewat halaman yang jelas kepanjangan. `min-width: 0`
   yang mengizinkan petaknya menyusut. */
.lvedit-form .lvrow .field,
.lvedit-form .lvrow input,
.lvedit-form .lvrow select { min-width: 0; }
.lvedit-form .lvrow input,
.lvedit-form .lvrow select { width: 100%; }

@media (max-width: 780px) {
  /* Di dua kolom, centang "hapus" tidak boleh terdampar sendirian di kolom
     kanan jauh dari baris yang dihapusnya. */
  .lvdel { grid-column: 1 / -1; padding-bottom: 0; }
  .lvedit-form { padding: 12px 10px; }
}

/* ============================================================
   Leave calendar + document expiry
   ============================================================ */

.calfilter { margin: 0; }
.spanpick { display: flex; gap: 6px; flex-wrap: nowrap; }
.calfilter select { min-width: 170px; padding: 6px 10px; font-size: 12.5px; }

.callegend { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12px; color: var(--muted); }
.callegend span { display: inline-flex; align-items: center; gap: 6px; }
.lg { width: 12px; height: 12px; border-radius: 3px; display: inline-block; border: 1px solid var(--border); }
.lg.app { background: color-mix(in srgb, var(--good) 46%, transparent); border-color: color-mix(in srgb, var(--good) 72%, transparent); }
.lg.pen { border-color: color-mix(in srgb, var(--warn) 70%, transparent);
          background-image: repeating-linear-gradient(45deg,
            transparent 0 3px, color-mix(in srgb, var(--warn) 55%, transparent) 3px 6px); }
.lg.wl  { background: color-mix(in srgb, var(--s1) 22%, transparent); border-color: color-mix(in srgb, var(--s1) 48%, transparent); }

/* ============================================================
   Papan cuti — nama jadi baris, tanggal jadi kolom
   ------------------------------------------------------------
   Menggantikan enam kotak kalender bulanan. Kotak bulanan cuma bisa bilang
   "2 org" per hari; yang ditanyakan orang di halaman ini justru "si A cutinya
   kapan sampai kapan, dan siapa lagi yang bareng dia". Bentuk ini mengikuti
   sheet tim yang sudah mereka hafal.
   ============================================================ */

/* Penggulungnya milik kartu ini sendiri, bukan halaman. Tanpa ini, 90 kolom
   membuat SELURUH halaman bisa digeser ke kanan — sidebar ikut bergerak dan
   semua halaman lain terasa rusak. */
.lbwrap { overflow-x: auto; overflow-y: visible; }

.lb {
  border-collapse: separate; border-spacing: 0; font-size: 11px; width: auto;
  /* Lebarnya dari <colgroup>, bukan dari isi selnya. Tanpa ini, tiap baris
     ikut menawar lebar kolom, dan cukup satu sel yang berperilaku beda untuk
     membuat kepala tabel dan badannya berakhir di kolom yang berbeda. */
  table-layout: fixed;
}
.lb-colname { width: 186px; }
.lb-colday  { width: var(--dw, 19px); }
.lb th, .lb td { padding: 0; border: 0; }

/* ---- kolom nama, dibekukan di kiri ---- */
/* Sama seperti freeze di sheet: waktu digeser ke kanan, namanya tetap terbaca.
   Latarnya WAJIB opak — kalau tembus pandang, batang cuti lewat di belakang
   namanya dan dua-duanya jadi tidak terbaca. */
.lb .lb-name {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface-1);
  text-align: left; font-weight: 600; font-size: 11.5px;
  padding: 0 10px 0 2px;
  min-width: 186px; max-width: 186px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  border-right: 1px solid var(--border);
}
.lb thead .lb-name, .lb tfoot .lb-name { z-index: 3; background: var(--surface-sunk); }
.lb-quiet .lb-name { color: var(--muted); font-weight: 500; }

/* ---- kepala tabel ---- */
.lb thead th { position: sticky; top: 0; z-index: 1; background: var(--surface-sunk); }
.lb-m {
  font-size: 11px; font-weight: 750; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-2);
  padding: 5px 6px !important; text-align: left;
  border-left: 1px solid var(--border);
}
.lb-d {
  width: var(--dw, 19px); min-width: var(--dw, 19px);
  font-size: 9.5px; font-weight: 600; color: var(--muted);
  text-align: center; padding: 3px 0 4px !important;
}
.lb-d.now { color: var(--s1); font-weight: 800; }

/* ---- sel harian ---- */
.lb tbody tr { height: 21px; }

/* Selang-seling baris. Dengan 40 nama dan papan selebar 3-4 layar, menyusuri
   satu baris lurus ke kanan itu justru bagian yang paling melelahkan — bukan
   membaca batangnya. Ini yang paling banyak menolong, lebih dari warna apa pun. */
.lb tbody tr:nth-child(even) > td { background: color-mix(in srgb, var(--ink) 3.5%, transparent); }
.lb tbody tr:nth-child(even) > .lb-name { background: color-mix(in srgb, var(--ink) 3.5%, var(--surface-1)); }

.lb tbody tr:hover > td { background: color-mix(in srgb, var(--s1) 10%, transparent); }
.lb tbody tr:hover > .lb-name { background: color-mix(in srgb, var(--s1) 10%, var(--surface-1)); }
.lb-c { width: var(--dw, 19px); min-width: var(--dw, 19px); position: relative; }
/* Akhir pekan cuma diberi nada waktu kolomnya masih lebar. Di tampilan
   setahun, garis tiap tujuh kolom berubah jadi kerlip, bukan penanda. */
.lb tbody tr > td.we { background: color-mix(in srgb, var(--ink) 7%, transparent); }
.lb-d.we { background: color-mix(in srgb, var(--ink) 7%, transparent); }

/* Pemisah bulan tegas — inilah jangkar utama waktu angka tanggalnya tidak muat. */
.lb-c.mstart, .lb-d.mstart, .lb-n.mstart, .lb-m {
  border-left: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
}
.lb-c.now, .lb-n.now, .lb-d.now { box-shadow: inset 1px 0 0 var(--s1), inset -1px 0 0 var(--s1); }

/* Batangnya digambar oleh <i>, bukan oleh sel itu sendiri: sel-selnya bersentuhan
   tanpa sela, jadi cuti 15 hari terbaca sebagai SATU batang. Ujung-ujungnya saja
   yang dibulatkan — itu yang membedakan "satu cuti panjang" dari "15 cuti sehari". */
.lb-c.on > i {
  position: absolute; inset: 3px 0;
  display: block;
  background: var(--bar);
  border-top: 1px solid var(--barline);
  border-bottom: 1px solid var(--barline);
}
/* ---- tampilan setahun ---- */
.lb.narrow { font-size: 10px; }
.lb.narrow .lb-d { font-size: 8.5px; overflow: visible; }
.lb.narrow tbody tr { height: 19px; }
/* Kolom 9px: batangnya dibuat lebih tebal supaya cuti 3 hari tetap terlihat
   sebagai batang, bukan sebagai debu. */
.lb.narrow .lb-c.on > i { inset: 2px 0; }
.lb.narrow .lb-c.wl > i { inset: 6px 0; }

.lb-c.on.st > i { left: 1px; border-left: 1px solid var(--barline);
                  border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
.lb-c.on.en > i { right: 1px; border-right: 1px solid var(--barline);
                  border-top-right-radius: 4px; border-bottom-right-radius: 4px; }

.lb-c.app { --bar: color-mix(in srgb, var(--good) 46%, transparent);
            --barline: color-mix(in srgb, var(--good) 72%, transparent); }
.lb-c.pen { --bar: color-mix(in srgb, var(--warn) 40%, transparent);
            --barline: color-mix(in srgb, var(--warn) 70%, transparent); }
/* Cuti kerja BUKAN hijau, walaupun --s3 dan --good kebetulan warna yang sama
   persis (#10b981). Kalau dibiarkan, "tetap masuk" dan "pergi" tergambar
   identik — dua arti berlawanan dengan satu warna. Jadi biru, dan batangnya
   sengaja lebih tipis: ditandai, tapi orangnya ada. */
.lb-c.wl  { --bar: color-mix(in srgb, var(--s1) 22%, transparent);
            --barline: color-mix(in srgb, var(--s1) 48%, transparent); }
.lb-c.wl > i { inset: 7px 0; }

/* Yang menunggu diberi garis miring, bukan cuma warna lain. Warna saja tidak
   cukup untuk orang yang sulit membedakan hijau dan kuning — dan ini justru
   perbedaan yang paling penting di halaman ini: sudah pasti vs belum. */
.lb-c.pen > i {
  background-image: repeating-linear-gradient(45deg,
    transparent 0 3px, color-mix(in srgb, var(--warn) 55%, transparent) 3px 6px);
}

/* ---- baris "yang pergi" ---- */
.lb tfoot td { background: var(--surface-sunk); }
.lb-n {
  width: var(--dw, 19px); min-width: var(--dw, 19px); height: 22px;
  text-align: center; font-size: 9.5px; font-weight: 700;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* Terlalu banyak yang pergi bersamaan — satu-satunya hal yang tidak boleh
   terlewat oleh leader di halaman ini. */
.lb-n.crowd {
  background: color-mix(in srgb, var(--crit) 20%, transparent);
  color: var(--crit);
}

/* ---- jumlah hari, di dalam kolom nama ---- */
/* Dengan ratusan kolom, apa pun yang ditaruh di ujung kanan berada di luar
   layar sampai digulung. Angka ini justru yang paling sering dicari, jadi dia
   ikut dibekukan bersama namanya.

   Flex-nya di <div> DI DALAM sel, bukan di selnya sendiri. `display:flex`
   langsung pada <th>/<td> mengeluarkan sel itu dari tata letak tabel: kolomnya
   berhenti sejajar dengan kepala tabel, dan di layar abby tanggal 1-15
   tergambar persis di belakang nama-namanya. */
.lb-nw { display: flex; align-items: center; gap: 6px; width: 100%; }
.lb-nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-dc {
  margin-left: auto; flex: 0 0 auto;
  font-size: 9.5px; font-weight: 800; line-height: 1;
  padding: 2px 5px; border-radius: 99px;
  color: var(--good);
  background: color-mix(in srgb, var(--good) 14%, transparent);
  font-variant-numeric: tabular-nums;
}

.lg.crowd { background: color-mix(in srgb, var(--crit) 20%, transparent);
            border-color: color-mix(in srgb, var(--crit) 55%, transparent); }

/* ---- expiry list ---- */
.expcard { margin-bottom: 16px; }
.explist { display: flex; flex-direction: column; }
.exprow {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 0; font-size: 12.5px;
  border-bottom: 1px solid var(--border);
  /* Boleh membungkus sejak tombol "tandai" ikut masuk baris (13 Sep 2026):
     yang pindah ke baris berikutnya seluruh .exp-kanan sekaligus, bukan nama
     orangnya yang dipecah jadi bertumpuk. */
  flex-wrap: wrap;
}
.exprow:last-child { border-bottom: 0; }
/* NOT `.soon` — that name already belongs to the "SOON" pill in the sidebar,
   and it brings a background and a width with it. */
.exprow.exp-soon { color: var(--ink); }
.exprow.exp-gone b { color: var(--crit); }
.expbad { color: var(--crit); font-weight: 620; }

/* ============================================================
   Master data — staff & websites
   ============================================================ */

.passcard { border-color: color-mix(in srgb, var(--good) 40%, transparent); }
.passbox { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.passbox code {
  flex: 0 1 auto;
  padding: 11px 16px;
  font: 16px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: .08em;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  user-select: all;
}

tr.urow-off td, tr.wrow-off td { opacity: .55; }
tr.urow-off b, tr.wrow-off input { font-style: italic; }

table.wsite input, table.wsite select { width: 100%; padding: 6px 9px; font-size: 12.5px; }
table.wsite td { padding: 5px 6px; vertical-align: middle; }
/* The number spinners eat the digits in a narrow column, and nobody drags
   them anyway — the value gets typed. */
table.wsite input.wnum { text-align: center; -moz-appearance: textfield; appearance: textfield; }
table.wsite input.wnum::-webkit-outer-spin-button,
table.wsite input.wnum::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
table.wsite input.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

/* ============================================================
   Control Tower
   ============================================================ */

.twstages { display: grid; grid-template-columns: repeat(6, 1fr); gap: 12px; margin-bottom: 18px; }
@media (max-width: 1150px) { .twstages { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 640px)  { .twstages { grid-template-columns: repeat(2, 1fr); } }

.twstage {
  display: block;
  padding: 13px 14px 14px;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--sh-1);
  color: inherit;
  transition: border-color .12s, box-shadow .12s;
}
.twstage:hover { border-color: color-mix(in srgb, var(--s1) 45%, transparent); box-shadow: var(--sh-2); }
/* A finished stage steps back so the eye lands on what is left. */
.twstage.done { opacity: .62; }
.twstage.done:hover { opacity: 1; }

.tw-top { display: flex; align-items: center; gap: 6px; }
.tw-label {
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.tw-tick { margin-left: auto; color: var(--good); font-weight: 800; }
.tw-num { font-size: 24px; font-weight: 680; line-height: 1.1; margin: 6px 0 8px; }
.tw-num span { font-size: 14px; font-weight: 400; color: var(--muted); }
.tw-hint { margin-top: 7px; font-size: 11.5px; color: var(--muted); }
.twstage.done .tw-hint { color: var(--good); }

.twdone { margin-bottom: 18px; border-color: color-mix(in srgb, var(--good) 40%, transparent); }

.twgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
@media (max-width: 980px) { .twgrid { grid-template-columns: 1fr; } }

.twlist { display: flex; flex-direction: column; }
.twrow {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 8px 0; font-size: 12.5px;
  border-bottom: 1px solid var(--border);
}
.twrow:last-child { border-bottom: 0; }
.twrow.twlong { background: color-mix(in srgb, var(--crit) 5%, transparent); }

.twcode { font-weight: 660; min-width: 92px; }
.twname { font-weight: 600; }
.twmiss { display: flex; gap: 5px; flex-wrap: wrap; flex: 1; }
.twchip {
  padding: 2px 8px;
  font-size: 11px;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 20px;
  white-space: nowrap;
}
.twwho { font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* Kolom aksi dirapatkan ke kanan. Waktu isinya rata kiri, tombol ⋯ berdiri di
   tempat yang berbeda-beda tiap baris — tergantung ada tidaknya tombol utama di
   sebelahnya. Dirapatkan ke kanan, ⋯ jadi satu garis lurus dari atas ke bawah,
   dan itu yang membuat kolomnya bisa dipindai. */
.cs-table th:last-child, td.cs-act { text-align: right; }

/* handover buttons sit side by side without stretching the column */
td.cs-act { white-space: nowrap; }
td.cs-act .btn + .btn { margin-left: 5px; }
td.cs-act > .rowmore { margin-left: 5px; }
td.cs-act > .rowmore:first-child { margin-left: 0; }

/* ---- menu ⋯ untuk tombol milik leader/admin ----------------------------
   Dipakai <details> dan bukan JS: menu yang butuh JS akan hilang diam-diam
   kalau berkasnya gagal dimuat, dan tombol paksa-lepas milik atasan adalah
   hal terakhir yang boleh hilang diam-diam. Ini tetap jalan tanpa JS.

   Panelnya position:absolute supaya membukanya TIDAK mengubah tinggi baris —
   itu justru keluhan aslinya. */
.rowmore { display: inline-block; position: relative; vertical-align: middle; }
.rowmore > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 24px; border-radius: var(--r-sm);
  color: var(--muted); font-size: 15px; line-height: 1;
  border: 1px solid transparent;
}
.rowmore > summary::-webkit-details-marker { display: none; }
.rowmore > summary:hover { color: var(--ink); background: var(--surface-2); border-color: var(--border); }
.rowmore[open] > summary { color: var(--ink); background: var(--surface-2); border-color: var(--border); }
.rm-menu {
  position: absolute; z-index: 40; top: calc(100% + 4px); right: 0;
  min-width: 172px; padding: 5px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: 0 10px 26px rgba(0,0,0,.16);
  display: flex; flex-direction: column; gap: 4px;
}
.rm-menu .btn { width: 100%; justify-content: flex-start; text-align: left; margin: 0 !important; }

/* ============================================================
   Kesalahan Fatal — the 51st task
   ============================================================ */

.ftduty { margin-bottom: 16px; border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.ftduty.mine { border-color: color-mix(in srgb, var(--crit) 45%, transparent); }
.ftduty-in { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.ftduty-ic { font-size: 22px; }
.ftduty b { font-size: 14px; }
.ftduty .sub { margin-top: 2px; }
.ftcount { text-align: right; }
.ftcount .mono { display: block; font-size: 22px; font-weight: 680; line-height: 1.1; }
.ftcount em { font-style: normal; font-size: 11px; color: var(--muted); }

.ftrow { padding: 14px 0; border-bottom: 1px solid var(--border); }
.ftrow:last-child { border-bottom: 0; padding-bottom: 0; }
.ftrow-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ftstaff { font-weight: 600; }
.ftpct {
  padding: 2px 9px;
  font-size: 12.5px; font-weight: 700;
  color: var(--crit);
  background: color-mix(in srgb, var(--crit) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--crit) 35%, transparent);
  border-radius: 20px;
}
.ftsubject { margin-top: 6px; font-size: 13.5px; font-weight: 560; }
.ftdesc { margin-top: 4px; font-size: 12.5px; color: var(--ink-2); word-break: break-word; }
.ftmeta { margin-top: 6px; font-size: 11.5px; color: var(--muted); }

.ftact { display: flex; align-items: center; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.ftdecide { display: flex; gap: 7px; align-items: center; flex-wrap: wrap; flex: 1; }
.ftdecide input[type=text] { flex: 1; min-width: 200px; padding: 6px 10px; font-size: 12.5px; }

td.mono.ftover { color: var(--crit); font-weight: 700; }

/* ---------- fatal duty: two holders, not one ---------- */
.ftroles { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 9px; }
.ftrole {
  display: inline-flex; align-items: baseline; gap: 7px;
  padding: 4px 10px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface-sunk);
}
.ftrole em {
  font-style: normal; font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.ftrole b { font-size: 12.5px; }
.ftrole.on {
  border-color: color-mix(in srgb, var(--s1) 55%, transparent);
  background: color-mix(in srgb, var(--s1) 9%, var(--surface-1));
}
.ftrole.on em { color: var(--s1); }

/* ---------- roster: the 51st task ---------- */
.jd-fatal {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 14px; padding: 13px 15px;
  border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent);
  border-radius: var(--r); background: color-mix(in srgb, var(--warn) 7%, var(--surface-1));
}
.jd-fatal-ic { font-size: 20px; }
.jd-fatal b { font-size: 13.5px; }
.jd-fatal .sub { margin-top: 2px; max-width: 46ch; }
.jd-pair { display: flex; gap: 8px; flex-wrap: wrap; }
.jd-pair span {
  display: inline-flex; flex-direction: column; gap: 1px;
  padding: 5px 11px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface-1);
}
.jd-pair em {
  font-style: normal; font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.jd-pair b { font-size: 12.5px; }

/* the fatal row inside the violation board */
.row-fatal > td { background: color-mix(in srgb, var(--warn) 6%, transparent); }
.row-fatal > td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
.row-fatal > td:first-child b { white-space: nowrap; }

/* ==================================================================
   Roster mingguan (fase 6)
   ================================================================== */
.wk-wrap { overflow-x: auto; margin-top: 12px; }
.wk { border-collapse: separate; border-spacing: 0; width: 100%; min-width: 760px; }

.wk th, .wk td { border-bottom: 1px solid var(--border); vertical-align: middle; }
.wk thead th {
  padding: 9px 8px; text-align: center; font-weight: 600;
  border-bottom: 1px solid var(--border); background: var(--surface-sunk);
}
.wk thead th em {
  display: block; font-style: normal; font-size: 10px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.wk thead th b { display: block; font-size: 12.5px; }
.wk-tag {
  display: inline-block; margin-top: 3px; font-size: 9.5px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--s1);
}

/* nama auditor: dibekukan supaya tetap terbaca waktu digeser ke kanan */
.wk .wk-name {
  position: sticky; left: 0; z-index: 2;
  min-width: 168px; padding: 9px 14px 9px 0;
  background: color-mix(in srgb, var(--s1) 7%, var(--surface-1));
  box-shadow: 1px 0 0 var(--border);
}
.wk thead .wk-name { text-align: left; z-index: 3; }
.wk .wk-name b { font-size: 13px; }
.wk .wk-name .muted { font-size: 11px; margin-top: 1px; }
/* Penanda "di luar pembagian" untuk admin & leader — cukup terbaca, tidak
   sampai berteriak; barisnya tetap baris biasa, bukan peringatan. */
.wk-norot {
  display: inline-block; margin-top: 3px;
  font-size: 10px; font-weight: 640; letter-spacing: .02em;
  padding: 1px 6px; border-radius: 99px;
  color: var(--muted);
  background: var(--wash);
  border: 1px solid var(--border);
}

.wk-cell { padding: 0; text-align: center; width: 11.5%; }
.wk-today { background: color-mix(in srgb, var(--s1) 5%, transparent); }
.wk-audit { background: color-mix(in srgb, var(--s1) 10%, transparent); }
.wk-away  { background: color-mix(in srgb, var(--muted) 10%, transparent); }

.wk-btn {
  display: flex; align-items: center; justify-content: center; gap: 5px;
  width: 100%; min-height: 46px; padding: 7px 6px;
  border: 0; background: none; font: inherit; color: inherit;
  cursor: pointer; position: relative;
  transition: border-color .12s ease;
}
.wk-btn.as-text { cursor: default; }
.wk-btn:not(.as-text):hover { outline: 1px solid var(--s1); outline-offset: -3px; border-radius: var(--r-sm); }
.wk-btn:focus-visible { outline: 2px solid var(--s1); outline-offset: -3px; }

.wk-chip {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 650; letter-spacing: .03em;
  border: 1px solid transparent;
}
.wk-pagi  { background: color-mix(in srgb, var(--warn) 16%, var(--surface-1));
            border-color: color-mix(in srgb, var(--warn) 40%, transparent); color: var(--ink); }
.wk-malam { background: color-mix(in srgb, var(--s1) 14%, var(--surface-1));
            border-color: color-mix(in srgb, var(--s1) 42%, transparent); color: var(--ink); }
.wk-other { background: var(--surface-sunk); border-color: var(--border); }
.wk-off   { background: var(--surface-sunk); border-color: var(--border); color: var(--muted); }
.wk-none  { color: var(--muted); font-size: 13px; }
.wk-sub   { display: block; font-size: 9.5px; color: var(--muted); margin-top: 2px; }

.wk-load {
  display: inline-block; min-width: 20px; padding: 1px 5px; border-radius: var(--r-sm);
  background: var(--surface-sunk); border: 1px solid var(--border);
  font-size: 10.5px; color: var(--muted);
}
.wk-pin { position: absolute; top: 4px; right: 5px; font-size: 8px; color: var(--s1); }

.wk-legend {
  display: flex; gap: 16px; flex-wrap: wrap; align-items: center;
  margin-top: 13px; padding-top: 12px; border-top: 1px solid var(--border);
  font-size: 11.5px; color: var(--muted);
}
.wk-legend span { display: inline-flex; align-items: center; gap: 6px; }

/* ---------- modal ubah shift ---------- */
.modal {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: 20px; background: color-mix(in srgb, #000 46%, transparent);
}
.modal[hidden] { display: none !important; }
.modal-box {
  width: 100%; max-width: 420px; max-height: 88vh; overflow-y: auto;
  padding: 20px 22px; border-radius: var(--r-lg);
  background: var(--surface-1); border: 1px solid var(--border); box-shadow: var(--sh-2);
}
.modal-box h3 { font-size: 15px; }
.modal-box .sub { margin-top: 2px; }
.modal-act { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }

.wk-opts { display: flex; flex-direction: column; gap: 6px; margin: 14px 0 14px; }
.wk-opt {
  display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: var(--r-sm);
  transition: border-color .12s ease;
}
.wk-opt:hover { border-color: color-mix(in srgb, var(--s1) 50%, var(--border)); }
.wk-opt input { margin-top: 2px; }
.wk-opt b { display: block; font-size: 12.5px; }
.wk-opt em { display: block; font-style: normal; font-size: 11px; color: var(--muted); margin-top: 1px; }
.wk-opt:has(input:checked) {
  border-color: var(--s1); background: color-mix(in srgb, var(--s1) 7%, var(--surface-1));
}

/* ---------- kelola shift ---------- */
.sh-row { padding: 15px 0; border-bottom: 1px solid var(--border); }
.sh-row:last-of-type { border-bottom: 0; }
.sh-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.sh-head b { font-size: 13.5px; }
.sh-grid { display: grid; grid-template-columns: 1.4fr .8fr .8fr 1.2fr auto auto; gap: 10px; align-items: end; }
@media (max-width: 900px) { .sh-grid { grid-template-columns: 1fr 1fr; } }
.sh-chk { display: flex; align-items: center; gap: 7px; padding-bottom: 9px; font-size: 12.5px; }
/* Legenda memakai penanda yang sama dengan sel, tapi di sana tidak ada
   ancestor ber-position — tanpa ini titiknya kabur ke pojok halaman. */
.wk-legend .wk-pin { position: static; }
.wk-legend .wk-load { min-width: 0; }
/* Catatan "cuti kerja" turun ke baris sendiri, tidak berdesakan dengan chip. */
.wk-btn { flex-wrap: wrap; }
.wk-sub { flex-basis: 100%; }
.wk-nav { display: flex; gap: 8px; flex-wrap: nowrap; }

/* ==================================================================
   Library (fase 9b)
   ================================================================== */
.lib-bar { padding-bottom: 0; }
.lib-search { display: flex; gap: 8px; align-items: center; margin: 0 0 14px; }
.lib-search input[type="search"] { flex: 1; }

.lib-tabs { display: flex; gap: 4px; flex-wrap: wrap; margin: 0 -18px; padding: 0 18px;
            border-top: 1px solid var(--border); }
.lib-tab {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 11px 13px; margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  font-size: 12.5px; color: var(--muted); text-decoration: none;
  transition: border-color .12s ease, color .12s ease;
}
.lib-tab:hover { color: var(--ink); }
.lib-tab.on { color: var(--ink); border-bottom-color: var(--s1); font-weight: 620; }
.lib-tab span {
  padding: 1px 7px; border-radius: 999px; font-size: 10.5px;
  background: var(--surface-sunk); border: 1px solid var(--border);
}

.lib-item { display: flex; gap: 13px; padding: 16px 0; border-bottom: 1px solid var(--border); }
.lib-item:last-of-type { border-bottom: 0; padding-bottom: 4px; }
.lib-item.pinned { background: color-mix(in srgb, var(--warn) 5%, transparent);
                   margin: 0 -18px; padding-left: 18px; padding-right: 18px; }
.lib-ic { font-size: 19px; line-height: 1.3; flex: 0 0 auto; }
.lib-main { flex: 1; min-width: 0; }

.lib-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lib-title b { font-size: 13.5px; }
.lib-title a { text-decoration: none; }
.lib-title a:hover b { text-decoration: underline; }

.lib-url { font-size: 11.5px; color: var(--muted); margin-top: 3px; word-break: break-all; }
.lib-ext {
  display: inline-block; padding: 0 5px; margin-right: 5px; border-radius: 3px;
  background: var(--surface-sunk); border: 1px solid var(--border);
  font-size: 9.5px; font-weight: 700; letter-spacing: .04em; color: var(--ink-2);
}

.lib-body {
  font-size: 12.5px; color: var(--ink-2); margin-top: 7px; line-height: 1.55;
  /* pre-wrap untuk SEMUA isi, bukan cuma catatan (14 Sep 2026).
     Keterangan yang ditempel di bawah file ternyata juga ditulis berbaris —
     panduan lengkap dengan judul, garis pemisah dan langkah 1..5 — dan tanpa
     pre-wrap seluruh enternya runtuh jadi satu paragraf gumpal. Tulisannya
     utuh di database; yang hilang cuma bentuknya, dan bentuk itulah yang
     membuatnya bisa dibaca.

     Tidak ada ruginya untuk yang memang satu paragraf: pre-wrap cuma
     mempertahankan apa yang diketik orang, dan paragraf tanpa enter tergambar
     persis sama. */
  white-space: pre-wrap;
}
.lib-body.lib-note {
  padding: 10px 13px; border-radius: var(--r-sm);
  background: var(--surface-sunk); border: 1px solid var(--border);
  max-height: 260px; overflow-y: auto;
}
.lib-meta { font-size: 11px; color: var(--muted); margin-top: 7px; }
.lib-act { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 9px; }

.lib-edit { margin-top: 12px; padding: 13px 15px; border-radius: var(--r-sm);
            background: var(--surface-sunk); border: 1px solid var(--border); }
.lib-edit[hidden] { display: none !important; }
.lib-edit-act { display: flex; gap: 8px; justify-content: flex-end; }

.lib-pages { display: flex; align-items: center; gap: 12px; justify-content: center;
             margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
             font-size: 12px; }

.lib-kinds { display: flex; gap: 6px; margin: 14px 0 16px; }
.lib-kind {
  flex: 1; padding: 10px 8px; border-radius: var(--r-sm); cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-1);
  font: inherit; font-size: 12.5px; color: var(--ink);
  transition: border-color .12s ease;
}
.lib-kind:hover { border-color: color-mix(in srgb, var(--s1) 50%, var(--border)); }
.lib-kind.on { border-color: var(--s1); background: color-mix(in srgb, var(--s1) 8%, var(--surface-1));
               font-weight: 620; }

/* ==================================================================
   Settings (fase 9b)
   ================================================================== */
.set-row {
  display: flex; gap: 24px; align-items: flex-start;
  padding: 15px 0; border-bottom: 1px solid var(--border);
}
.set-row:last-of-type { border-bottom: 0; padding-bottom: 2px; }
.set-row.danger { margin: 0 -18px; padding-left: 18px; padding-right: 18px;
                  background: color-mix(in srgb, var(--warn) 5%, transparent); }
.set-info { flex: 1; min-width: 0; }
.set-info label { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.set-info b { font-size: 13px; }
.set-info .sub { margin-top: 3px; max-width: 62ch; }
.set-key {
  display: inline-block; margin-top: 6px; padding: 1px 6px; border-radius: 3px;
  background: var(--surface-sunk); border: 1px solid var(--border);
  font-size: 10.5px; color: var(--muted);
}
.set-input { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding-top: 2px; }
.set-input input[type="number"] { width: 96px; text-align: right; }
.set-input input[type="time"] { width: 120px; }
.set-input input[type="text"] { width: 190px; }
.set-range { font-style: normal; font-size: 11px; color: var(--muted); white-space: nowrap; }
.set-toggle { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; cursor: pointer; }

/* "Sudah dicentang" dan "sudah disimpan" adalah dua hal yang berbeda, dan
   tanpa tanda ini keduanya terlihat sama persis — orang menutup halaman
   merasa selesai. Warnanya peringatan, bukan sukses: yang belum disimpan
   belum berlaku apa-apa. */
.set-toggle.belum-simpan span {
  color: var(--warn); font-weight: 650;
}
.set-toggle.belum-simpan span::after {
  content: ' • belum disimpan';
  font-weight: 500; opacity: .85;
}

.set-save {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin: 4px 0 18px; font-size: 12px;
}
.set-unknown { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.set-unknown code {
  padding: 3px 8px; border-radius: var(--r-sm); font-size: 11px; color: var(--muted);
  background: var(--surface-sunk); border: 1px solid var(--border);
}
.set-elsewhere { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.set-elsewhere a {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 13px; border-radius: var(--r-sm); text-decoration: none;
  border: 1px solid var(--border); color: var(--ink);
  transition: border-color .12s ease;
}
.set-elsewhere a:hover { border-color: color-mix(in srgb, var(--s1) 50%, var(--border)); }
.set-elsewhere b { font-size: 12.5px; }
.set-elsewhere em { font-style: normal; font-size: 11.5px; color: var(--s1); }
.set-elsewhere code {
  margin-left: auto; padding: 1px 6px; border-radius: 3px; font-size: 10.5px;
  color: var(--muted); background: var(--surface-sunk); border: 1px solid var(--border);
}

/* ---------- Library: pribadi vs publik ---------- */
.lib-tabs .spacer { flex: 1; }
.lib-tab.mine.on { border-bottom-color: var(--warn); }

/* Sticky note: hangat, sedikit miring dari yang lain, jelas beda dari isi tim. */
.lib-item.private {
  margin: 0 -18px; padding-left: 18px; padding-right: 18px;
  background: color-mix(in srgb, var(--warn) 6%, transparent);
  box-shadow: inset 3px 0 0 color-mix(in srgb, var(--warn) 55%, transparent);
}
.lib-item.private.pinned { background: color-mix(in srgb, var(--warn) 9%, transparent); }
.lib-item.private .lib-body.lib-note {
  background: color-mix(in srgb, var(--warn) 8%, var(--surface-1));
  border-color: color-mix(in srgb, var(--warn) 30%, var(--border));
}

.lib-vis { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 16px; }
.lib-visopt {
  display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: var(--r-sm);
  transition: border-color .12s ease;
}
.lib-visopt:hover { border-color: color-mix(in srgb, var(--s1) 50%, var(--border)); }
.lib-visopt input { margin-top: 3px; }
.lib-visopt b { display: block; font-size: 12.5px; }
.lib-visopt em { display: block; font-style: normal; font-size: 11px; color: var(--muted); margin-top: 1px; }
.lib-visopt:has(input:checked) {
  border-color: var(--s1); background: color-mix(in srgb, var(--s1) 7%, var(--surface-1));
}
.lib-visopt:has(#libVisPrivate:checked) {
  border-color: var(--warn); background: color-mix(in srgb, var(--warn) 8%, var(--surface-1));
}


/* ==================================================================
   Kerapatan & warna  ·  hasil dari "tetap padat & jelas, warnanya
   jangan datar" — kerapatan arah C, sidebar arah A, warna arah B.
   ================================================================== */

body { font-size: 13.5px; line-height: 1.5; }

/* ---------- sidebar lebih ramping, warna per wilayah ---------- */
.side  { width: 224px; flex: 0 0 224px; padding: 18px 9px 20px; }
.brand { padding: 2px 7px 20px; }
.navgrp {
  display: flex; align-items: center; gap: 7px;
  padding: 15px 9px 6px; font-size: 9px; color: var(--side-dim);
}
.navgrp::after {
  content: ''; flex: 1; height: 1px;
  background: linear-gradient(90deg, currentColor, transparent); opacity: .35;
}
/* Label kelompok dulu punya empat warna berbeda. Digabung dengan ikon emoji
   berwarna dan empat kartu ringkasan yang juga berwarna, satu layar berisi
   sembilan warna yang tidak satu pun menyampaikan keadaan — itu yang abby
   rasakan sebagai "nabrak".
   Warna modul TETAP ADA, tapi sekarang cuma di strip tab halaman (.pgtabs),
   tempat dia benar-benar menjawab "aku sedang di bagian mana". */
.navgrp[data-mod] { color: var(--side-dim); }

.nav { padding: 6px 9px; font-size: 13px; gap: 9px; border-radius: 8px; }
.nav .ic { width: 24px; height: 24px; border-radius: 7px; font-size: 12.5px; }

/* Menu aktif memakai warna wilayahnya sendiri, bukan biru untuk semuanya. */
.nav[data-mod="jobdesk"] { --m: var(--m-jobdesk); }
.nav[data-mod="work"]    { --m: var(--m-work); }
.nav[data-mod="tools"]   { --m: var(--m-tools); }
.nav[data-mod="master"]  { --m: var(--m-master); }
.nav.on {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--m, var(--side-accent)) 22%, transparent),
    transparent 92%);
  box-shadow: inset 3px 0 0 var(--m, var(--side-accent));
}
.nav.on .ic {
  background: var(--m, var(--side-accent));
  color: #06121f;
  box-shadow: 0 3px 10px color-mix(in srgb, var(--m, var(--side-accent)) 45%, transparent);
}

/* ---------- header & isi lebih rapat ---------- */
.top   { padding: 11px 22px; }
.top h1 { font-size: 18px; }
.wrap  { padding: 18px 22px 60px; }
.pgtabs { padding: 0 22px; }
.pgtab  { padding: 9px 13px 8px; font-size: 12.5px; }

.card { padding: 14px 16px; margin-bottom: 12px; border-radius: 12px; }
.card::before { border-radius: 12px 12px 0 0; }
.card h3 { font-size: 13.5px; font-weight: 750; }
.card-head { margin-bottom: 12px; gap: 12px; }

.tiles { gap: 12px; margin-bottom: 14px; }
.tile .val { font-size: 27px; }

/* ---------- tabel padat, kepala tabel bernada ---------- */
table { font-size: 13px; }
thead th {
  background: var(--surface-sunk);
  text-transform: uppercase; letter-spacing: .06em;
  font-size: 9.5px; font-weight: 800; color: var(--muted);
  padding-top: 8px; padding-bottom: 8px;
}
tbody tr { transition: background .1s; }
tbody tr:hover { background: color-mix(in srgb, var(--s1) 4%, transparent); }

/* ---------- status berwarna penuh, bukan tulisan berwarna ----------
   Warna teks di atas latar tipis terbaca sopan tapi hilang waktu disapu
   cepat. Blok warna penuh langsung kelihatan dari ujung meja.
   Mode gelap ditulis ulang juga: CSS dasar punya aturan khusus dark yang
   kalau tidak dilawan membuat teks sewarna latarnya. */
.badge {
  border-radius: 5px; font-weight: 800; font-size: 9.5px;
  letter-spacing: .04em; text-transform: uppercase; padding: 3px 8px;
}
.b-good, :root[data-theme="dark"] .b-good {
  background: var(--good); color: #04231a; border-color: transparent; }
.b-warn, :root[data-theme="dark"] .b-warn {
  background: var(--warn); color: #3a2600; border-color: transparent; }
.b-crit, :root[data-theme="dark"] .b-crit {
  background: var(--crit); color: #fff; border-color: transparent; }
.b-info, :root[data-theme="dark"] .b-info {
  background: var(--s1); color: #fff; border-color: transparent; }
.b-mut, :root[data-theme="dark"] .b-mut {
  background: var(--surface-sunk); color: var(--muted);
  border: 1px solid var(--border); }

/* ---------- kartu ringkasan ----------
   Dulu tiap kartu punya warnanya sendiri: pita cyan, hijau, oranye, ungu —
   diambil dari warna modul sidebar. Kelihatan hidup selama lima detik, lalu
   jadi masalah: warna yang sama dipakai untuk "kamu sedang di bagian mana" DAN
   untuk "ini kartu keberapa". Waktu satu lambang dipakai dua maksud, dia
   berhenti berarti apa-apa — dan layarnya jadi ramai tanpa memberi tahu
   apa pun.

   abby: "warna nya agak nabrak ya?"

   Aturannya sekarang satu kalimat:
       WARNA DI DASHBOARD HANYA UNTUK MAKNA, BUKAN UNTUK MEMBEDAKAN KOTAK.

   Jadi kartunya netral semua, dan yang berwarna cuma yang mengatakan sesuatu:
   batang kemajuan (biru), dan angka yang lewat ambang (merah). Di layar yang
   isinya netral, satu angka merah terlihat dari seberang ruangan. */
.tiles .card.tile .lbl { color: var(--muted); }
.tiles .card.tile .val { font-variant-numeric: tabular-nums; }
.tiles .card.tile .meter i { background: var(--s1); }

/* Kartu yang angkanya lewat ambang. Satu-satunya kartu yang boleh berwarna,
   karena dia satu-satunya yang mendesak. */
.tiles .card.tile.over {
  border-color: color-mix(in srgb, var(--crit) 34%, var(--border));
  background: color-mix(in srgb, var(--crit) 5%, var(--surface-1));
}
.tiles .card.tile.over::before {
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--crit) 55%, transparent) 22%,
    color-mix(in srgb, var(--crit) 55%, transparent) 78%, transparent);
}
.tiles .card.tile.over .lbl { color: color-mix(in srgb, var(--crit) 70%, var(--muted)); }

/* Tombol yang isinya cuma ikon: dibuat persegi supaya sejajar dengan lonceng
   di sebelahnya, bukan melebar mengikuti isi. */
.btn.icobtn { padding: 0; width: 34px; height: 30px; display: inline-grid; place-items: center; }
.btn.icobtn svg { width: 15px; height: 15px; display: block; }
.bell > summary svg { width: 16px; height: 16px; display: block; }

/* ---------- tombol dengan sedikit isi ---------- */
.btn { border-radius: 7px; font-weight: 650; }
.btn.pri {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--s1) 92%, #fff),
    var(--s1));
  border-color: color-mix(in srgb, var(--s1) 70%, #000);
  box-shadow: 0 2px 6px color-mix(in srgb, var(--s1) 32%, transparent);
}


/* ==================================================================
   Lebar layar
   ==================================================================
   Batas 1280px masuk akal untuk halaman yang isinya kalimat — baris
   teks yang terlalu panjang capek dibaca. Tapi halaman yang isinya
   SATU TABEL LEBAR jadi korban: tabelnya harus digeser ke samping
   padahal separuh layar menganggur. Ketahuan waktu abby zoom out ke
   67% dan halaman Websites tetap mengumpul di kiri.

   Jadi batasnya dinaikkan sedikit untuk semua, dan halaman yang
   memang berisi tabel lebar dilepas sama sekali. Dikenali dari
   pembungkus tabelnya sendiri, jadi tidak perlu menandai satu per
   satu halaman — halaman baru yang pakai pembungkus yang sama ikut
   otomatis.

   :has() tidak ada di browser lama; di situ batas 1560px tetap
   berlaku, dan tabelnya tetap bisa digeser seperti sebelumnya.
   ================================================================== */
.wrap { max-width: 1560px; }
.wrap:has(.gridwrap),
.wrap:has(.wk-wrap),
.wrap:has(table.wsite),
.wrap:has(table.wagrid) { max-width: none; }

/* ==================================================================
   Activity Log (fase 10)
   ================================================================== */
.lg-filter {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px; align-items: end;
}
.lg-filter .field { margin-bottom: 0; }
.lg-filter .lg-q { grid-column: span 2; }
.lg-act { display: flex; gap: 7px; padding-bottom: 1px; }
@media (max-width: 760px) { .lg-filter .lg-q { grid-column: auto; } }

.lg-day {
  display: flex; align-items: center; gap: 10px;
  margin: 16px 0 8px; font-size: 10px; font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.lg-day::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.lg-list > .lg-day:first-child { margin-top: 0; }

.lg-row { display: flex; gap: 11px; padding: 8px 0; align-items: flex-start; }
.lg-time { flex: 0 0 62px; font-size: 11px; color: var(--muted); padding-top: 2px; }
.lg-dot {
  flex: 0 0 7px; width: 7px; height: 7px; border-radius: 50%;
  margin-top: 6px; box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 12%, transparent);
}
.lg-body { flex: 1; min-width: 0; }
.lg-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.lg-head b { font-size: 12.5px; }
.lg-what { font-size: 12.5px; color: var(--ink-2); }
.lg-meta { font-size: 10.5px; color: var(--muted); margin-top: 2px; }
.lg-meta code { font-size: 9.5px; padding: 0 4px; }
.lg-cat { font-weight: 700; }

.lg-diff { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.lg-chg {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 2px 8px; border-radius: 5px; font-size: 11px;
  background: var(--surface-sunk); border: 1px solid var(--border);
  max-width: 100%; overflow: hidden;
}
.lg-chg em { font-style: normal; color: var(--muted); font-size: 10px; }
.lg-chg s  { color: var(--muted); text-decoration-thickness: 1px; }
.lg-chg i  { color: var(--muted); font-style: normal; }
.lg-chg b  { font-weight: 650; }

/* ==================================================================
   Profile (fase 10)
   ================================================================== */
.pf-head { display: flex; gap: 18px; align-items: flex-start; flex-wrap: wrap; }
.pf-photo {
  width: 84px; height: 84px; flex: 0 0 auto; border-radius: 18px; overflow: hidden;
  /* Sama dengan avatar di header — orang yang sama, jangan dua warna. */
  background: linear-gradient(140deg, var(--s1),
              color-mix(in srgb, var(--s1) 45%, #0a2246));
  display: grid; place-items: center;
  color: #fff; font-size: 27px; font-weight: 800; letter-spacing: .02em;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--s1) 30%, transparent);
}
.pf-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pf-id h2 { margin: 0; font-size: 20px; font-weight: 800; letter-spacing: -.025em; }
.pf-tags { display: flex; gap: 6px; flex-wrap: wrap; margin: 7px 0 6px; }
.pf-upload { flex: 0 0 auto; min-width: 250px; }
.pf-upload-act { display: flex; gap: 7px; margin-top: 8px; }

.pf-grid { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 14px; align-items: start; }
@media (max-width: 1000px) { .pf-grid { grid-template-columns: 1fr; } }

.pf-quota { display: flex; gap: 8px; margin-top: 10px; }
.pf-quota > div {
  flex: 1; text-align: center; padding: 9px 4px; border-radius: var(--r-sm);
  background: var(--surface-sunk); border: 1px solid var(--border);
}
.pf-quota b  { display: block; font-size: 19px; font-weight: 800; line-height: 1.1; }
.pf-quota em { font-style: normal; font-size: 10px; color: var(--muted); }

.pf-doc {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 12.5px;
}
.pf-doc:last-child { border-bottom: 0; padding-bottom: 0; }
.pf-doc-v em { display: block; font-style: normal; font-size: 10.5px; color: var(--muted); text-align: right; }
.pf-doc-v.pf-soon { color: var(--warn); }
.pf-doc-v.pf-soon em { color: var(--warn); }
.pf-doc-v.pf-gone { color: var(--crit); font-weight: 700; }
.pf-doc-v.pf-gone em { color: var(--crit); }

.pf-stat {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 12.5px;
}
.pf-stat:last-child { border-bottom: 0; padding-bottom: 0; }

.pf-log-row {
  display: flex; gap: 12px; padding: 6px 0; font-size: 12.5px;
  border-bottom: 1px solid var(--border);
}
.pf-log-row:last-child { border-bottom: 0; }
.pf-log-row .mono { flex: 0 0 88px; color: var(--muted); font-size: 11px; }

/* Avatar di header jadi tautan ke profil, dan bisa memuat foto. */
.who .av { overflow: hidden; text-decoration: none; }
.who .av img { width: 100%; height: 100%; object-fit: cover; display: block; }
.who-txt { text-decoration: none; color: inherit; }
.who-txt:hover .who-name { text-decoration: underline; }

/* ==================================================================
   Laporan matriks — situs x tanggal, dua angka per sel
   Meniru bentuk sheet yang tim sudah pakai sehari-hari.
   ================================================================== */
.mx { border-collapse: separate; border-spacing: 0; min-width: 100%; }
.mx th, .mx td { border-bottom: 1px solid var(--border); }

.mx thead th { padding: 6px 7px; text-align: center; vertical-align: bottom; }
.mx .mx-d { min-width: 56px; font-size: 12px; font-weight: 800; color: var(--ink-2); }
.mx .mx-d em {
  display: block; font-style: normal; font-size: 9px; font-weight: 700;
  letter-spacing: .08em; color: var(--muted); text-transform: uppercase;
}

/* Kolom nama situs dibekukan — tanpa ini, menggeser ke tanggal jauh
   membuat baris kehilangan identitasnya. */
.mx .mx-site {
  position: sticky; left: 0; z-index: 2;
  min-width: 158px; text-align: left; padding: 6px 12px 6px 14px;
  background: color-mix(in srgb, var(--s1) 7%, var(--surface-1));
  box-shadow: 1px 0 0 var(--border);
}
.mx thead .mx-site { z-index: 3; }
.mx .mx-site b  { display: block; font-size: 12.5px; }
.mx .mx-site em { display: block; font-style: normal; font-size: 10px; color: var(--muted); }

.mx .mx-avg {
  position: sticky; left: 158px; z-index: 2;
  min-width: 74px; padding: 6px 8px;
  background: color-mix(in srgb, var(--s3) 9%, var(--surface-1));
  box-shadow: 1px 0 0 var(--border);
}
.mx thead .mx-avg { z-index: 3; }

.mx-c { padding: 5px 7px; text-align: center; }
.mx-c.hot { background: color-mix(in srgb, var(--crit) 12%, transparent); }
.mx-c.none { background: color-mix(in srgb, var(--muted) 7%, transparent); }

/* Dua angka bertumpuk: total di atas, mischat di bawah. */
.mx-t, .mx-m {
  display: block; text-align: center;
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums; letter-spacing: -.03em;
}
.mx-t { font-size: 12px; font-weight: 650; color: var(--ink); }
.mx-m { font-size: 10.5px; color: var(--muted); margin-top: 1px; }
.mx-c.hot .mx-m { color: var(--crit); font-weight: 700; }
.mx .mx-avg .mx-t { font-weight: 800; }

/* ── Pembagian Harian: tiga kolom yang bisa diseret ──────────────────────
   Seretan cuma tambahan; <select> di tiap nama yang jadi jalur sebenarnya. */
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 16px; }
.dutylist { margin: 8px 0 0; }
.dutyitem {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; border-radius: 8px; border: 1px solid transparent;
}
.dutyitem:hover { background: color-mix(in srgb, var(--s3) 8%, transparent); }
.dutyname { flex: 1; font-size: 12.5px; }
.dutyform { margin: 0; display: flex; gap: 6px; }
.dutyform select { font-size: 11px; padding: 2px 4px; }
.dutyempty { font-size: 12px; }

/* Cuma waktu seretannya benar-benar hidup. Kalau JS-nya tidak sampai,
   kursornya tidak berubah jadi "bisa diseret" — isyarat yang berbohong lebih
   buruk daripada tidak ada isyarat. */
.dragon .dutyitem { cursor: grab; }
.dragon .dutyitem.dragging { opacity: .45; cursor: grabbing; }
.dutycol.dragover { outline: 2px dashed var(--s1); outline-offset: -4px; }
.dutyitem.saving { opacity: .5; }

/* Papan shift TETAP dibedakan dari papan malam-ini. Dua papan yang tergambar
   sama persis tapi berakibat sangat berbeda adalah undangan untuk salah seret:
   yang atas mengubah satu malam, yang ini setiap malam. */
.stdwrap .dutycol { border-style: dashed; }
.undobar { display: flex; align-items: center; margin: 0 0 12px; }

@media (max-width: 900px) {
  .grid3 { grid-template-columns: 1fr; }
}

/* ── Papan cuti: yang membuatnya bisa dibaca ────────────────────────────
   abby: "terlalu lebar", "susah tahu baris ini tanggal berapa", "tidak jelas
   siapa yang cuti hari ini". Tiga keluhan, tiga jawaban. */

/* 1. Siapa yang tidak masuk HARI INI — di atas papannya, bukan di dalamnya. */
.todaycard .offlist { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 4px; }
.offone {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 5px 10px; border-radius: 999px; font-size: 12.5px;
  background: color-mix(in srgb, var(--s3) 12%, var(--surface-1));
  border: 1px solid var(--border);
}
.offone em { font-style: normal; font-size: 10.5px; color: var(--muted); }
.offone.pen { border-style: dashed; }

/* 2. Garis bantu tiap Senin, menembus dari kepala sampai kaki tabel.
      Tanpa patokan, batang hijau di tengah papan cuma bisa ditelusuri dengan
      menghitung kolom satu per satu. Minggu dipilih karena itu satuan yang
      memang dipakai orang waktu bicara jadwal — bukan garis tiap 10 kolom yang
      tidak berarti apa-apa. */
/* NAMANYA `lbwk`, BUKAN `wk`. Percobaan pertama memakai `.wk` — dan itu nama
   yang SUDAH DIPAKAI tabel roster mingguan, lengkap dengan
   `min-width: 760px`. Akibatnya tiap kolom Senin di papan cuti melar jadi
   760px dan seluruh papannya berantakan: enam tanggal rapat, lalu satu kolom
   raksasa, lalu enam lagi.

   Yang membuatnya berbahaya: tidak ada error, semua tes tetap hijau, dan
   bentuknya cuma terlihat kalau papannya benar-benar digambar. Nama kelas yang
   pendek dan umum adalah tabrakan yang menunggu terjadi di stylesheet
   sepanjang ini. */
.lb .lbwk { box-shadow: inset 1px 0 0 var(--border); }

/* Slot yang ditinggal pulang — dilipat, tapi tidak dihilangkan.
   Yang berubah cuma urutan perhatian: yang sedang dipakai di depan, yang
   ditinggal dihitung dan bisa dibuka. Menghapusnya dari layar berarti
   menghapus satu-satunya tempat yang menunjukkannya. */
.twoff { margin-top: 12px; border-top: 1px dashed var(--border); padding-top: 10px; }
.twoff summary { cursor: pointer; font-size: 12.5px; color: var(--muted); }
.twoff summary b { color: var(--ink); }
.twoff summary .badge { margin-left: 7px; }
.twoff .twlist { margin-top: 9px; }
.twoff .sub { margin-top: 8px; }

/* Panel bergulir dua arah: tanggal tetap di atas, TOTAL tetap di bawah,
   nama situs tetap di kiri.

   abby: "krna situsnya bnyak jadi ketika di scroll down jadi ga nampak
   tanggal nya".

   KENAPA TINGGINYA DIPATOK. `position: sticky; top:0` tidak bisa bekerja
   terhadap gulungan HALAMAN di sini, karena .gridwrap sudah jadi wadah
   bergulir sendiri (overflow-x: auto membuat sumbu satunya ikut auto).
   Jadi tabelnya diberi tinggi maksimum dan digulir DI DALAM wadahnya —
   dengan begitu kepala dan kakinya benar-benar punya tempat untuk menempel. */
.mxwrap { max-height: 70vh; overflow: auto; }

.mx thead th { position: sticky; top: 0; z-index: 4; background: var(--surface-1); }
.mx tfoot td { position: sticky; bottom: 0; z-index: 4; background: var(--surface-1);
               border-top: 2px solid var(--border); }

/* Sudut kiri-atas dan kiri-bawah menempel di DUA arah sekaligus, jadi
   lapisannya harus di atas keduanya — kalau tidak, nama situs tertimpa
   angka waktu digulir menyerong. */
.mx thead .mx-site, .mx thead .mx-avg,
.mx tfoot .mx-site, .mx tfoot .mx-avg { z-index: 6; }

.mx tfoot .mx-site b { font-size: 12px; letter-spacing: .04em; }
.mx tfoot .mx-t { font-weight: 800; }

.mx-legend {
  display: flex; gap: 18px; flex-wrap: wrap; align-items: center;
  margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--border);
  font-size: 11.5px; color: var(--muted);
}
.mx-legend span { display: inline-flex; align-items: center; gap: 6px; }
.mx-legend .mx-t, .mx-legend .mx-m { display: inline; }
.mx-chip {
  width: 15px; height: 15px; border-radius: 4px; border: 1px solid var(--border);
}
.mx-chip.hot  { background: color-mix(in srgb, var(--crit) 25%, transparent); }
.mx-chip.none { background: color-mix(in srgb, var(--muted) 14%, transparent); }

/* ==================================================================
   Activity Log bentuk tabel — mengikuti panel yang tim sudah pakai
   ================================================================== */
.lgt { font-size: 12.5px; }
/* Padding samping dipersempit dari 12px bawaan tabel: sembilan kolom berebut
   lebar, dan 24px per kolom yang hilang untuk ruang kosong itulah yang membuat
   tanggal patah jadi dua baris. */
.lgt td, .lgt th { padding-left: 8px; padding-right: 8px; }
.lgt td { vertical-align: top; padding-top: 8px; padding-bottom: 8px; }
.lgt td:first-child, .lgt th:first-child { padding-left: 0; }
.lgt td:last-child,  .lgt th:last-child  { padding-right: 0; }

/* Kolom yang isinya satu satuan utuh tidak boleh dipatah di tengah. Tanggal
   yang terbaca "2026-08-" lalu "22" di baris berikutnya bukan cuma jelek —
   sekilas terbaca seperti dua nilai. */
.lgt .lgt-nw { white-space: nowrap; }

.lgt-role {
  display: block; font-style: normal; font-size: 10px; color: var(--muted); margin-top: 1px;
}
.lgt-what { font-weight: 600; }
/* Nama aksi mentah menempel di baris judulnya, bukan di baris sendiri —
   satu baris kali 50 baris itu setengah layar. */
code.lgt-act {
  font-size: 9.5px; padding: 1px 5px; margin-left: 6px;
  color: var(--muted); background: var(--wash); border-radius: 4px;
  white-space: nowrap;
}
.lgt-cat {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 700; font-size: 11px; line-height: 1.35;
}
.lgt-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; }
.lgt-note { font-size: 11.5px; color: var(--ink-2); font-style: italic; }
.lgt-ip { font-size: 11px; color: var(--muted); }
.lgt .lg-diff { margin-top: 5px; gap: 4px; }
.lgt .lg-chg { padding: 1px 6px; font-size: 10.5px; }

/* tombol export berpasangan */
.ex-group {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 9px 5px 11px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface-sunk);
}
.ex-lbl { font-size: 10.5px; color: var(--muted); font-weight: 600; }

/* pagination First 1 2 3 Next Last */
.pager {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 13px; border-top: 1px solid var(--border);
}
.pg {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 30px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-1); color: var(--ink-2);
  font-size: 12.5px; font-weight: 650; text-decoration: none;
  transition: border-color .12s, background .12s;
}
.pg:hover { border-color: color-mix(in srgb, var(--s1) 55%, var(--border)); text-decoration: none; }
.pg.on {
  background: var(--s1); border-color: var(--s1); color: #fff;
  box-shadow: 0 2px 6px color-mix(in srgb, var(--s1) 32%, transparent);
}
.pg.off { opacity: .4; pointer-events: none; }
.pg-info { margin-left: 8px; font-size: 11.5px; color: var(--muted); }

/* ------------------------------------------------ tabel hak akses
   Staff jadi baris, menu jadi kolom. Judul kolom dimiringkan supaya 15 menu
   muat tanpa membuat tabelnya selebar dua layar. */
.acl { border-collapse: separate; border-spacing: 0; }
.acl thead th { vertical-align: bottom; padding: 8px 4px; }
.acl th.acl-name { text-align: left; min-width: 190px; padding-left: 0; }
.acl th.acl-join, .acl th.acl-m { width: 58px; }
.acl th.acl-join em, .acl th.acl-m em {
  display: block; font-style: normal; font-size: 9.5px; color: var(--muted); font-weight: 500;
}
.acl th.acl-m span {
  display: block; writing-mode: vertical-rl; transform: rotate(180deg);
  white-space: nowrap; font-size: 11px; font-weight: 640; letter-spacing: .01em;
  max-height: 132px; margin: 0 auto 4px;
}
.acl th.acl-m[data-mod="jobdesk"] span { color: var(--m-jobdesk); }
.acl th.acl-m[data-mod="work"]    span { color: var(--m-work); }
.acl th.acl-m[data-mod="tools"]   span { color: var(--m-tools); }
.acl th.acl-m[data-mod="master"]  span { color: var(--m-master); }

.acl td { padding: 7px 4px; border-top: 1px solid var(--border); }
.acl td.acl-name { padding-left: 0; }
.acl td.acl-name b { font-size: 13px; }
.acl td.acl-name .muted { font-size: 11px; }
.acl td.acl-c, .acl td.acl-join { text-align: center; }
.acl td.acl-join { background: var(--wash); }
.acl input[type=checkbox] { width: auto; cursor: pointer; }
/* Kotak terkunci tetap harus TERBACA. Versi pertama memudarkannya ke .32 dan
   kotak tercentang jadi tak terbedakan dari kotak kosong — layarnya bilang
   "leader tidak punya Settings" padahal dia punya, cuma tidak bisa diubah.
   Ketahuan waktu halamannya digambar, bukan waktu datanya diperiksa. */
.acl input[type=checkbox]:disabled          { cursor: not-allowed; opacity: .5; }
.acl input[type=checkbox]:disabled:checked  { opacity: .85; }
/* Kolom yang memang tidak bisa disentuh diberi latar, jadi "tidak bisa diubah"
   terbaca dari bentuk kolomnya, bukan dari kepekatan tiap kotak. */
.acl td.acl-lock, .acl th.acl-lock { background: var(--wash); }

/* Pengecualian yang sedang berlaku diberi garis, bukan warna latar — latar
   berwarna di 15 kolom membuat tabelnya jadi papan catur yang tidak terbaca. */
.acl td.acl-x { box-shadow: inset 0 -3px 0 var(--s1); }
.acl tr.acl-locked td.acl-name b { color: var(--muted); }
.acl td.acl-act { text-align: right; padding-right: 0; white-space: nowrap; }

/* Pintasan rentang tanggal di Activity Log. Bawaannya hari ini supaya ringkas;
   tombol ini yang menjaga agar "ringkas" tidak berarti "jejak semalam hilang". */
.lg-quick {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}
.lg-quick .muted { font-size: 11px; font-weight: 600; }

/* ------------------------------------------------- papan pengumuman
   Daftar pengelolaan di Dashboard → Pengumuman. Kartu bacaannya sendiri
   (.announce) sudah ada sejak fase 0; ini yang sisi penulisnya. */
.ann-roles { display: flex; gap: 14px; flex-wrap: wrap; padding: 4px 0 2px; }
.ann-role {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 550; cursor: pointer;
}
.ann-role input { width: auto; cursor: pointer; }

.ann-manage { display: grid; gap: 10px; margin-top: 12px; }
.ann-row {
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 11px 13px; background: var(--surface-2);
  border-left: 3px solid var(--border);
}
/* Keadaan dibedakan lewat garis kiri, bukan latar berwarna — daftar yang
   tiap barisnya berwarna jadi sulit dibaca begitu isinya belasan.
   
   Namanya BERAWALAN ann-, bukan .live/.soon/.gone polos. Versi pertama memakai
   .soon dan langsung bertabrakan dengan pil "SOON" di sidebar, yang diwarnai
   untuk latar gelap — judul pengumuman jadi putih 45% di atas kartu terang,
   nyaris tak terbaca. Ini kedua kalinya .soon menabrak sesuatu; yang pertama
   merusak baris expiry paspor. */
.ann-row.ann-live { border-left-color: var(--good); }
.ann-row.ann-soon { border-left-color: var(--warn); }
.ann-row.ann-gone { border-left-color: var(--muted); opacity: .72; }
.ann-row.ann-on   { background: var(--wash-firm); border-color: var(--s1); }

.ann-row-top   { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; flex-wrap: wrap; }
.ann-row-title { font-size: 13.5px; display: block; }
.ann-row-body  { font-size: 12px; color: var(--ink-2); margin: 3px 0 8px; line-height: 1.5; }
.ann-row-foot  { display: flex; align-items: center; gap: 6px; font-size: 11px; flex-wrap: wrap; }

/* Foto lampiran pengumuman. Tinggi dibatasi supaya satu foto potret tidak
   mendorong pengumuman berikutnya keluar layar — yang mau lihat besar tinggal
   mengkliknya. */
.ann-photo { display: block; margin-top: 10px; }
.ann-photo img {
  max-width: 100%; max-height: 340px; border-radius: var(--r-sm);
  border: 1px solid var(--border); display: block;
}
.ann-photo-edit { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.ann-photo-edit img {
  width: 64px; height: 64px; object-fit: cover;
  border-radius: var(--r-sm); border: 1px solid var(--border);
}
.ann-row-thumb {
  width: 100%; max-width: 220px; max-height: 90px; object-fit: cover;
  border-radius: var(--r-sm); border: 1px solid var(--border);
  margin-top: 6px; display: block;
}

/* --------------------------------------- pemisah jenis situs di Vault
   Nama kelasnya berawalan cred- / cty- supaya tidak menabrak .ct-label dan
   .ct-type yang sudah dipakai sel tabel kredensial di dalamnya. */
.cred-type {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 18px 0 8px; padding-bottom: 6px;
  border-bottom: 2px solid color-mix(in srgb, var(--m-tools) 40%, var(--border));
}
.cred-type:first-of-type { margin-top: 4px; }
.cred-type .cty-name {
  font-size: 12px; font-weight: 750; letter-spacing: .1em; text-transform: uppercase;
  color: var(--m-tools);
}
.cred-type .cty-meta { font-size: 11.5px; color: var(--muted); }

/* --------------------------------------------------- LOGIN BANK di Vault
   Bagian ini sengaja LEBIH besar dan lebih keras daripada bagian lain di
   halaman ini. Bukan soal selera: kalau dua orang masuk ke satu rekening
   bank bersamaan, rekeningnya bisa diblokir. Jadi keadaan "sedang dipakai"
   harus terbaca dari seberang meja, bukan setelah dicari.

   Semua nama kelas berawalan bank- / bi- supaya tidak menabrak .b-* (lencana)
   maupun .bulk-* yang sudah ada. */
.bank-card { border-width: 2px; }
.bank-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.bank-title {
  font-size: 15px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink);
}
.bank-rule { font-size: 12.5px; color: var(--muted); }
.bank-rule b { color: var(--crit); }

.bank-grid {
  display: grid; gap: 14px; margin-top: 16px;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
}

.bank-item {
  /* --r-md tidak pernah ada; border-radius-nya jadi tidak berlaku sama
     sekali (sudutnya siku). --r adalah radius baku berkas ini. */
  border: 2px solid var(--border); border-radius: var(--r);
  background: var(--surface-1); overflow: hidden;
}
.bi-state {
  font-size: 12px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  padding: 8px 14px; text-align: center; color: #fff;
}
.bi-body { padding: 14px; }
.bi-name { font-size: 19px; font-weight: 780; line-height: 1.25; color: var(--ink); }
.bi-note { font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.bi-who  { font-size: 15px; font-weight: 720; margin-top: 10px; }
.bi-act  { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 13px; }
.bi-act .bi-btn { font-size: 12.5px; padding: 7px 11px; }
.bi-force { display: inline; margin: 0; }

/* --- bebas: tenang, tidak berteriak. Yang perlu menonjol itu yang dipakai. */
.bank-item.is-free { border-color: color-mix(in srgb, var(--good) 45%, var(--border)); }
.bank-item.is-free .bi-state { background: var(--good); }
.bank-item.is-free .bi-who,
.bank-item.is-free .bi-force,
.bank-item.is-free .cred-release { display: none; }

/* --- dipakai orang lain: merah penuh, tombolnya HILANG, bukan cuma pudar.
   Tombol yang kelihatan tapi ditolak server mengundang orang mencoba terus. */
.bank-item.is-busy {
  border-color: var(--crit);
  background: color-mix(in srgb, var(--crit) 7%, var(--surface-1));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit) 16%, transparent);
}
.bank-item.is-busy .bi-state { background: var(--crit); }
.bank-item.is-busy .bi-who   { color: var(--crit); }
.bank-item.is-busy .cred-copy,
.bank-item.is-busy .bi-open,
.bank-item.is-busy .cred-release { display: none; }

/* --- dipegang sendiri: kuning, dan tombol melepasnya paling menonjol. */
.bank-item.is-mine {
  border-color: var(--warn);
  background: color-mix(in srgb, var(--warn) 9%, var(--surface-1));
}
.bank-item.is-mine .bi-state { background: var(--warn); color: #4a3200; }
.bank-item.is-mine .bi-who   { color: #7d5800; }
:root[data-theme="dark"] .bank-item.is-mine .bi-who { color: var(--warn); }
.bank-item.is-mine .bi-force { display: none; }

/* Di tema gelap, --good dan --crit sengaja lebih terang supaya terbaca sebagai
   teks di latar gelap. Sebagai LATAR pita, teks putih di atasnya jadi tipis —
   jadi khusus dua pita itu tulisannya dibalik jadi gelap. Pita kuning sudah
   memakai tulisan gelap sejak awal, jadi tidak perlu diapa-apakan. */
:root[data-theme="dark"] .bank-item.is-free .bi-state { color: #04331d; }
:root[data-theme="dark"] .bank-item.is-busy .bi-state { color: #4a0413; }

/* Baris kelola (Edit / Hapus) di kartu bank — sengaja dipisah garis tipis dan
   dibuat lebih kecil dari tombol pemakaian. Yang dilakukan tiap malam itu
   menyalin password; mengedit itu sesekali, dan tidak boleh terlihat sama
   penting. */
.bi-manage {
  display: flex; gap: 7px; align-items: center;
  margin-top: 12px; padding-top: 10px;
  border-top: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
}
.bi-manage form { margin: 0; }

/* ------------------------------------------ teks orang tidak boleh merobek layout
   Satu catatan berisi 500 huruf tanpa spasi (tempelan ID transaksi, token, nama
   file panjang) memaksa barisnya melebar, dan karena kartunya ikut melebar
   SELURUH halaman jadi bisa digeser ke kanan — sidebar tergeser, kolom kanan
   terpotong. Dilaporkan abby di halaman Library, 22 Agustus 2026.

   `break-word`, bukan `anywhere`: yang pertama cuma memecah kata waktu dia
   memang tidak muat, tanpa mengubah lebar minimum yang dihitung browser. Kalau
   pakai `anywhere`, kolom tabel ikut menciut sampai satu huruf per baris. */
body { overflow-wrap: break-word; }

/* Wadah teks bebas dijamin boleh menciut. Tanpa min-width:0 sebuah flex/grid
   item menolak lebih sempit dari isinya, jadi aturan di atas tidak pernah
   sempat berlaku. */
.lib-main, .lib-body, .lib-title, .lib-url,
.ftdesc, .rc-note, .announce p, .ann-row-title { min-width: 0; overflow-wrap: break-word; }

/* Catatan Library memakai pre-wrap supaya barisnya utuh; itu mempertahankan
   spasi TAPI kata panjang tetap tidak dipecah, jadi dia butuh penegasan sendiri. */
.lib-body.lib-note { overflow-wrap: break-word; word-break: break-word; }

/* Judul dan nama file: dipecah di mana saja. Judul biasanya pendek, jadi tidak
   ada tabel yang dirugikan, dan judul 300 huruf tanpa spasi memang tidak punya
   tempat pecah yang wajar. */
.lib-title b, .lib-title a, .lib-url { overflow-wrap: anywhere; }

/* Blok yang sengaja "membludak" sampai tepi kartu — strip tab Library dan baris
   yang dipin/pribadi — ditulis dengan -18px, padahal padding kartu diperkecil
   jadi 16px waktu gaya ringkas dipasang. Selisih 2px itu membuat kartunya bisa
   digeser sedikit ke kanan. Ketahuan waktu mengukur halaman, bukan membacanya. */
.lib-tabs, .lib-item.pinned, .lib-item.private, .set-row.danger, .gridwrap {
  margin-left: -16px; margin-right: -16px;
  padding-left: 16px; padding-right: 16px;
}

/* ------------------------------------------- Control Tower: chat per auditor
   Nama kelas berawalan tc- supaya tidak menabrak tw- (kartu tahapan) maupun
   .meter yang dipakai kartu tahapan itu. */
.twchat .tc-sum {
  display: flex; align-items: center; gap: 26px; flex-wrap: wrap;
  padding: 4px 0 16px; border-bottom: 1px solid var(--border); margin-bottom: 14px;
}
.tc-big { display: flex; align-items: baseline; gap: 9px; }
/* Angka besar dirapatkan: .mono memberi jarak antarhuruf yang enak untuk kolom
   tabel, tapi di ukuran 30px membuat "256,5" terbaca seperti "256 , 5". */
.tc-total { font-size: 30px; font-weight: 780; line-height: 1; color: var(--ink);
            letter-spacing: -.015em; }
.tc-cap { font-size: 12px; color: var(--muted); }
.tc-side { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; }
.tc-side b { font-size: 14px; }

/* Naik/turun. Naik BUKAN otomatis hijau: chat yang melonjak bisa berarti malam
   yang berat, bukan prestasi. Jadi warnanya netral-biru untuk naik dan
   netral-jingga untuk turun — memberi tahu arahnya tanpa ikut menilai. */
.tc-d { font-weight: 700; }
.tc-d.up { color: var(--m-jobdesk, var(--ink)); }
.tc-d.dn { color: var(--warn); }
:root[data-theme="dark"] .tc-d.dn { color: var(--warn); }

.tc-rows { display: flex; flex-direction: column; }
.tc-row {
  display: flex; align-items: center; gap: 11px;
  padding: 8px 0; border-bottom: 1px dashed var(--border);
}
.tc-row:last-child { border-bottom: 0; }
.tc-rank { width: 18px; text-align: right; font-size: 11px; color: var(--muted); flex: 0 0 auto; }
.tc-name { font-weight: 650; flex: 0 0 148px; min-width: 0; overflow-wrap: break-word; }

.tc-bar {
  flex: 1 1 90px; min-width: 60px; height: 9px; border-radius: 99px;
  background: var(--surface-sunk); border: 1px solid var(--border); overflow: hidden;
}
.tc-bar i {
  display: block; height: 100%; border-radius: 99px;
  background: var(--m-jobdesk, var(--ink-2)); min-width: 2px;
}
.tc-val   { flex: 0 0 54px; text-align: right; font-weight: 700; font-size: 14px; }
.tc-delta { flex: 0 0 170px; font-size: 11.5px; }

/* Yang belum punya angka dipudarkan, bukan disembunyikan: hilang dari daftar
   terbaca sebagai "tidak masuk", padahal artinya "belum ada yang selesai". */
.tc-row.tc-zero .tc-name, .tc-row.tc-zero .tc-val { color: var(--muted); font-weight: 600; }
.tc-row.tc-zero .tc-bar i { background: var(--border); }

@media (max-width: 720px) {
  .tc-name  { flex: 1 1 100%; }
  .tc-delta { flex: 0 0 auto; }
}

/* ---------------------------------- Control Tower: "Belum selesai" jadi kolom
   Bentuk lamanya chip berjajar. Waktu satu-dua situs tertinggal itu terbaca
   enak; waktu 50 situs tertinggal — justru saat layar ini paling dibutuhkan —
   hasilnya 45 baris berisi empat kalimat yang sama persis, membungkus ke dua
   baris, tinggi barisnya beda-beda. Dilaporkan abby 22 Agustus 2026.

   Kolom tetap: tiap tahap selalu di tempat yang sama, jadi mata menyusuri
   kolom alih-alih membaca ulang kalimat yang sama puluhan kali. */
.atwrap { max-height: 430px; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.atgrid { display: table; width: 100%; border-collapse: collapse; }

.athead, .atrow { display: table-row; }
.athead > span { position: sticky; top: 0; z-index: 1; }

.athead > span, .atrow > * {
  display: table-cell; vertical-align: middle;
  padding: 7px 6px; border-bottom: 1px solid var(--border);
}
.athead > span {
  font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted);
  /* Opak, bukan --wash yang semi-transparan: baris yang tergulung di bawahnya
     akan terlihat menembus. Pelajaran yang sama dengan kolom sticky di grid. */
  background: color-mix(in srgb, var(--surface-sunk) 100%, var(--surface-1));
  border-bottom-width: 2px;
}

.atrow:last-child > * { border-bottom: 0; }
.at-site {
  font-weight: 660; font-size: 12.5px; text-decoration: none;
  white-space: nowrap; width: 1%;
}
.atrow:hover > * { background: color-mix(in srgb, var(--ink) 3%, transparent); }

/* Kolom tanda: sempit dan tengah, supaya empat kolomnya berbaris rapi. */
.at-c { width: 62px; text-align: center; font-size: 13px; line-height: 1; }
.athead .at-c { text-align: center; }
/* Ukuran tandanya hanya berlaku DI DALAM kolom. Waktu ditulis polos sebagai
   .at-chk / .at-no, keterangan di bawah tabel ikut kena — "belum" tercetak
   17px merah besar seperti judul, "tinggal dicek" jadi 9px hampir tak terbaca. */
.at-ok  { color: var(--good); }
.at-chk { color: var(--warn); }
.at-no  { color: color-mix(in srgb, var(--crit) 75%, var(--ink)); }
.at-c.at-chk { font-size: 9px; }
.at-c.at-no  { font-size: 17px; }

/* Satu baris, dipotong. Satu nama panjang tidak boleh menambah tinggi
   seluruh tabel. */
.at-who {
  font-size: 11.5px; color: var(--muted);
  max-width: 210px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Situs yang belum tersentuh sama sekali diberi pita tipis di kiri — bukan
   latar merah penuh, karena kalau 45 dari 50 baris merah, tidak ada yang
   menonjol lagi. */
.atrow.at-cold > .at-site { box-shadow: inset 3px 0 0 color-mix(in srgb, var(--crit) 55%, transparent); }

.atkey {
  display: flex; gap: 16px; flex-wrap: wrap;
  margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--border);
  font-size: 11px; color: var(--muted);
}
.atkey span { display: inline-flex; align-items: center; gap: 5px; }
.atkey b { font-size: 12px; line-height: 1; }
.atkey .at-chk b { font-size: 8px; }
.atkey .at-no  b { font-size: 15px; }

/* --------------------------------- Control Tower: "Yang bertugas" dikelompokkan
   Sebelumnya 34 baris beruntun, nama shift diulang di tiap baris, dan yang
   sudah selesai ikut memenuhi layar padahal Control Tower memang cuma untuk
   yang belum selesai. Dilaporkan abby 22 Agustus 2026. */
.dt-shift {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; gap: 7px;
  padding: 7px 2px 6px; margin-top: 10px;
  font-size: 10px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted);
  background: color-mix(in srgb, var(--surface-sunk) 100%, var(--surface-1));
  border-bottom: 2px solid var(--border);
}
.dt-shift:first-child { margin-top: 0; }
.dt-shift span {
  font-size: 10px; padding: 0 6px; border-radius: 99px;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--ink-2);
}

.dt-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 2px; border-bottom: 1px solid var(--border);
}
.dt-row:last-child { border-bottom: 0; }
.dt-name {
  flex: 1; min-width: 0; font-size: 12.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dt-chips { display: flex; gap: 5px; flex: 0 0 auto; }

/* Chip sisa kerja dibuat tenang, bukan oranye penuh. Waktu 30 orang sama-sama
   punya sisa, 60 lencana oranye berhenti berarti "perhatikan ini" dan cuma
   jadi warna latar. Angkanya yang ditebalkan — itu yang dibaca. */
.dt-chip {
  font-size: 11px; color: var(--ink-2);
  padding: 2px 8px; border-radius: 99px;
  background: var(--surface-2); border: 1px solid var(--border);
  white-space: nowrap;
}
.dt-chip b { color: var(--warn); font-weight: 750; }
:root[data-theme="dark"] .dt-chip b { color: var(--warn); }

.dt-sum {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--border);
  font-size: 12px;
}
.dt-sum .muted { font-size: 11.5px; min-width: 0; overflow-wrap: break-word; }
.dt-sum.dt-ok b { color: var(--good); }

/* Angka CS yang pernah diketik tapi belum pernah berlaku — dari masa sebelum
   kolom CS dihubungkan ke daftar cs_staff. */
.wcshint { margin-top: 3px; font-size: 10px; line-height: 1.3; color: var(--warn); max-width: 96px; }
.wcshint b { font-weight: 750; }

/* Situs IDN yang belum ditandai gede/kecil. Dikuningkan supaya kelihatan
   dari jauh waktu tabelnya di-scroll: yang belum ditandai TIDAK dihitung
   extra bonusnya, dan barisnya harus bisa dicari tanpa membaca satu-satu. */
.wgol-kosong { border-color: var(--warn) !important; color: var(--warn); }

/* -------------------------------------------------- Jatah Post (Roster)
   Beberapa kolom pendek, bukan satu daftar 51 baris — mengikuti bentuk sheet
   "JADWAL POSTING KESALAHAN" yang sudah dihafal tim. */
.pdgrid {
  display: grid; gap: 6px 14px;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  margin-bottom: 6px;
}
.pdrow {
  /* Kode situs tim panjang-panjang (DINGDONGTOGEL, PARTAITOGEL). Kolomnya
     dilebarkan sampai muat; yang lebih panjang lagi tetap dipotong, dan nama
     lengkapnya ada di title. */
  display: grid; grid-template-columns: 62px 1fr; gap: 9px;
  align-items: center; padding: 4px 0;
  border-bottom: 1px dashed var(--border);
}
.pd-site {
  font-size: 11.5px; font-weight: 680; letter-spacing: .02em;
  color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pd-pick { width: 100%; padding: 5px 8px; font-size: 12px; }
.pd-name { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }

/* Yang belum ada namanya ditandai di kode situsnya, bukan dengan mewarnai
   seluruh barisnya: waktu semuanya masih kosong, layar penuh warna tidak
   menunjukkan apa-apa. */
.pdrow.pd-empty .pd-site { color: var(--muted); font-weight: 600; }
.pdrow.pd-empty .pd-pick { border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); }

.pdclean .card-head { align-items: center; }

/* ------------------------------------- angka yang sudah diisi, dikunci
   CS Chat & Audit: kolom angkanya berjajar rapat dan diisi tengah malam. Satu
   klik nyasar lalu satu ketikan sudah cukup mengganti angka yang sudah benar,
   dan tidak ada yang tahu sampai totalnya tidak cocok. Dilaporkan abby
   23 Agustus 2026. */
/* Terlihat SUDAH SELESAI, bukan setengah jadi. Garis putus-putus sempat
   dicoba dan salah membaca: di aplikasi ini garis solid berarti "beres",
   putus-putus berarti "masih menunggu". Yang dikunci itu angka yang sudah
   benar — jadi solid, tenggelam sedikit, tulisannya lebih kalem. */
input.inp.locked {
  background: var(--surface-sunk);
  color: var(--ink-2);
  border-color: var(--line-soft, var(--border));
  box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ink) 7%, transparent);
  cursor: default;
}
/* Isyarat bahwa kuncinya bisa dibuka — muncul cuma waktu kursor di atasnya,
   supaya 15 baris sekaligus tidak jadi ramai. */
input.inp.locked:hover {
  border-color: color-mix(in srgb, var(--m-jobdesk, var(--ink)) 45%, var(--border));
  color: var(--ink);
}
.lockhint { font-size: 11.5px; color: var(--muted); margin-top: 8px; }
.lockhint b { color: var(--ink-2); font-weight: 650; }

/* ------------------------------- "dibuka di TOGELUP"
   Halaman ini memilih situsnya sendiri waktu dibuka tanpa ?website=. Pilihan
   yang tidak diumumkan bikin orang mengisi angka ke situs yang salah, jadi
   keterangannya harus terbaca — tapi bukan peringatan, karena tidak ada yang
   salah. Nada biru datar, sekali lihat selesai. */
.resumed {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  margin-bottom: 14px; padding: 9px 13px;
  font-size: 12.5px; color: var(--ink-2);
  background: color-mix(in srgb, var(--m-jobdesk) 8%, var(--surface-1));
  border: 1px solid color-mix(in srgb, var(--m-jobdesk) 26%, var(--border));
  border-radius: var(--r);
}
.resumed b { color: var(--ink); font-weight: 700; }
.resumed .muted { font-size: 11.5px; }

/* ------------------------------- siapa yang mencentang Final checks
   Datanya sudah disimpan sejak fase 2, cuma tidak pernah ditampilkan — jadi
   centang yang salah tidak bisa ditelusuri tanpa membuka Activity Log.
   Diminta abby 24 Agustus 2026. */
.ck-slot { display: block; margin-top: 4px; }
.ck-by {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; color: var(--muted);
}
.ck-by b { font-weight: 700; color: var(--ink-2); }
.ck-by.on { color: var(--good); }
.ck-by.on b { color: var(--good); }

/* ---------------------------- Chat Recap: situs yang malamnya sudah CLEAR
   Pita hijau tipis di kiri, bukan latar hijau penuh. Waktu 45 dari 51 situs
   sudah beres, latar penuh berhenti menandai apa pun — dan yang dicari mata di
   layar ini justru yang BELUM. Diminta abby 24 Agustus 2026. */
tr.rc-clear > td:first-child {
  box-shadow: inset 3px 0 0 var(--good);
}
tr.rc-clear > td { background: color-mix(in srgb, var(--good) 5%, transparent); }
.rc-tick { color: var(--good); font-weight: 700; margin-left: 5px; }
.rc-done { color: var(--good); font-weight: 750; }

/* ============================================================
   Dashboard yang hidup
   ------------------------------------------------------------
   Penanda kecil di pojok. Yang paling penting BUKAN tulisan "live" waktu
   semuanya lancar — tapi kalimat waktu penyegarnya gagal. Papan yang diam-diam
   membeku lebih berbahaya daripada papan yang jujur statis: orangnya jadi
   PERCAYA pada angka lama, justru karena ada tulisan "live" di pojok.
   ============================================================ */
.livedot {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 600;
  padding: 5px 11px; border-radius: 99px;
  border: 1px solid var(--border); background: var(--surface-1);
  white-space: nowrap;
}
.livedot > i {
  width: 7px; height: 7px; border-radius: 99px; flex: 0 0 auto;
  background: var(--good);
}
.livedot.on  { color: var(--muted); }
.livedot.on > i { animation: livepulse 2.4s ease-in-out infinite; }

/* Gagal = merah dan diam. Titik yang tetap berdenyut waktu datanya sudah
   basi persis kebalikan dari yang harus disampaikan. */
.livedot.off {
  color: var(--crit);
  border-color: color-mix(in srgb, var(--crit) 45%, transparent);
  background: color-mix(in srgb, var(--crit) 8%, var(--surface-1));
}
.livedot.off > i { background: var(--crit); animation: none; }

@keyframes livepulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* Angka yang baru berubah dikedipkan sebentar, supaya yang bergerak
   ketahuan tanpa harus mengingat angka sebelumnya. */
.livebump { animation: livebump .9s ease-out; border-radius: 4px; }
@keyframes livebump {
  0%   { background: color-mix(in srgb, var(--s1) 34%, transparent); }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .livedot.on > i { animation: none; }
  .livebump { animation: none; }
}

/* Pengumuman baru masuk: tautan muat-ulang, bukan isinya disuntikkan. */
.annnew {
  margin: 14px 0 4px; padding: 10px 14px;
  border-radius: var(--r); font-size: 13px;
  border: 1px solid color-mix(in srgb, var(--s1) 30%, var(--border));
  background: color-mix(in srgb, var(--s1) 9%, var(--surface-1));
}
.annnew a { font-weight: 700; margin-left: 6px; }


/* ==================================================================
   Keadaan malam ini — satu angka besar + papan 51 situs
   ==================================================================
   abby: "tpi bosan beb itu bentuknya".

   Sebelumnya empat kartu berukuran sama berjajar. Empat kotak sederajat
   tidak pernah mengatakan mana yang harus dilihat duluan, dan sesudah
   warnanya dilepas (AUDITOR_Aturan_Warna.md) tidak ada apa pun yang
   menggantikan pekerjaan itu.

   Yang menggantikannya BEDA UKURAN, bukan warna baru. Satu angka besar
   sekali menjawab satu pertanyaan dari seberang ruangan; sisanya mengecil
   jadi keterangan. Itu yang membuat sebuah layar terbaca mahal — bukan
   gradien.
   ================================================================== */
.state {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 14px; margin-bottom: 14px;
}
.st-hero { padding: 20px 22px 18px; display: flex; flex-direction: column; justify-content: center; }
.st-k {
  color: var(--muted); font-size: 10px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase;
}
/* clamp: di layar sempit dia mengecil sendiri, tapi tidak pernah turun sampai
   terbaca sebagai angka biasa. Yang bikin dia bekerja justru ukurannya. */
.st-big {
  font-size: clamp(52px, 9vw, 118px); font-weight: 800; line-height: .94;
  letter-spacing: -.045em; margin-top: 8px;
  font-variant-numeric: tabular-nums;
}
/* Penyebutnya diredupkan dan dikecilkan: yang berubah tiap malam pembilangnya.
   Dua angka sama besar berarti mata harus memilih dulu mana yang dibaca. */
.st-of { font-size: .38em; font-weight: 600; color: var(--muted); letter-spacing: -.02em; margin-left: 4px; }
.st-meter { margin-top: 16px; }
.st-note { font-size: 11.5px; color: var(--muted); margin-top: 9px; }

/* ── Kemajuan dalam CHAT, di dalam kartu yang sama dengan "situs CLEAR" ──
   Dipisah garis, bukan dijadikan kartu sendiri: keduanya menjawab satu
   pertanyaan yang sama ("malam ini sudah sejauh mana?"), cuma satuannya beda.
   Dua kartu berjajar akan terbaca sebagai dua hal yang tidak berhubungan.

   TIDAK BERWARNA. Ini angka kemajuan, bukan peringatan — 40% jam sepuluh malam
   itu normal, dan mewarnainya merah akan melatih orang mengabaikan merah. */
.st-chat {
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid var(--border);
}
.st-chat-top { display: flex; align-items: baseline; gap: 10px; }
.st-chat-pct {
  font-size: 22px; font-weight: 800; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.st-chat-n {
  font-size: clamp(24px, 3.4vw, 34px); font-weight: 700; line-height: 1.1;
  letter-spacing: -.03em; margin-top: 4px;
  font-variant-numeric: tabular-nums;
}
/* .st-of bawaan 0,38em — dipasang untuk angka raksasa 118px di atas. Di angka
   34px ini dia jatuh jadi 13px dan penyebutnya nyaris hilang, padahal
   "dari berapa" itu separuh artinya. */
.st-chat-n .st-of { font-size: .58em; }
.st-chat .st-meter { margin-top: 12px; }
.st-chat .st-note  { margin-top: 8px; }

.st-side { display: flex; flex-direction: column; gap: 10px; }
/* Baris, bukan kartu. Kartu terbaca setara dengan angka besar di sebelahnya —
   dan mereka memang tidak setara. */
.st-row {
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r); padding: 12px 15px;
  display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: 2px 12px; flex: 1;
}
/* Penempatannya DIPATOK, tidak diserahkan ke auto-placement. Waktu .st-rv
   diberi grid-row: 1 / span 2, penempatan otomatis melempar labelnya ke kolom
   kanan — jadi tiap baris terbaca terbalik: angka di kiri, judul di kanan. */
.st-rk {
  grid-column: 1; grid-row: 1;
  color: var(--muted); font-size: 10px; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase;
}
.st-rv {
  grid-column: 2; grid-row: 1 / span 2; align-self: center; justify-self: end;
  font-size: 25px; font-weight: 700; letter-spacing: -.025em;
  font-variant-numeric: tabular-nums;
}
.st-rd { grid-column: 1; grid-row: 2; font-size: 11.5px; color: var(--muted); }
.st-row.over {
  border-color: color-mix(in srgb, var(--crit) 34%, var(--border));
  background: color-mix(in srgb, var(--crit) 5%, var(--surface-1));
}
.st-row.over .st-rk { color: color-mix(in srgb, var(--crit) 70%, var(--muted)); }
.st-row.over .st-rd { color: color-mix(in srgb, var(--crit) 78%, var(--muted)); }

@media (max-width: 1000px) {
  .state { grid-template-columns: 1fr; }
  .st-big { font-size: clamp(46px, 13vw, 72px); }
}

/* ==================================================================
   Lambang SMB Group — ciri khas AUDITOR
   ==================================================================
   abby: "bisa ga buatkan tampilannya punya khas unik beda dengan dashboard
   lainnya, jadi ketika dibuka otomatis sudah tau oh ini dashboard auditor".

   Jawabannya logo SMB Group milik abby, di sisi gelap saja.

   Pita 51 goresan di header dan papan 51 kotak di dashboard sempat ada di
   sini juga, lalu dicabut atas permintaan abby ("ini ga usah") pada hari yang
   sama. CSS-nya ikut dilepas, bukan disisakan sebagai aturan mati — aturan
   CSS yang tidak dipakai apa pun akan menabrak nama kelas baru suatu hari,
   dan itu sudah kejadian di proyek ini (.soon vs .exp-soon).
   ================================================================== */

.brand {
  display: block; padding: 4px 8px 20px; text-decoration: none;
}
.brand-logo {
  display: block; width: 100%; max-width: 186px; height: auto;
  /* Gambarnya sudah nyaris putih; sedikit diredupkan supaya tidak lebih
     terang daripada menu aktif di bawahnya. Lambang yang paling menyala di
     layar akan merebut perhatian dari pekerjaan. */
  opacity: .92;
}
/* Nama alatnya, di bawah nama perusahaannya. Dua tingkat, bukan satu:
   SMB GROUP yang punya, AUDITOR nama alatnya. */
.brand-app {
  display: block; margin-top: 9px; padding-left: 2px;
  font-size: 10px; font-weight: 800; letter-spacing: .34em;
  color: var(--side-dim);
}

/* ---------- logo di halaman login ---------- */
.lg-logo { display: block; width: 300px; max-width: 78%; height: auto; }

/* ==================================================================
   Rasa buku audit — pilihan kedua abby untuk ciri khas
   ==================================================================
   AUDITOR dan "Buku Dosa" memang buku. Jadi tampilannya ikut jadi buku:
   angka berjajar rapi seperti pembukuan, garis rambut sebagai pemisah, dan
   pernyataan "sudah diperiksa" berbentuk STEMPEL — karena di dunia nyata
   memang begitu bentuknya.
   ================================================================== */

/* ---------- stempel CLEAR ----------
   Bukan lencana bulat. CLEAR itu PERNYATAAN SESEORANG — "saya sudah
   memeriksa situs ini" — bukan status yang dihitung mesin, dan stempel
   membawa arti itu tanpa satu kata tambahan pun.

   Warnanya tetap biru, bukan hijau: aturan warna berlaku juga di sini
   (AUDITOR_Aturan_Warna.md). Yang membuatnya terbaca sebagai stempel adalah
   BENTUKNYA — garis tepi ganda, huruf direnggangkan, dan kemiringan kecil. */
.stamp {
  display: inline-block; padding: 4px 12px 3px;
  border: 2px solid var(--s1); border-radius: 4px;
  color: var(--s1); background: transparent;
  font-size: 11.5px; font-weight: 800; letter-spacing: .22em;
  text-transform: uppercase; line-height: 1.15;
  /* -2.5deg: cukup untuk terbaca sebagai dicap, tidak cukup untuk terlihat
     seperti elemen yang posisinya rusak. Di atas ~4deg orang mulai mengira
     halamannya salah render. */
  transform: rotate(-2.5deg);
  /* Garis tepi kedua di dalam — inilah yang membedakannya dari kotak biasa.
     Stempel karet sungguhan meninggalkan dua garis karena tepinya lebih
     menekan daripada tengahnya. */
  box-shadow: inset 0 0 0 1px var(--surface-1), inset 0 0 0 3px color-mix(in srgb, var(--s1) 45%, transparent);
  /* Tinta stempel tidak pernah rata. 88% supaya terbaca dicap, bukan dicetak. */
  opacity: .88;
}
/* Di dalam tabel dia tidak boleh memiringkan barisnya. */
td .stamp, .rc-clear .stamp { transform: none; font-size: 10px; padding: 2px 8px 1px; }

/* ---------- angka berjajar seperti pembukuan ----------
   Semua angka di aplikasi ini dibandingkan ke atas-bawah: 51 baris chat, 51
   baris mischat, durasi audit. Angka proporsional membuat "118,027" dan
   "9,004" tidak sejajar digitnya, dan mata harus membaca ulang tiap baris
   alih-alih memindai kolomnya.

   Ini bukan selera: ini yang membedakan tabel angka yang bisa dipindai dari
   tabel angka yang harus dibaca. */
.mono, td.num, .val, .st-big, .st-rv, .rb-n, input[type="number"] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
}
/* Nama alat di bawah logo perusahaan — dua tingkat, sama seperti di sidebar. */
.lg-app {
  margin-top: 12px; font-size: 11px; font-weight: 800;
  letter-spacing: .42em; color: rgba(255,255,255,.5);
}

/* ==================================================================
   Jatah saya malam ini
   ==================================================================
   abby: "tampilan dashboard utama boleh ganti isinya".

   Sebelumnya dashboard memberi tahu KEADAAN tapi tidak memberi tahu APA YANG
   HARUS DIKERJAKAN — jadi halaman pertama sesudah login justru bukan halaman
   yang dipakai. Dua bagian ini yang menutupnya.
   ================================================================== */
.duty { margin-bottom: 18px; }
.duty-head {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.duty-head h3 {
  font-size: 10px; font-weight: 700; letter-spacing: .13em;
  text-transform: uppercase; color: var(--muted); margin: 0;
}
.duty-head > :last-child { margin-left: auto; }
.duty-left { font-size: 11.5px; color: var(--muted); }
.duty-left b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

/* auto-fit, bukan jumlah kolom tetap: jumlah kotaknya berubah per orang —
   auditor bisa dapat 2, 3, atau 4 (kalau kebagian tugas fatal). Kolom tetap
   akan meninggalkan satu kotak sendirian di baris kedua. */
.duty-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 12px;
}
.duty-card {
  display: block; text-decoration: none;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r); padding: 13px 15px 14px;
  transition: border-color .12s, transform .12s;
}
.duty-card:hover { border-color: var(--s1); transform: translateY(-1px); }
/* Yang sudah beres diredupkan lewat WARNA, bukan opacity — opacity di bawah 1
   membuat stacking context dan sudah sekali mengurung menu yang bisa diklik
   (lihat pelajaran no. 41). */
.duty-card.done { background: var(--surface-sunk); border-style: dashed; }
.duty-card.done .dc-lbl, .duty-card.done .dc-n { color: var(--muted); }

.dc-top { display: flex; align-items: baseline; gap: 10px; margin-bottom: 9px; }
.dc-lbl { font-size: 12.5px; font-weight: 650; color: var(--ink); }
.dc-n {
  margin-left: auto; font-size: 17px; font-weight: 700; color: var(--ink);
  letter-spacing: -.02em; font-variant-numeric: tabular-nums;
}
.dc-of { font-size: .62em; color: var(--muted); font-weight: 500; }

/* Warnanya DIPATOK. Kartunya <a>, jadi teks di dalamnya mewarisi warna
   tautan — dan keterangan "tugas ke-51 belum disentuh" tergambar biru, yaitu
   warna yang di aplikasi ini berarti "bisa ditekan". Keterangan yang berwarna
   seperti tombol adalah keterangan yang mengaku jadi tombol. */
.dc-sites { display: flex; flex-wrap: wrap; gap: 4px; font-size: 11.5px; color: var(--muted); }
.dc-clear { font-size: 11.5px; color: var(--muted); }
/* Kode situs, bukan nama panjang: yang dicari orang di daftar ini "situs mana
   lagi", dan kode itu yang dia hafal. Nama lengkapnya ada di title. */
.dc-site {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px; font-weight: 600; letter-spacing: .02em;
  padding: 2px 6px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--border); color: var(--ink-2);
}
.dc-more { font-size: 10.5px; color: var(--muted); align-self: center; padding-left: 2px; }

/* ---------- pengumuman sebagai bagian UTAMA dashboard ----------
   abby: "aku maunya yang dashboard depan ini untuk utamanya pengumuman".

   Dua hal yang membuat sesuatu terbaca utama, dan dia butuh dua-duanya:
   LETAK (paling atas, tepat di bawah sapaan) dan UKURAN. Kalau judulnya
   tetap label 10px huruf besar seperti bagian lain, dia terbaca setara dengan
   "Jatah saya" dan tautan cepat — padahal dia yang utama. */
.ann-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.ann-head h2 {
  margin: 0; font-size: 19px; font-weight: 700;
  letter-spacing: -.02em; color: var(--ink);
}
/* Jumlah yang belum dibaca. Angka ini yang menentukan halaman ini perlu
   dibaca atau tinggal dilewati — jadi dia berdiri di sebelah judulnya,
   bukan disembunyikan di dalam daftar. */
.ann-new {
  font-size: 11.5px; color: var(--s1); font-weight: 600;
  padding: 2px 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--s1) 12%, transparent);
}
.ann-new b { font-variant-numeric: tabular-nums; }

/* ---------- kemajuan baca per situs (Chat & Missed Recap) ----------
   abby: "bikin status bar-nya per situs, jadi sudah berjalan berapa persen
   yang sudah selesai dibaca".

   Halaman ini menunjukkan berapa CHAT-nya dan apakah sudah CLEAR. Yang tidak
   terlihat: PROSESNYA. Situs dengan 15 CS yang baru dibaca 2 dan situs yang
   tinggal 1 lagi terlihat persis sama — dua-duanya "belum CLEAR". */
.rdc { width: 150px; }
.rd { display: flex; align-items: center; gap: 9px; }
.rd-bar {
  flex: 1; height: 6px; min-width: 44px; border-radius: 3px;
  background: var(--surface-sunk); overflow: hidden;
}
.rd-bar i { display: block; height: 100%; background: var(--s1); border-radius: 3px; }
/* Yang sudah penuh TIDAK diberi warna lain — aturan warna berlaku di sini juga
   (AUDITOR_Aturan_Warna.md). Yang membedakannya batangnya yang memang penuh,
   dan angkanya yang berhenti berbeda. Hijau untuk "selesai" akan jadi warna
   ketiga di aplikasi yang cuma punya dua. */
.rd-bar.full i { background: color-mix(in srgb, var(--s1) 62%, var(--muted)); }
.rd-n {
  font-size: 11px; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.rd-of { color: var(--muted); font-weight: 500; }
/* Situs yang semua CS-nya nol chat. Bukan "0%" — tidak ada yang perlu dibaca,
   dan aturan aplikasi ini memang melarang memulainya. */
.rd-none { font-size: 10.5px; color: var(--muted); font-style: italic; }

@media (max-width: 1100px) {
  /* Di layar sempit angkanya yang wajib, batangnya yang dilepas. */
  .rdc { width: 62px; }
  .rd-bar { display: none; }
}

/* ---------- konfirmasi keluar ----------
   abby: "buatkan juga notifikasi ketika logout untuk konfirmasi logout all
   credential".

   Keluar memang sudah melepas semua slot sejak fase 3; yang ditambahkan
   konfirmasinya, bukan aturannya. Dialog ini cuma muncul kalau orangnya
   memang sedang memegang sesuatu. */
.lgo {
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface-1); color: var(--ink);
  padding: 22px 24px; max-width: 460px; width: calc(100% - 32px);
  box-shadow: 0 24px 60px rgba(0,0,0,.28);
}
.lgo::backdrop { background: rgba(3, 7, 18, .55); }
.lgo h3 { margin: 0 0 8px; font-size: 16px; font-weight: 700; letter-spacing: -.01em; }
.lgo-sub { margin: 0 0 14px; font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.lgo-sub b { color: var(--ink); font-weight: 650; }

/* Daftar yang akan dilepas. Garis rambut antar baris, bukan kotak per baris:
   ini daftar, dan daftar dibaca ke bawah. */
.lgo-list {
  list-style: none; margin: 0 0 18px; padding: 0;
  border: 1px solid var(--border); border-radius: var(--r); overflow: hidden;
}
.lgo-list li {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  padding: 8px 12px; border-top: 1px solid var(--border);
  background: var(--surface-2);
}
.lgo-list li:first-child { border-top: 0; }
.lgo-site {
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; font-weight: 700; color: var(--ink);
}
/* Slot admin ditandai MERAH — dia cuma boleh dipegang 1 orang, jadi yang
   dilepas mendadak bisa membuat orang lain masuk ke panel yang sedang dipakai.
   Sesuai aturan warna: merah cuma untuk hal yang memang masalah. */
.lgo-kind {
  font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 999px;
  background: var(--surface-sunk); color: var(--muted); border: 1px solid var(--border);
}
.lgo-kind.adm {
  color: var(--crit);
  border-color: color-mix(in srgb, var(--crit) 40%, var(--border));
  background: color-mix(in srgb, var(--crit) 10%, var(--surface-sunk));
}
.lgo-lbl { font-size: 11.5px; color: var(--ink-2); }
.lgo-since { margin-left: auto; font-size: 10.5px; color: var(--muted); white-space: nowrap; }

.lgo-act { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.lgo-act .btn.pri { margin-left: 0; }
@media (max-width: 480px) {
  .lgo-act { flex-direction: column-reverse; }
  .lgo-act .btn { width: 100%; text-align: center; }
}

/* ── Malam yang menahan tanggal ─────────────────────────────────────────
   abby memilih aturan "tanggal tidak maju sampai malam sebelumnya ditutup".
   Aturan itu aman HANYA kalau keadaan tertahannya kelihatan. Tanggal yang
   diam-diam berhenti terbaca sebagai sistem rusak, dan orang jam 2 pagi akan
   mengarang jalan keluar sendiri.

   Warnanya ikut aturan proyek: BIRU waktu ini sekadar keadaan yang bisa
   dikerjakan (tertinggal 1 malam — normal, tinggal diselesaikan), MERAH baru
   waktu sudah tertinggal 2 malam atau lebih, karena di situ dia berhenti jadi
   pekerjaan biasa dan mulai jadi masalah. Bukan dua warna untuk membedakan
   kotak — dua warna untuk dua ARTI. */
.nightheld {
  display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; margin: 0 0 14px;
  border: 1px solid color-mix(in srgb, var(--s1) 42%, transparent);
  border-left: 3px solid var(--s1);
  border-radius: var(--r-sm);
  background: var(--wash);
}
.nightheld.nh-crit {
  border-color: color-mix(in srgb, var(--crit) 42%, transparent);
  border-left-color: var(--crit);
  background: color-mix(in srgb, var(--crit) 8%, var(--surface-1));
}
.nh-main { flex: 1 1 320px; line-height: 1.5; }
/* Daftar malam tertunggak. Tiap malam punya barisnya sendiri beserta
   tautannya — satu angka gabungan tidak bisa ditindaklanjuti, yang mau
   mengerjakannya tetap harus menebak malam yang mana. */
.nh-list { margin: 8px 0 0; padding-left: 18px; }
.nh-list li { margin: 3px 0; }
.nh-list a { font-variant-numeric: tabular-nums; }
.nh-close { position: relative; }
.nh-close > summary { list-style: none; cursor: pointer; }
.nh-close > summary::-webkit-details-marker { display: none; }
.nh-form {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 30;
  width: 320px; padding: 12px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-1); box-shadow: 0 10px 28px rgba(0,0,0,.28);
}
.nh-form input[type=text] { width: 100%; margin: 8px 0; }
@media (max-width: 640px) {
  .nh-form { position: static; width: auto; box-shadow: none; }
}

/* Chip "TERTAHAN" di sebelah kotak tanggal, di SETIAP halaman.
   Biru = tinggal diselesaikan; merah = sudah tertinggal 2 malam atau lebih. */
.chip-hold {
  color: var(--s1); background: var(--wash-firm);
  text-decoration: none;
}
.chip-hold.chip-crit {
  color: var(--crit);
  background: color-mix(in srgb, var(--crit) 15%, transparent);
}

/* Persen kecil di sebelah angka kartu ringkasan Chat Recap.
   Sengaja jauh lebih kecil dan redup daripada angkanya: yang dikejar orang
   jumlah situs yang tersisa, bukan persennya — persen cuma memberi rasa
   "sudah sejauh mana" dari kejauhan. Kalau ukurannya disamakan, dua angka
   sederajat di satu kartu berebut perhatian dan tidak ada yang menang. */
.tile-pct {
  font-size: .52em; font-weight: 600; color: var(--muted);
  margin-left: 8px; letter-spacing: .02em;
}

/* ── Panel "slot yang saya pegang" ──────────────────────────────────────
   abby: "penambahan bisa release satu satu dari bagian 🔒 You are holding 4
   slots". Pil di header jadi <details> yang isinya daftar slot, satu tombol
   Lepas per baris.

   Panelnya position:absolute supaya membukanya tidak menggeser header —
   masalah yang sama dengan menu ⋯ di tabel CS, dan di header akibatnya lebih
   parah: SELURUH halaman turun beberapa piksel. */
.heldbox { position: relative; }
.heldbox > summary { list-style: none; cursor: pointer; }
.heldbox > summary::-webkit-details-marker { display: none; }

.hb-menu {
  position: absolute; right: 0; top: calc(100% + 7px); z-index: 60;
  width: 320px; padding: 10px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-1); box-shadow: var(--sh-2);
}
.hb-head {
  margin: 0 0 8px; font-size: 10px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
}
.hb-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 8px; border-radius: var(--r-sm);
  border-left: 3px solid transparent;
}
.hb-row + .hb-row { margin-top: 2px; }
.hb-row:hover { background: var(--surface-2); }
/* Slot admin: merah, karena dia cuma boleh dipegang SATU orang — yang
   tertinggal memblokir seluruh situs untuk semua orang. Warna untuk MAKNA. */
.hb-crit { border-left-color: var(--crit); }
.hb-what { flex: 1 1 auto; min-width: 0; line-height: 1.35; }
.hb-lab {
  display: block; font-size: 11.5px; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hb-meta { display: block; font-size: 10.5px; color: var(--muted); }
.hb-adm-t { color: var(--crit); font-weight: 650; }
.hb-all { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.hb-link {
  display: block; margin-top: 8px; font-size: 11.5px;
  color: var(--s1); text-decoration: none;
}
.hb-link:hover { text-decoration: underline; }

@media (max-width: 640px) {
  .hb-menu { position: fixed; left: 12px; right: 12px; width: auto; }
}

/* ── Data Staff: berkas + rekening gaji ─────────────────────────────────
   Tabelnya lebar (2 kolom teks + 3 kolom gambar), jadi dia yang menggulir
   sendiri di dalam kotaknya — BUKAN halamannya. Halaman yang menggulir ke
   samping membuat sidebar ikut hilang (pelajaran no. 31). */
.sfwrap { overflow-x: auto; }
.sf-table { width: 100%; border-collapse: collapse; min-width: 900px; }
.sf-table th {
  text-align: left; font-size: 10.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); font-weight: 700;
  padding: 8px 10px; border-bottom: 1px solid var(--border);
}
.sf-table td {
  padding: 10px; border-bottom: 1px solid var(--border);
  vertical-align: top;
}
/* Baris sendiri diberi pita biru tipis: di daftar 20-an orang, "mana baris
   saya" itu pertanyaan pertama yang dibawa orang ke halaman ini. */
.sf-me td:first-child { border-left: 3px solid var(--s1); }
.sf-sub  { font-size: 11px; color: var(--muted); margin-top: 2px; }
.sf-bank { font-weight: 600; }
.sf-acc  { font-size: 13px; letter-spacing: .04em; }

/* Penanda "beda dari nama staff".
   TIDAK diwarnai dengan sengaja. Nama rekening yang berbeda dari nama staff
   itu wajar — gaji masuk ke rekening istri, misalnya — jadi warna peringatan
   akan berbohong. Yang dibutuhkan cuma supaya tidak terbaca seperti salah
   ketik. Bukan .badge: di dalam baris kecil ini badge punya padding dan lebar
   minimum yang membuatnya menonjol lebih dari isinya sendiri. */
.sf-tag {
  font-style: normal; font-size: 9px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 1px 5px; margin-left: 4px; white-space: nowrap;
}
.sf-lock { opacity: .75; }

/* Tombolnya TURUN ke bawah gambar, tidak berdesakan di sampingnya.
   Percobaan pertama membiarkannya sebaris (`white-space: nowrap`) dan
   hasilnya kolom yang melebar sampai tabelnya harus digulir untuk melihat
   kolom terakhir — di baris sendiri, tempat tombolnya justru paling sering
   dipakai. Ketahuan dari menggambarnya, bukan dari membaca CSS-nya. */
.sf-doc { min-width: 132px; }

/* Sebelum gambarnya dimuat, ini TAUTAN "Lihat" biasa — bentuknya dipatok
   seukuran gambarnya supaya barisnya tidak melompat waktu pratinjaunya
   datang. Kalau thumbs.js tidak termuat, dia tetap tautan yang bekerja. */
.sf-thumb {
  display: inline-flex; align-items: center; justify-content: center;
  width: 84px; height: 56px;
  border: 1px dashed var(--border); border-radius: var(--r-sm);
  background: var(--surface-2);
  font-size: 11px; color: var(--s1); text-decoration: none;
}
.sf-thumb:hover { text-decoration: none; filter: brightness(1.03); }
/* Sudah jadi gambar: garisnya rapat, bukan putus-putus. */
.sf-has { border-style: solid; padding: 0; overflow: hidden; }
.sf-thumb img {
  width: 84px; height: 56px; object-fit: cover; display: block;
}
/* Pratinjaunya gagal. Tautannya TETAP hidup — servernya sendiri yang akan
   menjelaskan sebabnya waktu dibuka. */
.sf-gagal {
  border-color: color-mix(in srgb, var(--crit) 45%, transparent);
  color: var(--crit);
}
.sf-up { display: inline-block; margin: 6px 5px 0 0; vertical-align: top; }
/* Tombol pilih berkas: <input type=file> disembunyikan di dalam <label>
   supaya bentuknya sama dengan tombol lain di aplikasi ini. Yang disembunyikan
   input-nya, bukan fungsinya — labelnya tetap membukanya. */
.sf-pick { position: relative; overflow: hidden; cursor: pointer; }
.sf-pick input[type=file] {
  position: absolute; inset: 0; opacity: 0; cursor: pointer; font-size: 0;
}
.sf-edit { margin-top: 8px; }
.sf-edit > summary { list-style: none; cursor: pointer; display: inline-block; }
.sf-edit > summary::-webkit-details-marker { display: none; }
.sf-form { margin-top: 8px; display: grid; gap: 7px; max-width: 260px; }
.sf-form label { font-size: 11px; color: var(--muted); display: grid; gap: 3px; }
.sf-form input { width: 100%; }

/* Toast yang membawa pesan "tersimpan" turun ke dekat mata waktu gulirannya
   dikembalikan ke tengah halaman. Bentuknya sama dengan toast yang sudah ada,
   tapi warnanya ikut ARTI: hijau-netral untuk berhasil, merah untuk gagal.
   `.toast` bawaan selalu merah karena selama ini cuma dipakai untuk kegagalan. */
.ks-toast { background: var(--ink); color: #fff; }
.ks-toast.ks-bad { background: var(--crit); }

/* Gambar berkas staff yang gagal dimuat. Ikon patah bawaan peramban tidak
   membedakan "berkasnya hilang" dari "tidak boleh dilihat" dari "servernya
   sibuk" — tiga hal yang jalan keluarnya beda. Ini memberinya bentuk yang
   bisa dibaca, dan tetap bisa diklik untuk melihat pesan aslinya di tab baru. */
.sf-broken {
  width: 84px; height: 56px; object-fit: contain;
  border: 1px dashed color-mix(in srgb, var(--crit) 45%, transparent);
  background: color-mix(in srgb, var(--crit) 6%, transparent);
  font-size: 9.5px; color: var(--crit); padding: 2px;
}

/* Daftar permintaan lambat di Settings.
   TIDAK BERWARNA sama sekali — baris yang tercatat di sini adalah bukti,
   bukan peringatan; yang menilai apakah dia buruk tetap orang yang membacanya.
   Aturan warna proyek ini: warna hanya untuk makna. Yang dijaga di sini cuma
   keterbacaan — lebarnya jangan pernah mendorong halaman jadi bergeser ke
   samping, jadi barisnya digulir di dalam kotaknya sendiri. */
.slowlog {
  margin: 8px 0 0; padding: 10px 12px; max-height: 340px;
  overflow: auto; overflow-x: auto;
  background: var(--surface-sunk); border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; line-height: 1.7; color: var(--ink-2);
  white-space: pre;
}

/* ── Lencana "belum dibaca" di sidebar ───────────────────────────────────
   abby: "jika ada Pengumuman baru mungkin bisa di kasih tanda apa gtu biar
   staff ku ngeh". Tandanya sebenarnya sudah ada — di DALAM bagian Pengumuman
   di dashboard, yang letaknya jauh di bawah layar dan tidak pernah tergulir.
   Di sini dia kelihatan dari halaman mana pun.

   BIRU (--s1), bukan merah. Merah di aplikasi ini berarti "ada yang salah";
   pengumuman dan SOP baru itu KABAR, bukan masalah. Kalau dua-duanya merah,
   yang benar-benar mendesak berhenti menonjol. */
.navcount {
  margin-left: auto; flex: 0 0 auto;
  min-width: 18px; padding: 1px 6px;
  border-radius: 999px;
  background: var(--s1); color: #fff;
  font-size: 10.5px; font-weight: 700; line-height: 16px; text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ── Pengumuman: tombol "sudah saya baca" ───────────────────────────────── */
.ann-readform { margin: 0; }
.ann-done { font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* Siapa yang belum baca, di halaman kelola. Tidak berwarna: pengumuman yang
   belum dibaca semua orang jam sembilan pagi itu keadaan biasa. */
.ann-belum { margin: 6px 0 2px; }
.ann-belum > summary {
  cursor: pointer; font-size: 11.5px; color: var(--ink-2);
  list-style: none; display: inline-block;
  padding: 2px 8px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface-2);
}
.ann-belum > summary::-webkit-details-marker { display: none; }
.ann-belum-list {
  margin-top: 6px; padding: 8px 10px;
  background: var(--surface-sunk); border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: 11.5px; line-height: 1.7; color: var(--ink-2);
}
.ann-belum-all { margin: 6px 0 2px; font-size: 11.5px; color: var(--muted); }

/* ── SOP & Soundingan ────────────────────────────────────────────────────
   Kartu SOP sengaja SAMA BENTUKNYA dengan kartu pengumuman: dua daftar yang
   isinya sejenis tapi digambar berbeda memaksa mata belajar dua kali. Yang
   membedakannya isi dan letaknya di menu, bukan gayanya. */
.sop-list { display: grid; gap: 12px; }
.sop-top  { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.sop-title { margin: 0 0 8px; font-size: 16px; }
.sop-body { font-size: 13px; line-height: 1.75; color: var(--ink); white-space: normal; }
.sop-foot {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border);
  font-size: 11.5px;
}
.sop-done { font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* SOP yang belum dibaca dapat garis kiri — SATU-SATUNYA hal berwarna di daftar
   ini, karena itu satu-satunya yang berarti sesuatu: ada yang belum kamu baca. */
.sop-baru { border-left: 3px solid var(--s1); }
/* TIDAK ADA garis merah untuk yang revisi — sempat ada, lalu dilepas.
   Merah di aplikasi ini berarti "ada yang salah", dan SOP yang belum dibaca
   itu kabar, bukan masalah; alasan yang sama sudah ditulis waktu tanda "Baru"
   pengumuman dibuat biru. Yang membedakan "BARU" dari "REVISI — baca ulang"
   kalimatnya, bukan warnanya. */
.sop-arsip { opacity: .72; }
.sop-lama  { opacity: .85; }

/* "Yang berubah" — ditaruh di atas isinya supaya orang yang sudah pernah
   membaca SOP ini tidak perlu membandingkan dua teks panjang sendiri. */
.sop-note {
  margin: 0 0 10px; padding: 8px 10px;
  background: var(--wash); border: 1px solid var(--border); border-radius: var(--r-sm);
  font-size: 12px; line-height: 1.6; color: var(--ink-2);
}

.sop-photo { display: block; margin-top: 10px; max-width: 420px; }
.sop-photo img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--border); border-radius: var(--r-sm);
}

.sop-new { padding: 12px 14px; }
.sop-new > summary, .sop-rev > summary { cursor: pointer; list-style: none; display: inline-block; }
.sop-new > summary::-webkit-details-marker,
.sop-rev > summary::-webkit-details-marker { display: none; }
.sop-rev { margin-top: 10px; }

.sop-form { display: grid; gap: 10px; margin-top: 12px; max-width: 720px; }
.sop-form label { display: grid; gap: 4px; font-size: 11.5px; color: var(--muted); }
.sop-form input[type="text"], .sop-form textarea { width: 100%; }
.sop-form textarea { font: inherit; line-height: 1.7; resize: vertical; }
.sop-form button { justify-self: start; }

/* Siapa sudah / siapa belum. Dua kolom sederajat: yang "belum" ditaruh DULU
   karena itu yang bisa ditindaklanjuti. */
.who-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 760px) { .who-grid { grid-template-columns: 1fr; } }
.who-list { margin: 0; padding-left: 18px; font-size: 12.5px; line-height: 1.9; }
.who-list .muted { font-size: 11px; margin-left: 6px; }

/* ── Kotak foto (lightbox) ───────────────────────────────────────────────
   abby: "setiap buka foto itu mesti lari ke tab baru ya? apa ga bisa bentu
   view di tab yang aja aja".

   Latarnya gelap pekat di DUA tema — dan itu bukan lupa mengurus tema terang.
   Yang dilihat di sini tangkapan layar bukti dan foto SOP; latar terang di
   belakang gambar terang membuat batas gambarnya hilang, dan yang sedang
   dinilai justru apa yang ada di dalam gambar itu. */
.fotobox {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  background: rgba(9, 12, 20, .88);
  padding: 56px 16px 24px;
}
.fotobox[hidden] { display: none !important; }

.fb-bar {
  position: absolute; top: 0; left: 0; right: 0; height: 48px;
  display: flex; align-items: center; gap: 12px; padding: 0 14px;
  color: #eef2f8; font-size: 12.5px;
}
.fb-judul  { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fb-hitung { flex: 0 0 auto; font-variant-numeric: tabular-nums; opacity: .75; }

.fb-tutup {
  flex: 0 0 auto; width: 32px; height: 32px; padding: 0;
  border: 1px solid rgba(255,255,255,.22); border-radius: 999px;
  background: rgba(255,255,255,.08); color: #eef2f8;
  font-size: 20px; line-height: 1; cursor: pointer;
}
.fb-tutup:hover { background: rgba(255,255,255,.18); }

.fb-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 64px; padding: 0;
  border: 1px solid rgba(255,255,255,.18); border-radius: var(--r-sm);
  background: rgba(255,255,255,.07); color: #eef2f8;
  font-size: 26px; line-height: 1; cursor: pointer;
}
.fb-nav:hover { background: rgba(255,255,255,.16); }
.fb-prev { left: 14px; }
.fb-next { right: 14px; }
/* Satu foto saja: panahnya tidak digambar. Tombol yang tidak bisa melakukan
   apa-apa mengajari orang bahwa tombol di sini boleh diabaikan. */
.fb-solo .fb-nav { display: none; }

.fb-isi { max-width: 100%; max-height: 100%; display: flex;
          align-items: center; justify-content: center; }
.fb-isi img {
  max-width: 100%; max-height: calc(100vh - 90px);
  width: auto; height: auto; display: block;
  border-radius: var(--r-sm); background: #fff;
}
.fb-muat .fb-isi::after {
  content: 'memuat…'; color: #cfd7e4; font-size: 12.5px;
}
.fb-muat .fb-isi img { display: none; }

/* Yang gagal TIDAK jadi ikon patah dan TIDAK ditutup diam-diam: kalimat apa
   adanya plus tautan ke halaman aslinya, karena cuma server yang tahu
   sebabnya. */
.fb-pesan {
  margin: 0; padding: 18px 20px; max-width: 46ch;
  background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.16);
  border-radius: var(--r-sm);
  color: #eef2f8; font-size: 13px; line-height: 1.7; text-align: center;
}
.fb-pesan a { color: #fff; text-decoration: underline; }

@media (max-width: 640px) {
  .fotobox { padding: 52px 8px 12px; }
  .fb-nav { width: 36px; height: 52px; font-size: 22px; }
}

/* ── Ganti nama CS (Master Data -> Websites -> Nama CS) ──────────────────
   Kisi, bukan tabel: yang diisi cuma satu kolom, dan tabel satu kolom
   memaksa mata turun jauh untuk lima belas baris yang sebenarnya muat
   bersebelahan. */
.csn-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 10px; margin-top: 4px;
}
.csn-row {
  display: grid; grid-template-columns: 62px 1fr; align-items: center; gap: 8px;
  padding: 8px 10px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.csn-no {
  font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums;
  display: flex; flex-direction: column; gap: 3px; align-items: flex-start;
}
.csn-row input { width: 100%; min-width: 0; }
/* Tanda "nonaktif": teks kecil biasa, bukan .badge. Badge di kolom sesempit
   ini meluber menimpa kotak isiannya — dan itu terlihat di gambar, bukan di
   berkas CSS-nya. */
.csn-tag {
  font-style: normal; font-size: 9px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); line-height: 1.2;
}
/* CS nonaktif tetap ditampilkan: barisnya memegang riwayat, dan namanya masih
   mengunci nama itu di situs ini. Dipudarkan, bukan disembunyikan — nama yang
   bentrok dengan sesuatu yang tak terlihat mustahil diperbaiki. */
.csn-off { opacity: .62; }
/* Merah HANYA untuk baris yang ditolak. Itu satu-satunya hal di halaman ini
   yang berarti "ada yang salah". */
.csn-bad { border-color: var(--crit); background: color-mix(in srgb, var(--crit) 5%, var(--surface-1)); }
.csn-err {
  grid-column: 1 / -1; font-size: 11px; line-height: 1.5; color: var(--crit);
}
.csn-foot {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border);
  font-size: 11.5px;
}

/* ==================================================================
   LAYAR SEMPIT — AUDITOR dibuka dari HP
   ==================================================================

   Diukur dulu, bukan dikira. Sebelum blok ini:

       1100  doc=1100  ok
       1000  doc=1085  MELEBER
        390  doc=1085  MELEBER   112 elemen keluar layar   sidebar=224

   Halamannya BERHENTI MENGECIL di 1085px. Mau layarnya 1000 atau 360,
   lebarnya tetap 1085 — dan sidebar tetap 224px, yaitu 57% dari layar
   HP 390px. Isinya kebagian 166px sisanya.

   Catatan lama menyebut "di bawah 969px batang atas keluar layar". Itu
   gejalanya, bukan penyakitnya: batang atas yang paling kelihatan karena
   dia paling atas. Yang sebenarnya, tata letak untuk layar sempit memang
   tidak pernah ada.

   DUA AMBANG, DAN BEDANYA DISENGAJA:

   1080px — batang atasnya melipat. Sidebar TETAP. Laptop 1024 dan tablet
            mendatar masih bekerja seperti biasa; yang berubah cuma barisan
            atas yang tadinya berjejer terus ke kanan.

    900px — sidebar jadi laci. Ini perubahan besar, jadi ambangnya dibuat
            jauh dari lebar laptop mana pun. Semua orang di sini bekerja
            dari laptop; HP cuma untuk melihat-lihat, dan perbaikan HP yang
            diam-diam mengubah tampilan laptop merugikan orang yang tidak
            pernah mengeluh.
   ================================================================== */

/* Sakelar lacinya sendiri tidak pernah terlihat, tapi TIDAK memakai
   `display: none`: elemen yang begitu hilang dari pohon aksesibilitas, dan
   `~ .side` tetap bekerja selama dia ada di DOM. */
.side-sw   { position: absolute; width: 0; height: 0; opacity: 0; pointer-events: none; }
.side-veil { display: none; }
.side-btn  { display: none; }

@media (max-width: 1080px) {
  /* Yang membuat halamannya mentok di 1085px: barisan atas ini flex satu
     baris tanpa lipatan, jadi anak-anaknya mendorong lebar dokumen dan
     SELURUH halaman ikut lebar. Satu baris `flex-wrap` menyelesaikan itu. */
  .top { flex-wrap: wrap; row-gap: 8px; padding: 11px 16px; }
  .top .spacer { display: none; }
  /* Tulisan "Audit date" dilepas, kotak tanggalnya tetap: yang dibutuhkan
     tanggalnya, bukan keterangan bahwa itu tanggal. */
  .datepick .lbl { display: none; }
  .wrap { padding: 20px 16px 56px; }
  .pgtabs { padding: 0 16px; }
}

@media (max-width: 900px) {
  .side-btn {
    display: inline-flex; align-items: center; justify-content: center;
    flex: none; width: 36px; height: 36px; cursor: pointer;
    border: 1px solid var(--border); border-radius: 10px;
    background: var(--surface-1); color: var(--ink);
  }
  .side-btn svg { width: 19px; height: 19px; }
  .side-btn:hover { background: var(--surface-2); }

  /* Dikeluarkan dari alur, jadi 224px-nya benar-benar kembali ke isinya —
     bukan cuma digeser keluar layar sambil tetap memesan tempat. */
  .side {
    position: fixed; top: 0; bottom: 0; left: 0; z-index: 60;
    width: 262px; height: 100vh;
    transform: translateX(-100%); transition: transform .22s ease;
  }
  .side-sw:checked ~ .side { transform: none; box-shadow: 0 0 44px rgba(0, 0, 0, .45); }
  .side-sw:checked ~ .side-veil {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: color-mix(in srgb, #060c16 52%, transparent);
  }

  /* Nama dan peran dilepas, fotonya tetap: fotonya masih tautan ke Profil,
     jadi tidak ada yang hilang selain tulisan yang sudah dia wakili. */
  .who-txt { display: none; }
  .top h1 { font-size: 16px; }

  /* Panel lebar tetap 320px menyembul keluar layar 390px begitu dibuka —
     dan isi loncengnya justru yang paling dibuka orang dari HP.

     Menyempitkan lebarnya saja TIDAK cukup, dan itu ketahuan waktu diukur:
     panelnya menempel ke kanan loncengnya (`right: 0`), sedangkan loncengnya
     sendiri sudah dekat tepi kanan — jadi yang menyembul pinggir KIRInya
     (kiri = -141). Di layar sempit dia dilepas dari loncengnya dan dipatok
     ke layar, dengan jarak sama di dua sisi. */

  /* TABEL DI DALAM KARTU MENGGULIR DI DALAM KARTUNYA.
     Disapu dulu, bukan ditebak: dari 33 tabel di seluruh view, cuma 14 yang
     punya pembungkus berpenggulung sendiri (sfwrap, wk-wrap, gridwrap,
     lbwrap, mxwrap, report). Sisa 19 telanjang — dan membungkus satu per satu
     berarti 19 suntingan view yang tiap satunya bisa salah, plus tabel ke-20
     yang ditulis bulan depan tetap telanjang.

     `:has()` dipakai supaya cuma kartu yang BENAR-BENAR berisi tabel yang
     jadi penggulung. `overflow-x: auto` membuat sumbu satunya ikut auto
     (pelajaran yang sudah tertulis di blok Library di atas), jadi kartu yang
     tidak perlu jangan ikut kena.

     KECUALI reports/chat_print.php: dia halaman cetak berdiri sendiri, punya
     CSS sendiri, dan tidak memuat app.css sama sekali. */
  .wrap table { display: block; width: max-content; max-width: 100%; overflow-x: auto; }

  .bell-panel, .hb-menu {
    position: fixed; top: 56px; left: 12px; right: 12px;
    width: auto; max-width: none;
  }
}

@media (max-width: 560px) {
  .top { padding: 10px 12px; }
  .wrap { padding: 16px 12px 48px; }
  .pgtabs { padding: 0 12px; }
  /* Kotak tanggal memenuhi barisnya sendiri, tidak lagi berdesakan dengan
     lonceng dan tombol tema. */
  .datepick { width: 100%; justify-content: space-between; }
}

/* ---------------------------------------------------------- kode QR 2FA */
/* Latarnya PUTIH dipatok, tidak ikut tema gelap. Bukan soal selera: QR yang
   terbalik warnanya (terang di atas gelap) ditolak sebagian besar pemindai,
   dan yang membacanya kamera HP, bukan mata orang. Bingkai putihnya sudah di
   dalam SVG; kotak ini yang memberi jarak dari isi halaman di sekitarnya. */
.qr-kotak {
  display: inline-block; padding: 10px; margin: 4px 0 14px;
  background: #fff; border: 1px solid var(--border); border-radius: 12px;
  line-height: 0;
}
.qr-kotak svg { display: block; width: 264px; height: 264px; }
@media (max-width: 560px) { .qr-kotak svg { width: 216px; height: 216px; } }

.qr-ketik > summary { cursor: pointer; font-size: 12.5px; color: var(--muted); }
.qr-ketik > summary:hover { color: var(--ink-2); }
.qr-rahasia {
  font-family: 'JetBrains Mono', monospace; font-size: 15px; letter-spacing: .08em;
  padding: 12px; margin: 8px 0 4px; background: var(--surface-sunk);
  border-radius: 8px; user-select: all; overflow-x: auto;
}

/* ------------------------------------------- saringan jenis di Access log */
/* abby: "bedain mana bank sama admin & lc jadi ga gabung jadi bingung".
   CSS murni, tanpa JavaScript: kalau JS gagal dimuat, yang terjadi cuma semua
   baris terlihat — persis keadaan sebelumnya, bukan halaman yang rusak. */
.log-filter { display: flex; gap: 6px; flex-wrap: wrap; margin: 2px 0 14px; }
.log-filter input { position: absolute; opacity: 0; pointer-events: none; }
.log-filter label {
  padding: 5px 12px; border-radius: 99px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-1);
  font-size: 12px; font-weight: 600; color: var(--muted);
}
.log-filter label:hover { background: var(--surface-2); }
.log-filter input:checked + label {
  background: var(--s1); border-color: var(--s1); color: #fff;
}
/* Terlihat waktu dipilih pakai papan tik — tombol yang cuma bisa dipakai
   dengan tetikus menutup pintu untuk orang yang tidak memakai tetikus. */
.log-filter input:focus-visible + label { outline: 2px solid var(--s1); outline-offset: 2px; }

.log-filter:has(#lfBank:checked)  ~ .log-tabel tr[data-jenis]:not([data-jenis="bank"]),
.log-filter:has(#lfAdmin:checked) ~ .log-tabel tr[data-jenis]:not([data-jenis="admin"]),
.log-filter:has(#lfLc:checked)    ~ .log-tabel tr[data-jenis]:not([data-jenis="lc"]) {
  display: none;
}

/* ---------------------------------------- baris ringkas, detail waktu ditekan */
/* Namanya acc-*, BUKAN lg-*. Percobaan pertama memakai .lg-row — dan itu
   sudah dipakai daftar Activity Log sejak lama, dengan `display: flex`.
   Akibatnya barisnya berhenti jadi baris tabel: kolomnya lepas dari kepalanya
   dan seluruh tabel berantakan. Tidak ada error, cuma tata letak yang salah,
   dan tesnya tetap hijau karena yang diuji perilakunya. Ketahuan dari melihat
   gambarnya. */
/* abby: "ga bisa compact aja ya? jadi pas ditekan baru detailnya".
   Kotak centang tersembunyi, sama seperti laci menu — tanpa JavaScript.
   Tanpa JS semua detail terlihat sekaligus: itu keadaan sebelum ronde ini,
   bukan halaman yang rusak. */
.acc-sw { position: absolute; opacity: 0; pointer-events: none; }
/* Jamnya tidak boleh patah jadi dua baris: "04 Sep · 09:41" dibaca sekali
   lihat, dan yang terpotong justru bagian yang paling dipindai mata waktu
   menyapu daftar. */
.acc-row > td:first-child { white-space: nowrap; }
.acc-more {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; margin-left: 6px; vertical-align: middle;
  border-radius: 6px; cursor: pointer; color: var(--muted);
  font-size: 11px; line-height: 1; user-select: none;
}
.acc-more:hover { background: var(--surface-2); color: var(--ink-2); }
.acc-sw:focus-visible + .acc-more { outline: 2px solid var(--s1); outline-offset: 1px; }
/* Panahnya berputar supaya keadaan terbuka/tertutup terbaca tanpa harus
   melihat ada-tidaknya baris di bawahnya. */
.acc-sw:checked + .acc-more { transform: rotate(180deg); color: var(--ink-2); }

.acc-detail > td { padding: 0; border-top: 0; }
.acc-inner {
  display: none; flex-direction: column; gap: 10px;
  padding: 10px 12px 14px; font-size: 12.5px; color: var(--muted);
}
.acc-inner b { color: var(--ink-2); font-weight: 600; margin-right: 5px; }

/* Urutan yang benar-benar terjadi di dalam satu sesi: acquire, copy_email,
   copy_password, release — masing-masing dengan jamnya. Inilah yang dicari
   orang waktu bertanya "dia ngapain saja waktu pegang slot itu". */
.acc-urut { list-style: none; margin: 0; padding: 0; display: flex;
            flex-direction: column; gap: 5px; }
.acc-urut li { display: flex; align-items: center; gap: 10px; }
.acc-urut li .mono:first-child { min-width: 62px; }
.acc-meta { display: flex; gap: 22px; flex-wrap: wrap; }
.acc-cred { color: var(--muted); margin-left: 7px; }
.acc-jml  { font-size: 11.5px; margin-left: 6px; }
/* Ringkasannya harus muat satu baris: "COPY_PASSWORD 4 aksi ▾" yang patah
   dua baris membuat tiap barisnya jadi setinggi dua baris, dan seluruh
   gunanya diringkas hilang. */
.acc-row > td:last-child { white-space: nowrap; }
.acc-row:has(.acc-sw:checked) + .acc-detail .acc-inner { display: flex; }

/* Baris detail yang tertutup tidak boleh menyisakan garis kosong. */
.acc-row:not(:has(.acc-sw:checked)) + .acc-detail { border: 0; }

/* ------------------------------- buka kembali situs di Buku Dosa */
/* Kelas sendiri, bukan .rowmore: yang itu tombol titik-tiga di dalam baris
   tabel dengan summary yang dipatok 26x24 piksel — kalimat sepanjang ini
   terperas jadi empat baris yang saling menimpa. */
.vreopen { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 14px; }
.vreopen > summary {
  cursor: pointer; user-select: none; list-style: none;
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 600; color: var(--muted);
  padding: 6px 12px; border-radius: 99px;
  border: 1px solid var(--border); background: var(--surface-1);
}
.vreopen > summary::-webkit-details-marker { display: none; }
/* Panahnya ditulis sebagai KARAKTER, bukan kode escape CSS. Percobaan
   pertama memakai '\21BA' dan yang tergambar malah huruf "BA" — dua digit
   pertamanya termakan sebagai kode, sisanya jadi tulisan biasa. */
.vreopen > summary::before { content: '↺'; font-size: 14px; }
.vreopen > summary:hover { color: var(--ink-2); background: var(--surface-2); }
.vreopen[open] > summary { color: var(--ink-2); background: var(--surface-2); }
/* Isinya diberi jarak dari tombolnya supaya kalimat peringatannya terbaca
   sebagai penjelasan, bukan sebagai bagian dari tombol. */
.vreopen > form { margin-top: 12px; max-width: 520px; }

/* ---------------------------------- ganti link channel dari grid */
/* Pensilnya samar sampai kepala kolomnya disentuh: dia jarang dipakai, dan
   ikon yang selalu terang di 51 kolom sekaligus membuat barisnya ramai
   padahal yang dikerjakan orang bukan itu. Di layar sentuh tidak ada hover,
   jadi di sana dia selalu terlihat — kalau tidak, satu-satunya jalan masuk
   dari HP jadi tidak kelihatan. */
/* SELALU TERLIHAT. Semula pensilnya samar sampai kepala kolomnya disentuh
   kursor, karena waktu itu klik kanan jadi jalan utamanya. Klik kanan sudah
   dibuang, jadi pensil ini SATU-SATUNYA jalan masuk — dan satu-satunya jalan
   masuk tidak boleh disembunyikan. */
.wg-edit {
  border: 0; background: none; cursor: pointer; padding: 2px 4px;
  color: var(--muted); font-size: 12px; line-height: 1;
  opacity: .55; transition: opacity .12s, color .12s;
}
.wg-head:hover .wg-edit, .wg-edit:focus-visible { opacity: 1; }
.wg-edit:hover { color: var(--ink-2); opacity: 1; }

.wg-menu {
  position: fixed; z-index: 80; width: 288px; padding: 12px;
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
  display: flex; flex-direction: column; gap: 8px;
}
.wg-menu-head { font-weight: 700; font-size: 12.5px; color: var(--ink); }
.wg-menu-item {
  font-size: 12.5px; color: var(--s1); text-decoration: none;
  padding: 5px 0; border-bottom: 1px solid var(--border);
}
.wg-menu-item:hover { text-decoration: underline; }
.wg-menu-lbl { font-size: 11px; font-weight: 600; color: var(--muted); }
.wg-menu-in {
  width: 100%; font: inherit; font-size: 12.5px; padding: 7px 9px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--ink);
}
.wg-menu-err { font-size: 11.5px; color: var(--crit); }
.wg-menu-err:empty { display: none; }
.wg-menu-act { display: flex; gap: 6px; justify-content: flex-end; }

/* ==========================================================================
   Cek Kesalahan CS  (Jobdesk → Cek Kesalahan)

   Awalan .ck- dipakai untuk SEMUA kelas di sini, dan itu bukan kerapian
   belaka. Dua kali di proyek ini kelas baru diberi nama yang sudah punya
   pemilik — .lg-row (Access log) dan .rowmore (tombol titik-tiga) — dan
   dua-duanya merusak halaman lain TANPA satu pun tes berubah merah.
   Salah nama kelas CSS tidak jadi error; dia cuma tidak cocok, lalu diam.
   ========================================================================== */

.ck-card { padding-bottom: 14px; }

.ck-form { display: flex; gap: 10px; align-items: stretch; margin: 0; }
.ck-inputwrap { position: relative; flex: 1; display: flex; align-items: center; min-width: 0; }
.ck-ic {
  position: absolute; left: 13px; width: 17px; height: 17px;
  color: var(--muted); pointer-events: none; display: flex;
}
.ck-ic svg { width: 17px; height: 17px; }
/* `input.ck-input`, BUKAN `.ck-input`.

   Aturan bawaan di atas menyebut `input[type=text]` — kekhususannya 0,1,1,
   sementara satu kelas cuma 0,1,0. Ditulis `.ck-input` saja, ukuran huruf dan
   jarak dalamnya TIDAK PERNAH BERLAKU sama sekali, di lebar layar mana pun.
   Tidak ada error, tidak ada yang kosong — kotaknya cuma tetap seperti kotak
   isian biasa. Ketahuan dari tes peramban yang MENGUKUR hasilnya, bukan dari
   membaca CSS-nya.

   16px, bukan 14.5px: di Safari iOS kotak isian di bawah 16px membuat
   peramban memperbesar halamannya sendiri begitu kotaknya disentuh — dan
   sesudah itu halamannya tertinggal dalam keadaan melebar. Halaman ini justru
   yang paling mungkin dibuka dari HP sambil memegang chat. */
input.ck-input {
  width: 100%; padding: 13px 38px;
  font-size: 16px;
  /* Monospace dengan sengaja: yang diketik di sini kode, bukan kalimat.
     Perbedaan antara O dan 0, l dan 1, harus terlihat waktu orang
     membandingkan isinya dengan layar livechat di sebelahnya. */
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
.ck-clear {
  position: absolute; right: 10px; width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: 50%; background: var(--surface-2); color: var(--ink-2);
  font-size: 15px; line-height: 1; text-decoration: none;
}
.ck-clear:hover { background: var(--axis); text-decoration: none; }

.ck-foot { display: flex; align-items: center; gap: 12px; margin-top: 10px; flex-wrap: wrap; }
.ck-hint { font-size: 11.5px; color: var(--muted); }

.ck-sync { font-size: 11.5px; color: var(--muted); display: inline-flex; align-items: center; gap: 6px; }
.ck-sync i { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex: none; }
.ck-sync-ok i  { background: var(--good); }
.ck-sync-bad   { color: var(--crit); }
.ck-sync-bad i { background: var(--crit); }
.ck-syncerr { margin-top: 6px; font-size: 11.5px; color: var(--ink-2); }

.ck-verdict {
  display: flex; gap: 14px; align-items: flex-start;
  padding: 16px 18px; border: 1px solid var(--border);
  border-radius: 12px; margin-bottom: 14px;
}
.ck-verdict h3 { margin: 0; font-size: 15.5px; font-weight: 700; }
.ck-verdict p  { margin: 4px 0 0; font-size: 12.5px; color: var(--ink-2); }
.ck-bad {
  border-color: color-mix(in srgb, var(--crit) 34%, transparent);
  background: color-mix(in srgb, var(--crit) 6%, transparent);
}
.ck-neutral { background: var(--surface-1); }
.ck-vic {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  display: grid; place-items: center;
  font-weight: 800; font-size: 17px; background: var(--crit); color: #fff;
}
.ck-vic-mut { background: var(--surface-2); color: var(--ink); font-size: 15px; }

/* Lencana "banding diterima" — abby, 15 Oktober 2026, di samping Bukti.

   KECIL dan TENANG, bukan hijau besar. Dia keterangan tambahan, bukan vonis:
   barisnya TETAP kesalahan yang dihitung (pilihan abby), dan lencana hijau
   besar akan terbaca sebagai "beres, tidak usah dihitung" — kebalikan dari
   yang sebenarnya.

   Warnanya biru-abu dari --side-accent, satu-satunya warna di AUDITOR yang
   tidak dipakai untuk menilai apa pun. Hijau, kuning dan merah semuanya
   sudah punya arti di halaman ini. */
.ck-banding {
  display: inline-block; margin-left: 6px;
  padding: 1px 6px; border-radius: 6px;
  /* 9,5px — SEDIKIT lebih kecil daripada lencana keterangan (10px), dan
     bedanya memang disengaja. Sama besar, dia bersaing dengan vonis
     kesalahannya; lebih besar, dia terbaca sebagai vonis yang menggantikan.
     Ada tesnya, supaya tidak "dirapikan" jadi sama suatu hari. */
  font-size: 9.5px; font-weight: 700; letter-spacing: .02em;
  white-space: nowrap; vertical-align: middle;
  color: color-mix(in srgb, var(--side-accent) 72%, var(--ink));
  background: color-mix(in srgb, var(--side-accent) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--side-accent) 34%, transparent);
}

/* ---------- MIRIP, BUKAN COCOK (abby, 15 Oktober 2026) ----------

   Warnanya KUNING, bukan hijau dan bukan merah, dan itu bukan soal rasa:
   hijau terbaca "ketemu" dan merah terbaca "tidak ada" — dua jawaban yang
   dua-duanya salah di sini. Yang benar "hampir, tapi periksa dulu", dan
   satu-satunya warna yang sudah dipakai untuk itu di seluruh AUDITOR
   adalah kuning peringatan. */
.ck-warn {
  border-color: color-mix(in srgb, var(--warn) 42%, transparent);
  background: color-mix(in srgb, var(--warn) 8%, transparent);
}
.ck-vic-warn { background: var(--warn); color: #11161f; }

.ck-mirip-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding-bottom: 10px; margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
  font-size: 12px;
}
.ck-mirip-q { font-size: 15px; letter-spacing: .04em; }

.ck-mirip {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 0; border-bottom: 1px dashed var(--border);
}
.ck-mirip:last-of-type { border-bottom: 0; }
.ck-mirip-kode { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
                 font-size: 12px; min-width: 0; }
.ck-mirip-asli { font-size: 15px; letter-spacing: .04em; word-break: break-all; }
.ck-mirip-nb   { font-size: 11px; }
.ck-mirip-tip  { font-size: 11.5px; margin: 12px 0 0; }

/* Huruf yang BERBEDA ditandai — dan ditandai dengan DUA hal sekaligus,
   warna DAN garis bawah tebal. Warna sendirian tidak cukup: yang tidak
   bisa membedakan merah dari teks biasa justru harus membandingkan dua
   baris huruf dengan mata, yaitu pekerjaan yang baru saja gagal dia
   kerjakan dan yang membuat dia ada di halaman ini. */
.ck-beda {
  color: var(--crit);
  background: color-mix(in srgb, var(--crit) 14%, transparent);
  border-bottom: 2px solid var(--crit);
  padding: 0 1px; border-radius: 3px;
}

/* ---------- hasil: BARIS TABEL, bukan kartu ----------

   Bentuk kartu dibuang atas permintaan abby: 28 hasil pencarian nama jadi
   lima layar penuh, dan yang orang lakukan dengan daftar itu justru
   MEMBANDINGKAN baris — tanggal, situs, keterangan. Kolom sejajar
   mengerjakan itu; kartu memaksa menggulir bolak-balik.

   Nyaris tidak ada aturan di sini: table/th/td di app.css sudah menangani
   garis, tinggi baris, dan sorotan baris. Yang ditulis cuma lebar kolom dan
   apa yang boleh memotong. Tabel yang memakai gaya bawaan otomatis sama
   dengan tabel lain di AUDITOR — dan tidak bisa bentrok dengan siapa pun. */
.ck-tblcard { padding: 0; overflow: hidden; }

/* Bayangan tepi yang MENGHILANG SENDIRI kalau sudah mentok.

   Di layar HP tabelnya digeser mendatar. Tanpa tanda apa pun, kolom yang di
   kanan sama saja dengan tidak ada — orang tidak menggeser sesuatu yang tidak
   dia tahu bisa digeser, dan yang terlihat cuma lencana keterangan yang
   terpotong di tepi layar.

   Caranya lapisan `local` (menempel pada isi, ikut bergeser) menutupi lapisan
   `scroll` (menempel pada kotaknya) tepat waktu gulirannya mentok. CSS murni,
   tanpa satu baris JS — halaman ini memang tidak punya JS sama sekali, dan
   petunjuk gulir bukan alasan yang cukup untuk mulai punya. */
.ck-tblwrap {
  overflow-x: auto;
  background:
    linear-gradient(to right, var(--surface-1) 40%, transparent) left  / 28px 100% no-repeat local,
    linear-gradient(to left,  var(--surface-1) 40%, transparent) right / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%,   rgba(0,0,0,.14), transparent) left  / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.14), transparent) right / 12px 100% no-repeat scroll;
}
.ck-tbl { min-width: 880px; }
.ck-tbl th { white-space: nowrap; background: var(--surface-1); }
.ck-tbl td { vertical-align: middle; padding: 7px 12px; }
.ck-tbl tbody tr:last-child td { border-bottom: 0; }

/* Baris yang cocok persis dengan yang ditempel. Ditandai dengan GARIS di tepi
   kiri, bukan dengan latar berwarna: latar berwarna bertabrakan dengan
   sorotan hover bawaan tabel, dan yang kalah justru penandanya. */
.ck-tbl tr.ck-match td { background: color-mix(in srgb, var(--s1) 7%, transparent); }
.ck-tbl tr.ck-match td:first-child { box-shadow: inset 3px 0 0 var(--s1); }

.ck-c-tgl   { white-space: nowrap; width: 1%; font-variant-numeric: tabular-nums; }
.ck-c-situs { font-weight: 650; white-space: nowrap; width: 1%; }
.ck-c-ket   { width: 1%; }
.ck-c-by    { white-space: nowrap; width: 1%; }
.ck-c-chat  { white-space: nowrap; width: 1%; font-size: 12px; }
.ck-c-link  { white-space: nowrap; width: 1%; }
/* STAFF satu-satunya yang boleh melar; nama panjang dipotong dengan titik
   tiga, lengkapnya tetap terbaca dari tooltip judul kolomnya sendiri. */
.ck-c-staff { max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Lencana keterangan: teksnya panjang-panjang ("NOTE PENGECEKKAN TIDAK
   BERUJUNG"), jadi di dalam tabel dia tidak boleh membungkus. */
.ck-tbl .badge { white-space: nowrap; font-size: 10px; }

/* pemilih orang waktu satu ketikan cocok ke lebih dari satu nama */
.ck-people { display: flex; flex-direction: column; gap: 6px; }
.ck-person {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 11px 13px; border: 1px solid var(--border); border-radius: 10px;
  color: var(--ink); text-decoration: none; background: var(--surface-1);
}
.ck-person:hover { border-color: var(--axis); background: var(--surface-2); text-decoration: none; }
.ck-person-n { font-weight: 650; font-size: 13.5px; }
.ck-person-c { font-size: 12px; color: var(--ink-2); }
.ck-person-d { font-size: 11px; color: var(--muted); }

@media (max-width: 780px) {
  .ck-hit-grid { grid-template-columns: 1fr 1fr; }
  .ck-form { flex-wrap: wrap; }
  .ck-form .btn { width: 100%; }
}

/* ==========================================================================
   Buku Dosa — kasus yang TERLEWAT shift input

   Awalan .md- (missed data). Diperiksa v/dosa.js: tidak boleh ada satu pun
   nama di sini yang sudah punya pemilik di atasnya. Dua kali di proyek ini
   kelas baru menumpang nama orang lain (.lg-row, .rowmore) dan merusak tata
   letak tanpa satu pun tes berubah merah.
   ========================================================================== */

/* <details>, bukan tombol JS — halaman yang butuh JS kehilangan tombolnya
   diam-diam kalau berkasnya gagal termuat, dan ini satu-satunya jalan
   mencatat temuan sebelum situsnya ditutup. Panah bawaan dibuang; yang
   tersisa terlihat dan berperilaku seperti tombol biasa. */
.md-add { margin-top: 14px; }
.md-add > summary { display: inline-flex; list-style: none; cursor: pointer; }
.md-add > summary::-webkit-details-marker { display: none; }
.md-add[open] > summary { opacity: .55; }

.md-form {
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 7%, transparent);
  border-radius: 12px; padding: 15px 17px; margin-top: 12px;
}
.md-form-head { font-size: 14px; margin-bottom: 13px; }
.md-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; }
.md-grid label { display: block; }
.md-grid .md-wide { grid-column: 1 / -1; }
.md-grid input, .md-grid select { width: 100%; }
.md-k {
  display: block; font-size: 9.5px; font-weight: 750; letter-spacing: .09em;
  color: var(--muted); margin-bottom: 4px;
}
.md-warn {
  margin-top: 13px; font-size: 12px; line-height: 1.65; color: var(--ink-2);
  border-left: 3px solid var(--warn); padding-left: 11px;
}
.md-form-act { display: flex; gap: 8px; margin-top: 14px; }

/* Lencana di barisnya sendiri. Ringkasan di kaki tabel tidak ikut terbaca
   waktu orang cuma melihat satu baris yang dia cari. */
.md-badge { font-size: 9.5px; letter-spacing: .06em; }
.md-note {
  margin-top: 12px; font-size: 12.5px; color: var(--ink-2);
  border-left: 3px solid var(--warn); padding: 2px 0 2px 11px;
}

.md-alert { margin-bottom: 16px; }
.md-who { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.md-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px; border-radius: 20px; font-size: 12px;
  border: 1px solid var(--border); background: var(--surface-1); color: var(--ink);
}
.md-chip b { font-variant-numeric: tabular-nums; }
.md-sub { margin-top: 10px; font-size: 11.5px; color: var(--muted); max-width: 720px; }

@media (max-width: 780px) { .md-grid { grid-template-columns: 1fr; } }

/* ═══════════════════════════════════════════════════════════════════════
   KESEHATAN SISTEM  (.hs-)

   Satu aturan warna yang menentukan apakah halaman ini berguna atau justru
   berbahaya: LAMPU "TIDAK TAHU" TIDAK BOLEH SEWARNA DENGAN LAMPU "SEHAT".
   Baris yang tidak bisa diperiksa lalu terlihat hijau adalah satu-satunya
   cara halaman ini bisa berbohong tanpa satu pun angka yang salah — jadi
   dia abu-abu pudar, dan itu diuji dengan MENGUKUR warnanya, bukan membaca
   berkas ini.
   ═══════════════════════════════════════════════════════════════════════ */

.hs-verdict { display: flex; gap: 18px; align-items: flex-start; padding: 20px 22px; }
.hs-verdict h3  { margin: 0 0 4px; font-size: 17px; }
.hs-verdict .sub { margin: 0; max-width: 780px; line-height: 1.7; }
.hs-verdict.bad  { border-left: 4px solid var(--crit); }
.hs-verdict.warn { border-left: 4px solid var(--warn); }
.hs-verdict.good { border-left: 4px solid var(--good); }
/* Varian keempat, 19 Sep 2026: TIDAK BISA DIPERIKSA. Wajib beda dari .good —
   nol yang tidak diketahui tidak boleh terbaca seperti nol yang diketahui. */
.hs-verdict.unknown { border-left: 4px solid var(--muted); }

.hs-vnum {
  flex: 0 0 auto; width: 46px; height: 46px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  font: 700 22px/1 system-ui, sans-serif; font-variant-numeric: tabular-nums;
}
.hs-verdict.bad  .hs-vnum { background: color-mix(in srgb, var(--crit) 14%, transparent); color: var(--crit); }
.hs-verdict.warn .hs-vnum { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.hs-verdict.good .hs-vnum { background: color-mix(in srgb, var(--good) 16%, transparent); color: var(--good); }
.hs-verdict.unknown .hs-vnum { background: color-mix(in srgb, var(--muted) 16%, transparent); color: var(--muted); }

.hs-row  { display: flex; gap: 14px; padding: 16px 4px; }
.hs-lamp { flex: 0 0 auto; width: 10px; height: 10px; border-radius: 50%; margin-top: 6px; }
.hs-row.good    .hs-lamp { background: var(--good); }
.hs-row.warn    .hs-lamp { background: var(--warn); }
.hs-row.bad     .hs-lamp { background: var(--crit); }
.hs-row.unknown .hs-lamp { background: var(--muted); opacity: .55; }

.hs-main  { min-width: 0; flex: 1; }
.hs-top   { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.hs-top b { font-size: 14.5px; }
.hs-sched { font-size: 12px; color: var(--muted); margin-top: 3px; }

/* Dua tanggal, dua baris — "terakhir berhasil" dan "terakhir dicoba" tidak
   pernah ditumpuk. Kalau ditumpuk, cadangan yang gagal tiap malam terlihat
   seperti cadangan yang segar. */
.hs-when {
  display: flex; flex-direction: column; gap: 3px;
  margin-top: 7px; font-size: 13px; line-height: 1.6;
}
.hs-fail  { color: var(--crit); }
.hs-never { color: var(--crit); font-weight: 600; }

.hs-detail { margin: 7px 0 0; font-size: 12.8px; line-height: 1.72; color: var(--ink-2); max-width: 800px; }
.hs-detail code { font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; }

.hs-err {
  margin-top: 10px; border-left: 3px solid var(--crit);
  padding: 7px 0 7px 11px; font-size: 12.5px; line-height: 1.7;
}
.hs-err code { font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--crit); word-break: break-word; }

.hs-fix     { margin-top: 11px; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.hs-fixhead { flex: 0 0 100%; font-size: 11.5px; color: var(--muted); }
.hs-fixnota { flex: 0 0 100%; font-size: 11.5px; color: var(--muted); }
.hs-cron {
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 11px; overflow-x: auto; white-space: nowrap; max-width: 100%;
}

@media (max-width: 640px) {
  .hs-verdict { gap: 13px; padding: 16px; }
  .hs-cron    { font-size: 11px; }
}

/* Siapa yang menjalankan pekerjaan ini terakhir kali — cuma muncul kalau
   BUKAN cron. Kalimat kecil ini yang mencegah baris hijau dibaca sebagai
   "cron saya jalan" padahal yang jalan tombol manual atau denyut. */
.hs-oleh {
  font-size: 11.5px; color: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border-radius: 5px; padding: 1px 7px; white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════
   CUTI — paspor & visa di kotak keputusan, dan ceklis "sudah naik ADM"

   Satu aturan warna yang menentukan apakah kotak ini berguna atau justru
   berbahaya: "BELUM DIISI" TIDAK BOLEH SEWARNA DENGAN "MASIH LAMA".
   Kotak ini dibaca tepat sebelum tombol Approve ditekan — paspor yang
   tanggalnya belum pernah diisi lalu terlihat hijau berarti layar ini
   menjamin sesuatu yang tidak dia ketahui, dan orang menyetujui cuti
   berdasarkan jaminan itu. Diuji dengan MENGUKUR warnanya, bukan membaca
   berkas ini.
   ═══════════════════════════════════════════════════════════════════════ */

.dok-hr { border-top: 1px dashed var(--border); margin: 9px 0 7px; }

.dok-no {
  font: 11px ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 4px; padding: 0 5px; color: var(--ink-2);
}
.dok-sisa { font-size: 11px; color: var(--muted); font-weight: 400; }

.dok-mati   b { color: var(--crit); }
.dok-dekat  b { color: var(--warn); }
.dok-kosong b { color: var(--muted); }

/* "Belum diisi" TIDAK hijau dan TIDAK merah: dia bukan aman, dan juga bukan
   kerusakan — dia ketiadaan kabar. Pelajaran yang sama dengan lampu abu-abu
   di Kesehatan Sistem, di layar yang berbeda. */
.dok-belum {
  font-style: italic; font-weight: 600; color: var(--muted);
  border-bottom: 1px dotted var(--muted);
}
.dok-mati .dok-sisa  { color: var(--crit); }
.dok-dekat .dok-sisa { color: var(--warn); }

/* Warna memberi tahu ADA sesuatu; kalimat memberi tahu APA. Yang membaca
   kotak ini sedang memutuskan, bukan memindai. */
.dok-kena {
  margin: 5px 0 2px; font-size: 11.5px; line-height: 1.55;
  color: var(--crit); font-weight: 600;
}
.dok-isi { margin: 7px 0 0; font-size: 11.5px; }
/* --brand tidak pernah ada di berkas ini; tautannya jadi mewarisi warna
   teks biasa dan tidak terlihat sebagai tautan. Diganti --s1, warna yang
   memang dipakai tombol utama. (Ditemukan 8 September 2026 lewat
   pemeriksaan variabel tak terdefinisi.) */
.dok-isi a { color: var(--s1); }

/* ---- ceklis "sudah naik ADM" ---- */
.lvadm { display: inline-flex; align-items: center; gap: 8px; margin: 0; flex-wrap: wrap; }
.adm-who { font-size: 11px; color: var(--muted); }
.adm-tandai { border-style: dashed; }
.adm-belumsql { font-size: 11.5px; }
.adm-belumsql code {
  font: 11px ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--surface-2); border-radius: 4px; padding: 1px 5px;
}

/* ---- blok paspor & visa di halaman ADM report ---- */
.rp-dokwrap { display: grid; gap: 8px; }
.rp-dok {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: 9px;
  background: var(--surface-1); font-size: 13px;
}
.rp-dok > span:first-child { min-width: 118px; color: var(--muted); font-size: 12px; }
.rp-dok.dok-mati  { border-color: color-mix(in srgb, var(--crit) 45%, transparent); }
.rp-dok.dok-dekat { border-color: color-mix(in srgb, var(--warn) 45%, transparent); }

.rp-adm { padding: 14px 18px; }
.rp-adm.sudah { border-left: 3px solid var(--good); }
.rp-admform { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0; }

@media (max-width: 640px) {
  .rp-dok > span:first-child { min-width: 0; flex: 0 0 100%; }
  .lvadm { width: 100%; }
}

/* ── blok paspor/visa di kotak keputusan, BERTUMPUK ──────────────────────
   Bentuk pertama memakai .lvstat (label kiri, nilai kanan). Di kotak
   selebar ini "30 Apr 2034 · 2792 hari lagi" tidak muat satu baris: dia
   membungkus jadi dua baris yang tidak rata dengan apa pun, dan angkanya
   terpisah dari tanggalnya. Bertumpuk selalu muat, berapa pun panjangnya. */
.dokblok { padding: 7px 0 2px; }
.dokblok + .dokblok { border-top: 1px dashed var(--border); }
.dokblok-atas {
  display: flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--muted); margin-bottom: 2px;
}
.dokblok-nilai {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 13px; line-height: 1.5;
}
.dokblok-nilai b { font-variant-numeric: tabular-nums; }

/* ── dua kolom: visa kiri, paspor kanan ─────────────────────────────────
   Ambang visa 60 hari dan ambang paspor 365 hari tidak bisa dibandingkan,
   jadi barisnya tidak boleh berdampingan seolah bisa: "18 Sep" di kolom
   visa berarti hampir mepet, "18 Sep" di kolom paspor berarti masih sangat
   longgar. Satu daftar campur membuat keduanya terbaca sama mendesaknya. */
.expcols { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.expcol  { min-width: 0; }
.expcol-head {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  padding-bottom: 7px; margin-bottom: 4px; border-bottom: 1px solid var(--border);
}
.expcol-head b   { font-size: 13.5px; }
.expcol-win      { font-size: 11px; color: var(--muted); }
.expcol-kosong   { margin: 9px 0 2px; font-size: 12.5px; color: var(--muted); }
.expcol .exprow  { gap: 8px; }
.exp-tgl  { font-size: 12.5px; color: var(--ink-2); white-space: nowrap; }
.exp-sisa { font-size: 11.5px; color: var(--muted); white-space: nowrap; min-width: 62px; text-align: right; }

/* 1200, bukan 900, sejak tombol "tandai" ikut masuk baris (13 Sep 2026):
   dua kolom berdampingan baru muat kalau layarnya benar-benar lebar. */
@media (max-width: 1200px) { .expcols { grid-template-columns: 1fr; gap: 14px; } }

/* ═══════════════════════════════════════════════════════════════════════
   BONUS CASHBACK KEKALAHAN PNG  (.bn-)

   Satu aturan warna yang menentukan apakah halaman ini berguna atau justru
   berbahaya: "REPORTAN BELUM DIISI" TIDAK BOLEH SEWARNA DENGAN "KLOP".
   Kotak reportan yang lupa diisi lalu terlihat hijau berarti auditor
   menyetujui pembagian miliaran rupiah yang tidak pernah dibandingkan
   dengan apa pun — dan tidak ada satu angka pun di layar yang salah.
   Karena itu KLOP hijau, TIDAK KLOP kuning, dan BELUM DIISI abu-abu pudar;
   ketiganya diukur warnanya oleh tes, bukan dibaca dari berkas ini.

   (Pelajaran .lg-row dan .rowmore, dua kali: awalan .bn- diperiksa tidak
   menumpang nama yang sudah punya pemilik.)
   ═══════════════════════════════════════════════════════════════════════ */

.bn-err {
  border-left: 4px solid var(--crit); padding: 14px 18px;
  font-size: 13.5px; line-height: 1.7;
}

/* ---------------------------------------------------- Bonus IDN (.bni-)
   Awalannya SENDIRI, bukan menumpang .bn-. Halaman IDN memakai sebagian
   besar kelas .bn- apa adanya — bentuknya memang sama — dan yang di sini
   cuma yang TIDAK ada di PNG: tab jadwal, penanda syarat turnover, dan
   penanda golongan situs.

   Kenapa tidak menambah varian ke .bn- saja: dua halaman yang berbagi satu
   nama kelas untuk hal yang berbeda sudah dua kali merusak tata letak proyek
   ini tanpa satu tes pun merah (.lg-row, .rowmore, .bn-jaman).
   v/css_clash_test.php yang menangkap ketiganya. */
.bni-jadwal { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
              padding: 12px 16px; }
.bni-jlabel { font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
              color: var(--muted); font-weight: 700; margin-right: 2px; }
/* --s1 (biru tombol utama), --border, --surface-2: nama variabel yang
   BENAR-BENAR ada di berkas ini. Percobaan pertama memakai --acc, --line,
   dan --bg-2 — ketiganya tidak ada, dan CSS yang memakai variabel tak
   terdefinisi TIDAK error: properti itu cuma diabaikan. Akibatnya tab yang
   sedang aktif jadi putih di atas putih, tidak terbaca sama sekali, dan
   tidak satu pun tes berubah merah. Ketahuan dari tangkapan layar. */
.bni-jtab   { padding: 6px 14px; border-radius: 8px; font-size: 13px; font-weight: 650;
              text-decoration: none; color: var(--ink-2);
              border: 1px solid var(--border); background: var(--surface-2); }
.bni-jtab:hover { border-color: var(--s1); color: var(--s1); }
.bni-jtab.on    { background: var(--s1); border-color: var(--s1); color: #fff; }
.bni-jnote  { flex: 1 1 100%; font-size: 12px; color: var(--muted); margin-top: 2px; }

/* Syarat turnover, ditulis DI KOTAK JENISNYA. Tiga jenis punya, tiga tidak —
   dan kalau suatu hari syaratnya salah dipasang ke Arcade, kalimat inilah
   satu-satunya tempat kesalahan itu bisa ketahuan sebelum angkanya
   terlanjur dibagikan. Yang "perlu" diberi warna supaya bedanya kelihatan
   dari sudut mata, bukan cuma terbaca. */
/* Tingginya dipatok CUKUP UNTUK DUA BARIS, bukan satu.
   min-height: 2.6em sebelumnya sama saja dengan tinggi satu baris — jadi dia
   tidak pernah menyamakan apa pun. Di layar 1380px ketiga kalimatnya
   kebetulan muat satu baris dan kotaknya sejajar; di 1200px yang panjang
   membungkus jadi dua baris dan yang pendek tidak, dan kotak berkasnya
   bergeser 17px antar kolom.

   Kotak berkas yang tidak sejajar membuat orang salah lihat kotak mana yang
   sedang dia isi — dan berkasnya berjudul kolom sama persis, jadi itu
   kesalahan yang TIDAK BISA diperbaiki dari dalam berkas.

   Ketahuannya bukan dari tes: pematah "kotak berkas tidak lagi sejajar"
   HIJAU, dan waktu dikejar kenapa, yang ketemu justru kotaknya memang sudah
   tidak sejajar sejak awal di lebar itu. Pematah yang tidak menggigit
   ternyata menunjuk bug, bukan pagar yang berlebihan. */
.bni-to       { font-size: 11.5px; line-height: 1.5; padding: 5px 9px; border-radius: 7px;
                margin-bottom: 10px; min-height: calc(3em + 12px); }
.bni-to.perlu { background: color-mix(in srgb, var(--warn) 12%, transparent);
                color: var(--warn); border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent); }
.bni-to.bebas { background: var(--surface-2); color: var(--muted); border: 1px solid var(--border); }

.bni-tomark   { font-size: 10.5px; font-weight: 700; letter-spacing: .02em;
                color: var(--warn); border: 1px solid color-mix(in srgb, var(--warn) 34%, transparent);
                border-radius: 6px; padding: 1px 6px; margin-left: 6px; white-space: nowrap; }

/* Yang gugur syarat TO, waktu jumlahnya BUKAN nol. Nol tetap ditulis, tapi
   abu-abu: dia keterangan, bukan temuan. */
.bni-kenato   { border-color: color-mix(in srgb, var(--warn) 40%, transparent) !important;
                color: var(--warn); }

.bni-golongan    { font-size: 10.5px; font-weight: 700; text-transform: uppercase;
                   letter-spacing: .03em; color: var(--muted); margin-left: 6px; }
.bni-belumtandai { font-size: 10.5px; font-weight: 700; color: var(--warn); margin-left: 6px; }
.bni-hari        { font-size: 12.5px; font-weight: 600; color: var(--muted); }

.bn-form   { padding: 20px 22px; }
.bn-head h3 { margin: 0 0 4px; font-size: 17px; }
.bn-head .sub { margin: 0; max-width: 760px; line-height: 1.7; }

.bn-periode {
  display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap;
  margin: 18px 0 4px; padding: 14px 16px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px;
}
/* max-width: 100% pada LABEL-nya, bukan cuma pada select-nya.
   min-width pada select membuat dia lebih lebar dari layar sempit, dan
   flex-item bawaannya TIDAK boleh menyusut di bawah lebar isinya
   (min-width: auto) — jadi tanpa min-width: 0 di sini, halamannya meluber
   ke samping di layar 420px dan seluruh isinya ikut bergeser.

   Ketahuan 8 September 2026 di halaman Bonus IDN, yang pilihan periodenya
   lebih panjang ("21 Aug 2026 - 31 Aug 2026 (11 hari) (periode berjalan)")
   daripada punya PNG. Halaman PNG memakai aturan yang sama dan kebetulan
   selamat karena kalimatnya lebih pendek — kebetulan, bukan penjagaan. */
.bn-periode label { display: flex; flex-direction: column; gap: 5px;
                    min-width: 0; max-width: 100%; }
.bn-periode select { max-width: 100%; }
.bn-situs select { min-width: 220px; font-weight: 620; }
/* Periodenya dipilih, tidak diketik — lebar supaya kedua tanggalnya utuh. */
.bn-periodepilih select { min-width: 300px; font-weight: 620; }
@media (max-width: 560px) {
  /* Di layar sempit yang menang KEUTUHAN HALAMAN, bukan keutuhan kalimat:
     pilihan yang terpotong masih bisa dibuka dan dibaca isinya, halaman yang
     meluber membuat seluruh kolom bergeser. */
  .bn-situs select, .bn-periodepilih select { min-width: 0; width: 100%; }
}
.bn-periode label > span { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.bn-pnote  { margin: 0; font-size: 13px; line-height: 1.7; }
.bn-pnote .sub { font-size: 12px; }

/* Keterangan daftar situs. Kuning waktu AUDITOR belum bisa membedakan PNG
   dari IDN — dan sengaja TIDAK abu-abu: aturan hitung yang tertukar tidak
   menimbulkan error, cuma angka yang salah, jadi kalimat itu harus terbaca. */
.bn-situsnota {
  margin-top: 10px; padding: 10px 13px; border-radius: 9px;
  font-size: 12.5px; line-height: 1.75; border: 1px solid var(--border);
}
.bn-situsnota code { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; }
.bn-situsnota.ok   { color: var(--ink-2); background: var(--surface-2); }
.bn-situsnota.warn {
  border-color: color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
}
.bn-situsnota.bad  {
  border-color: color-mix(in srgb, var(--crit) 40%, transparent);
  background: color-mix(in srgb, var(--crit) 7%, transparent);
}

.bn-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 14px; margin-top: 16px;
}
/* Satu kotak sendirian tidak dipaksa selebar sepertiga layar — tapi juga
   tidak dibiarkan selebar penuh: kotak berkas selebar 1200px membuat label
   dan kotak isiannya berjauhan sampai mata kehilangan pasangannya. */
.bn-grid.satu { grid-template-columns: minmax(0, 560px); }
.bn-box {
  border: 1px solid var(--border); border-radius: 11px; padding: 15px 16px;
  display: flex; flex-direction: column; gap: 10px;
}
.bn-jenis  { font-weight: 680; font-size: 14.5px; }
/* Tinggi minimal dua baris supaya ketiga kotak sejajar: syarat Slot muat
   satu baris dan dua lainnya dua baris, dan kotak isian yang tidak sejajar
   membuat orang salah lihat kotak mana yang sedang dia isi. */
/* min-height muat TIGA baris, bukan dua. Kalimat Live Games ("2,5% untuk
   Rp100.000-Rp249.999.999 · 5% untuk Rp250.000.000 ke atas") jauh lebih
   panjang dari dua lainnya, dan di bawah 1150px dia jadi tiga baris sementara
   Arcade dan PP tetap dua — kotak berkasnya bergeser 19px, dan orang mengisi
   kotak sebelah tanpa merasa ada yang salah. Sama persis dengan yang sudah
   terjadi di .bni-to; ini baris keduanya. */
.bn-syarat { font-size: 11.8px; color: var(--muted); line-height: 1.6;
             min-height: calc(3 * 1.6em); }

.bn-file { display: flex; flex-direction: column; gap: 5px; }
.bn-file > span { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.bn-file input[type=file] { font-size: 12px; }

.bn-lead {
  border-top: 1px dashed var(--border); padding-top: 11px;
  display: flex; flex-direction: column; gap: 8px;
}
.bn-leadhead { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.bn-lead label { display: flex; align-items: center; gap: 9px; }
.bn-lead label > span { flex: 0 0 60px; font-size: 12.5px; color: var(--ink-2); }
.bn-lead input { flex: 1; min-width: 0; font-variant-numeric: tabular-nums; }

/* Peringatan ukuran unggahan. Merah waktu kirimannya sudah pasti dibuang
   server, kuning waktu masih muat tapi sudah mepet. */
.bn-ukuran {
  margin-top: 14px; padding: 10px 13px; border-radius: 9px;
  font-size: 12.6px; line-height: 1.75; border: 1px solid var(--border);
}
.bn-ukuran code { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; }
.bn-ukuran.warn {
  border-color: color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
}
.bn-ukuran.bad {
  border-color: color-mix(in srgb, var(--crit) 40%, transparent);
  background: color-mix(in srgb, var(--crit) 7%, transparent);
}

.bn-act { margin-top: 18px; display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.bn-actnote { font-size: 12px; color: var(--muted); line-height: 1.65; max-width: 520px; }

.bn-tahu   { padding: 18px 22px; }
.bn-tahu h4 { margin: 0 0 10px; font-size: 13.5px; }
.bn-tahu ul { margin: 0; padding-left: 19px; }
.bn-tahu li { font-size: 12.8px; line-height: 1.75; margin-bottom: 8px; color: var(--ink-2); }
.bn-tahu code { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; }

/* ---- hasil ---- */

.bn-vonis { display: flex; gap: 18px; align-items: flex-start; padding: 20px 22px; }
.bn-vonis h3   { margin: 0 0 4px; font-size: 17px; }
/* Nama situs lebih besar dari kalimat vonisnya: yang pertama dicari mata di
   selembar PDF arsip adalah "ini situs apa", bukan "klop atau tidak". */
.bn-vsitus     { display: flex; align-items: baseline; gap: 9px; margin-bottom: 3px; flex-wrap: wrap; }
.bn-vsitus b   { font-size: 21px; font-weight: 720; letter-spacing: -.01em; }
.bn-vsitus span {
  font-size: 11px; letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--border); border-radius: 99px; padding: 2px 8px;
}
.bn-vonis .sub { margin: 0; max-width: 720px; line-height: 1.7; }
.bn-vonis.good { border-left: 4px solid var(--good); }
.bn-vonis.warn { border-left: 4px solid var(--warn); }
.bn-vonis.bad  { border-left: 4px solid var(--crit); }
.bn-vmark {
  flex: 0 0 auto; width: 46px; height: 46px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  font: 700 22px/1 system-ui, sans-serif;
}
.bn-vonis.good .bn-vmark { background: color-mix(in srgb, var(--good) 16%, transparent); color: var(--good); }
.bn-vonis.warn .bn-vmark { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.bn-vonis.bad  .bn-vmark { background: color-mix(in srgb, var(--crit) 14%, transparent); color: var(--crit); }
.bn-print { flex: 0 0 auto; margin-left: auto; }

.bn-kembar {
  border-left: 4px solid var(--crit); padding: 15px 18px;
  font-size: 13.5px; line-height: 1.7;
}
.bn-kembar p { margin: 6px 0 0; font-size: 12.6px; color: var(--ink-2); max-width: 780px; }

.bn-hasil        { padding: 17px 20px; border-left: 4px solid var(--border); }
.bn-hasil.good   { border-left-color: var(--good); }
.bn-hasil.warn   { border-left-color: var(--warn); }
.bn-hasil.bad    { border-left-color: var(--crit); }
/* Abu-abu pudar, dan SENGAJA tidak hijau: belum diisi bukan berita baik. */
.bn-hasil.kosong { border-left-color: var(--muted); opacity: .92; }
.bn-hasil.rusak  { border-left-color: var(--crit); }

.bn-hhead   { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }
.bn-hhead > div { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; min-width: 0; }
.bn-hhead .badge { margin-left: auto; }
.bn-hjenis  { font-weight: 690; font-size: 15px; }
.bn-hberkas {
  font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted); word-break: break-all;
}

.bn-galat {
  margin-top: 11px; border-left: 3px solid var(--crit); padding: 8px 0 8px 12px;
  font-size: 13px; line-height: 1.75; max-width: 820px;
}

.bn-angka { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 14px; }
.bn-kolom {
  border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px;
  background: var(--surface-2);
  /* Tabel adu punya empat kolom angka dan tidak bisa menyempit tanpa batas.
     Yang melebar HARUS bergeser di dalam kotaknya sendiri — halaman yang
     meluber ke samping membuat kolom Selisih hilang di layar kecil, dan
     kolom itu satu-satunya alasan tabel ini ada. */
  min-width: 0; overflow-x: auto;
}
.bn-khead { font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase; color: var(--muted); margin-bottom: 9px; }
.bn-brs {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 4px 0; font-size: 13px;
}
.bn-brs > span { color: var(--ink-2); }
.bn-brs > b    { font-variant-numeric: tabular-nums; white-space: nowrap; }
.bn-besar      { font-size: 15px; font-weight: 700; }
.bn-selisih    { border-top: 1px dashed var(--border); margin-top: 5px; padding-top: 7px; }

/* ── KOTAK REPORTAN LEADER, BARIS PER BARIS ──────────────────────────────
   Bentuknya mengikuti reportan leader: satu baris per pita persen (Live
   Games) atau per level (Extra Bonus), dua kolom Form dan Coin. Yang
   ditiru bukan gayanya, melainkan URUTAN BACANYA — mata pindah dari
   reportan ke layar tanpa harus mencari padanan tiap angka. */
.bni-leadgrid {
  display: grid; grid-template-columns: 1fr 78px 108px; gap: 5px 7px;
  align-items: center;
}
.bni-leadhd  { font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
               color: var(--muted); text-align: right; }
.bni-leadlbl { font-size: 12.3px; color: var(--ink-2); min-width: 0;
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bni-leadgrid input {
  min-width: 0; width: 100%; text-align: right;
  font-variant-numeric: tabular-nums; padding: 5px 7px; font-size: 12.5px;
}
.bni-leadnote { display: block; text-transform: none; letter-spacing: 0;
                font-size: 11px; color: var(--muted); margin-top: 2px; }

/* Delapan baris level butuh baris yang lebih rapat, kalau tidak kotak
   Extra Bonus jadi dua kali tinggi kotak lain dan barisan kotaknya
   bergerigi. */
.bni-lead.banyak .bni-leadgrid       { gap: 3px 7px; }
.bni-lead.banyak .bni-leadgrid input { padding: 3px 7px; font-size: 12px; }

/* ── REPORTAN LEADER: DITUTUP, TAPI TETAP TERLIHAT ADA ────────────────────
 *
 * abby, 9 September 2026, tidak lagi memakainya sehari-hari — history koin
 * sudah cukup. Jadi kotaknya ditutup, BUKAN dihapus: satu hal masih cuma bisa
 * dijawab dari sini (kenapa hitungan kita beda dari yang DILAPORKAN leader).
 *
 * Yang dijaga aturan-aturan di bawah: kotak yang tertutup harus terlihat
 * seperti sesuatu yang BISA DIBUKA. Details tanpa tanda panah dan tanpa
 * kursor tunjuk terbaca seperti judul mati, dan fitur yang tidak terlihat
 * bisa dibuka sama saja dengan fitur yang dihapus. */
.bni-leadwrap > summary {
  cursor: pointer; list-style: none; display: flex; align-items: baseline;
  flex-wrap: wrap; gap: 0 8px; padding: 2px 0; border-radius: 6px;
}
.bni-leadwrap > summary::-webkit-details-marker { display: none; }
.bni-leadwrap > summary::before {
  content: '\25B8'; font-size: 10px; color: var(--muted);
  transition: transform .12s; display: inline-block;
}
.bni-leadwrap[open] > summary::before { transform: rotate(90deg); }
.bni-leadwrap > summary:hover  { color: var(--ink-2); }
.bni-leadwrap > summary:focus-visible { outline: 2px solid var(--s1); outline-offset: 2px; }
.bni-leadwrap[open] > .bni-leadgrid { margin-top: 9px; }

/* "opsional" — supaya kotak yang tertutup tidak terbaca seperti langkah yang
   terlewat. Kata ini yang membedakan "belum kukerjakan" dari "memang tidak
   perlu". */
.bni-leadopsi {
  font-size: 10px; letter-spacing: .04em; text-transform: none;
  color: var(--muted); border: 1px solid var(--border);
  border-radius: 99px; padding: 1px 7px;
}
/* Sudah ada isinya: dikatakan DI KEPALA yang tertutup. Angka yang diketik lalu
   tersembunyi di balik kotak yang terlihat kosong adalah angka yang ikut
   terkirim tanpa diketahui pengetiknya. */
.bni-leadwrap.terisi > summary .bni-leadopsi {
  color: var(--s1); border-color: color-mix(in srgb, var(--s1) 40%, transparent);
  background: color-mix(in srgb, var(--s1) 8%, transparent);
}

/* ── TABEL ADU DI LAYAR HASIL ─────────────────────────────────────────── */
.bni-adu { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.bni-adu th {
  font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; padding: 0 0 5px; text-align: left;
}
.bni-adu th.num, .bni-adu td.num { text-align: right; }
.bni-adu td {
  padding: 5px 0 5px 9px; border-top: 1px solid var(--border);
  font-variant-numeric: tabular-nums; vertical-align: top;
}
.bni-adu td:first-child { padding-left: 0; color: var(--ink-2); }
.bni-adu tfoot td       { border-top: 2px solid var(--border); font-weight: 700; }

/* Angka kita ditulis DI BAWAH angka leader, bukan di kolom terpisah:
   yang dicari mata "beda berapa", dan dua angka yang bersebelahan
   dibandingkan lebih cepat daripada dua kolom yang berjauhan. */
.bni-kita    { display: block; font-size: 11px; color: var(--muted); font-weight: 400; }
.bni-nominal { display: block; font-size: 11px; color: var(--muted); }
.bni-kosong  { font-size: 11.5px; color: var(--muted); font-weight: 400; }

/* Baris yang tidak klop diberi warna, yang kosong TIDAK — kosong bukan
   temuan, dan mewarnainya membuat delapan level yang belum diisi terbaca
   seperti delapan selisih. */
.bni-adu tr.bad  td { background: color-mix(in srgb, var(--warn) 9%, transparent); }
.bni-adu tr.good td { color: var(--ink-2); }

@media (max-width: 560px) {
  .bni-leadgrid { grid-template-columns: 1fr 62px 88px; }
  .bni-adu      { font-size: 11.8px; }
}

.bn-belum, .bn-rusak {
  font-size: 12.7px; line-height: 1.75; color: var(--ink-2);
  padding: 4px 0;
}
.bn-rusak { color: var(--crit); }

/* Reportan yang sengaja dikosongkan, sementara jenisnya SUDAH terbukti dari
   history koin. Hijau pudar, bukan abu-abu: keadaan ini hasil yang benar,
   bukan langkah yang terlewat. */
.bn-bukti {
  font-size: 12.7px; line-height: 1.75; color: var(--ink-2); padding: 4px 0;
}
.bn-bukti > p {
  margin: 7px 0 0; padding-top: 7px; font-size: 12.2px; color: var(--muted);
  border-top: 1px dashed var(--border);
}
.bn-rusak code { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; }

.bn-beda {
  margin-top: 12px; border-left: 3px solid var(--warn);
  padding: 8px 0 8px 12px; font-size: 12.8px; line-height: 1.75; max-width: 840px;
}

.bn-rinci { margin-top: 13px; display: flex; gap: 8px; flex-wrap: wrap; }
.bn-tier {
  font-size: 11.8px; line-height: 1.5;
  border: 1px solid var(--border); border-radius: 8px; padding: 5px 10px;
  background: var(--surface-1);
}
.bn-tier b  { margin-right: 5px; }
.bn-tier em { color: var(--muted); font-style: normal; }
.bn-abu     { color: var(--muted); background: var(--surface-2); }

.bn-catat {
  margin-top: 13px; display: flex; flex-direction: column; gap: 9px;
  font-size: 12.4px; line-height: 1.75; color: var(--ink-2);
}
.bn-catat code { font: 11px ui-monospace, SFMono-Regular, Menlo, monospace; word-break: break-all; }

.bn-lagi { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 15px 20px; }
.bn-lagi .sub { font-size: 12.3px; max-width: 560px; line-height: 1.65; }

@media (max-width: 980px) {
  .bn-grid  { grid-template-columns: 1fr; }
  .bn-angka { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .bn-vonis   { gap: 13px; padding: 16px; }
  /* min-width: 0 pada kolom teksnya. Tanpa ini kolom itu tidak mau menyempit
     di bawah lebar isinya, jadi tombol "Cetak / simpan PDF" (156px) terdorong
     keluar dan di layar 375px SELURUH halaman bisa digeser ke samping. Yang
     terlihat orang bukan tombolnya — melainkan halaman yang bergoyang tiap
     kali disentuh.
     (flex-wrap: wrap pernah ikut ditulis di sini dan sudah dibuang: dipatahkan
     tetap hijau, karena min-width sendiri sudah cukup.) */
  .bn-vonis > div { min-width: 0; }
  .bn-print   { margin-left: 0; }
  .bn-hhead .badge { margin-left: 0; }
}

/* Dicetak jadi PDF: yang disimpan auditor cuma kertas ini, jadi tombol dan
   menu tidak ikut, dan tiap jenis tidak boleh terpotong di tengah halaman. */
@media print {
  .bn-print, .bn-lagi { display: none !important; }
  .bn-hasil, .bn-vonis, .bn-kembar { break-inside: avoid; }
}

/* ═══════════════════════════════════════════════════════════════════════
   PAPAN BONUS PNG  (.bp-)  — situs mana yang sudah dikerjakan

   Satu aturan warna yang menentukan apakah papan ini berguna atau justru
   berbahaya: EMPAT KEADAAN, EMPAT WARNA. "Sudah dicek tapi TIDAK KLOP"
   tidak boleh sewarna dengan "belum dikerjakan" — yang pertama butuh
   leader menelusuri selisihnya, yang kedua butuh auditor mengerjakannya,
   dan menyamakan keduanya membuat papan ini menyuruh orang mengulang yang
   sudah selesai sambil mendiamkan yang belum. Keempatnya diukur tes.
   ═══════════════════════════════════════════════════════════════════════ */

.bp-ok  { border-left: 4px solid var(--good); padding: 13px 17px; font-size: 13.5px; line-height: 1.7; }
.bp-err { border-left: 4px solid var(--crit); padding: 13px 17px; font-size: 13.5px; line-height: 1.7; }
.bp-err code, .bp-ok code { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; }

.bp-atas { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; padding: 19px 22px; }
.bp-atas h3   { margin: 0 0 4px; font-size: 17px; }
.bp-atas .sub { margin: 0; max-width: 700px; line-height: 1.7; }
/* max-width, bukan cuma flex-wrap di induknya: pilihan periode menulis dua
   tanggal penuh ditambah "(11 hari) (berjalan)", jadi <select> ikut selebar
   pilihan terpanjangnya — 360px — dan di ponsel dialah yang membuat SELURUH
   halaman bisa digeser ke samping. Yang terlihat orang bukan kotak pilihnya:
   yang terlihat papan yang bergoyang tiap kali disentuh. */
.bp-pilih { margin-left: auto; display: flex; align-items: flex-end; gap: 9px;
            max-width: 100%; min-width: 0; flex-wrap: wrap; }
.bp-pilih label { display: flex; flex-direction: column; gap: 5px;
                  min-width: 0; max-width: 100%; }
.bp-pilih select { max-width: 100%; }
.bp-pilih label > span { font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }

.bp-ringkas { display: flex; gap: 12px; flex-wrap: wrap; padding: 15px 22px; }
.bp-kotak {
  flex: 1 1 130px; display: flex; flex-direction: column; gap: 2px;
  border: 1px solid var(--border); border-radius: 10px; padding: 11px 14px;
}
.bp-kotak b    { font-size: 22px; font-variant-numeric: tabular-nums; line-height: 1.1; }
.bp-kotak span { font-size: 11.5px; color: var(--muted); }
.bp-kotak.good   { border-color: color-mix(in srgb, var(--good) 40%, transparent); background: color-mix(in srgb, var(--good) 7%, transparent); }
.bp-kotak.good b { color: var(--good); }
.bp-kotak.bad    { border-color: color-mix(in srgb, var(--crit) 38%, transparent); background: color-mix(in srgb, var(--crit) 6%, transparent); }
.bp-kotak.bad b  { color: var(--crit); }
.bp-kotak.warn   { border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.bp-kotak.warn b { color: #7d5800; }
:root[data-theme="dark"] .bp-kotak.warn b { color: var(--warn); }
/* Belum dikerjakan: abu-abu, dan SENGAJA bukan hijau pudar. */
.bp-kotak.kosong   { background: var(--surface-2); }
.bp-kotak.kosong b { color: var(--muted); }

/* BELUM DIBANDINGKAN: biru, warna kelima.
 *
 * 9 September 2026. Keadaan ini dulu ikut merah, dan PWVIP4D — situs yang
 * pembagiannya justru sudah terbukti benar 31 dari 31 member — tertulis
 * "tidak klop" tiga kali. Birunya sengaja: bukan merah (tidak ada yang
 * salah), bukan hijau (belum ada yang membuktikan), dan bukan abu-abu
 * (pekerjaannya sudah dikerjakan, tinggal buktinya). */
.bp-kotak.tanya   { border-color: color-mix(in srgb, var(--s1) 40%, transparent);
                    background: color-mix(in srgb, var(--s1) 7%, transparent); }
.bp-kotak.tanya b { color: var(--s1); }

/* Tidak ada overflow-x di sini. Pernah kutambahkan, dan mematahkannya tetap
   HIJAU: aturan yang sama sudah ada di @media (max-width: 900px) di bawah,
   bersama .bp-tbl { min-width: 720px } yang membuatnya berarti. Aturan kedua
   yang tidak menahan apa pun terbaca seperti dua penjagaan, dan yang asli
   bisa terhapus tanpa satu tes pun berubah warna. */
.bp-daftar { padding: 6px 0 0; }
.bp-tbl td { vertical-align: middle; }
.bp-tbl td:first-child { white-space: nowrap; }

.bp-lampu {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-right: 9px; vertical-align: middle; background: var(--muted); opacity: .45;
}
.bp-row.good   .bp-lampu { background: var(--good); opacity: 1; }
.bp-row.bad    .bp-lampu { background: var(--crit); opacity: 1; }
.bp-row.warn   .bp-lampu { background: var(--warn); opacity: 1; }
.bp-row.tanya  .bp-lampu { background: var(--s1); opacity: 1; }

.bp-kead { margin-left: 9px; font-size: 11.5px; color: var(--muted); }
.bp-row.bad  .bp-kead { color: var(--crit); font-weight: 600; }
.bp-row.warn .bp-kead { color: #7d5800; font-weight: 600; }
:root[data-theme="dark"] .bp-row.warn .bp-kead { color: var(--warn); }
.bp-row.tanya .bp-kead { color: var(--s1); font-weight: 600; }

.bp-sel {
  display: inline-block; font-size: 11px; font-weight: 620; letter-spacing: .01em;
  border-radius: 99px; padding: 2px 9px; white-space: nowrap;
  border: 1px solid var(--border);
}
.bp-sel.good   { color: var(--good); border-color: color-mix(in srgb, var(--good) 34%, transparent); background: color-mix(in srgb, var(--good) 8%, transparent); }
.bp-sel.bad    { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 36%, transparent); background: color-mix(in srgb, var(--crit) 7%, transparent); }
.bp-sel.kosong { color: var(--muted); border-color: transparent; background: none; }
/* "perlu dicek" — tidak bisa dibersihkan DAN tidak bisa dituduh. Warnanya
   sendiri, karena memaksanya jadi hijau atau merah bohong ke dua arah. */
.bp-sel.ragu { color: var(--s1); border-color: color-mix(in srgb, var(--s1) 34%, transparent);
               background: color-mix(in srgb, var(--s1) 8%, transparent); }

/* Baris judul pertama: TERHADAP APA tiap kolom diadu. Tanpa dia, sel
   "reportan kosong" di baris yang bertanda KLOP terbaca seperti pekerjaan
   yang belum selesai. */
.bp-grup th { border-bottom: 0; padding-bottom: 0; background: var(--surface-sunk); }
.bp-gcol {
  font-size: 9.5px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; text-align: left;
  border-bottom: 1px solid var(--border) !important; padding-bottom: 3px !important;
}

/* "Berapa jenis tidak ikut diperiksa" menempel pada sel per-member yang
   hijau. Tanpa ini, "687 ID klop" untuk dua dari tiga jenis terbaca sama
   dengan "687 ID klop" untuk ketiga-tiganya. */
.bp-lewat { display: block; margin-top: 2px; font-size: 10.5px; color: var(--warn); }

/* Daftar jenis yang belum diadu dengan apa pun, di spanduk vonis. Birunya
   sama dengan keadaan "belum dibandingkan" di papan: bukan salah, cuma
   belum terbukti. */
.bn-vbelum {
  margin: 8px 0 0; padding: 7px 11px; font-size: 12.5px; line-height: 1.6;
  border-radius: 8px; color: var(--ink-2);
  border: 1px solid color-mix(in srgb, var(--s1) 30%, transparent);
  background: color-mix(in srgb, var(--s1) 6%, transparent);
}
.bn-vbelum b { color: var(--s1); }

.bp-oleh { font-size: 12px; line-height: 1.5; }
.bp-oleh .sub { font-size: 11px; }

@media (max-width: 900px) {
  .bp-pilih  { margin-left: 0; }
  .bp-daftar { overflow-x: auto; }
  .bp-tbl    { min-width: 720px; }
}

/* Tombol simpan di halaman hasil. */
.bn-simpan {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 15px 20px; border-left: 4px solid var(--s1);
}
.bn-simpan > div { font-size: 12.6px; line-height: 1.75; color: var(--ink-2); max-width: 700px; }
.bn-simpan.mati  { border-left-color: var(--border); }
.bn-simpan code  { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; }

.bn-arsip {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 13px 18px; font-size: 12.8px; line-height: 1.75;
  border-left: 4px solid var(--s1);
}
.bn-arsip .btn { margin-left: auto; }

@media print { .bn-simpan, .bn-arsip .btn { display: none !important; } }

/* Peringatan periode: tanggal yang salah pilih tidak menghasilkan error,
   dia menghasilkan periode lain yang sama sahnya dan disimpan dengan benar
   ke tempat yang salah. Merah waktu periodenya belum selesai (selalu salah),
   kuning waktu bukan periode berjalan (kadang memang disengaja). */
.bn-jaman {
  margin-top: 12px; padding: 11px 14px; border-radius: 9px;
  font-size: 12.7px; line-height: 1.75; border: 1px solid var(--border);
}
/* Bentuk kartunya punya NAMA SENDIRI, bukan .card.bn-jaman. Satu nama kelas
   untuk dua bentuk berbeda harus ditimpa dengan pemilih gabungan, dan
   penimpaan seperti itu sudah dua kali merusak tata letak di proyek ini
   tanpa satu tes pun merah (.lg-row, .rowmore). */
.bn-jamankartu { padding: 14px 18px; font-size: 12.7px; line-height: 1.75; }

.bn-jaman.warn, .bn-jamankartu.warn {
  border-color: color-mix(in srgb, var(--warn) 48%, transparent);
  background: color-mix(in srgb, var(--warn) 11%, transparent);
  border-left-width: 4px;
}
.bn-jaman.bad, .bn-jamankartu.bad {
  border-color: color-mix(in srgb, var(--crit) 42%, transparent);
  background: color-mix(in srgb, var(--crit) 8%, transparent);
  border-left-width: 4px;
}

/* Tombol hapus di halaman arsip — admin & leader saja. */
.bn-hapus {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 20px; border-left: 4px solid var(--crit);
}
.bn-hapus > div { font-size: 12.6px; line-height: 1.75; color: var(--ink-2); max-width: 680px; }
@media print { .bn-hapus { display: none !important; } }

/* ── HISTORY KOIN: pemeriksaan per member ───────────────────────────────
   Kartu ini menjawab pertanyaan yang berbeda dari kartu per jenis di
   bawahnya — bukan "apakah totalnya sama", melainkan "apakah tiap member
   dibayar sebesar haknya". Karena itu dia di atas, dan warnanya sendiri. */
.bni-koin { border-left: 4px solid var(--s1); padding: 16px 20px; margin-top: 14px; }
.bni-koinhead { display: flex; gap: 14px; align-items: flex-start; justify-content: space-between; }
.bni-koinhead h4 { margin: 0 0 4px; font-size: 14.5px; }
.bni-koinhead .sub { margin: 0 0 12px; max-width: 760px; line-height: 1.7; }
.bni-koinopsi {
  flex: 0 0 auto; font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); border: 1px solid var(--border); border-radius: 6px; padding: 2px 8px;
}
.bni-koinnota { margin: 12px 0 0; padding-left: 18px; font-size: 12.3px;
                line-height: 1.75; color: var(--ink-2); }
.bni-koinnota code { font: 11.5px ui-monospace, SFMono-Regular, Menlo, monospace; }

.bni-koinhasil      { padding: 18px 20px; margin-bottom: 14px; border-left: 4px solid var(--border); }
.bni-koinhasil.good { border-left-color: var(--good); }
.bni-koinhasil.warn { border-left-color: var(--warn); }
.bni-koinhasil.bad  { border-left-color: var(--crit); }

.bni-koinangka { display: flex; flex-wrap: wrap; gap: 10px 26px; margin: 14px 0 4px; }
.bni-koinangka > div { display: flex; flex-direction: column; gap: 1px; }
.bni-koinangka span  { font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.bni-koinangka b     { font-size: 15px; font-variant-numeric: tabular-nums; }
.bni-ok     { color: var(--good); }
.bni-bad    { color: var(--crit); }
.bni-kurang { color: var(--warn); }

.bni-koinbersih, .bni-koinaturan { margin: 10px 0 0; line-height: 1.7; max-width: 760px; }
.bni-koinkosong {
  border-left: 4px solid var(--muted); padding: 14px 18px; margin-bottom: 14px;
  font-size: 12.7px; line-height: 1.75; color: var(--ink-2);
}

/* max-height + overflow-y: daftar temuan bisa ratusan baris kalau pembagian
   satu hari kacau. Tanpa batas tinggi, kartunya mendorong seluruh isi halaman
   ke bawah dan judul kolomnya hilang dari layar begitu digulung. Kotak gulung
   sendiri inilah yang juga membuat <th> sticky di bawah bekerja. */
.bni-temuan { margin-top: 12px; max-height: 62vh; overflow-y: auto; }
.bni-tabel  { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.bni-tabel th {
  /* Latarnya datang dari `thead th` umum (--surface-sunk) dan tidak ditulis
     ulang di sini: pematah membuktikan menghapusnya di sini tidak mengubah
     apa pun. Tapi latar pekat itu WAJIB ada — judul kolom yang menempel
     tapi tembus pandang membuat baris temuan menembusnya waktu digulung,
     jadi nama ID dan judul kolom bertumpuk di baris yang sama. Dijaga
     bonusidn.js, di keadaan `koinbanyak`. */
  position: sticky; top: 0; z-index: 1;
  font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; text-align: left; padding: 6px 10px 6px 0;
  border-bottom: 1px solid var(--border);
}
.bni-tabel th.num, .bni-tabel td.num { text-align: right; white-space: nowrap; }
/* Kolom ID cukup lebar untuk memuat DUA ejaan. Tanpa ini kolom sebab memakan
   semuanya dan ejaan kedua terpotong jadi dua-tiga baris.

   Dipatok ke .bni-idm, BUKAN ke :first-child: sejak ada kolom centang di
   halaman arsip, kolom pertamanya bukan kolom ID lagi — dan 148px untuk satu
   kotak centang mendorong seluruh tabelnya keluar layar. */
.bni-tabel td.bni-idm, .bni-tabel th.bni-idm { min-width: 148px; }
.bni-tabel td {
  padding: 6px 10px 6px 0; border-bottom: 1px solid var(--grid);
  font-variant-numeric: tabular-nums; vertical-align: top;
}
.bni-tabel tr.t-tidak-dibayar td { background: color-mix(in srgb, var(--warn) 8%, transparent); }
.bni-tabel tr.t-bukan-hak td     { background: color-mix(in srgb, var(--crit) 8%, transparent); }
/* "Sepertinya ID yang sama" bukan kesalahan yang terbukti — dia pertanyaan.
   Warnanya biru, sama dengan "perlu dicek" di papan, bukan merah. */
.bni-tabel tr.t-beda-tulis td    { background: color-mix(in srgb, var(--s1) 7%, transparent); }
.bni-ejaan {
  display: block; margin-top: 2px; font-size: 11px; font-weight: 400;
  color: var(--muted);
}
.bni-ejaan b { color: var(--s1); font-weight: 600; }
.bni-pecah { display: block; font-size: 11px; color: var(--muted); font-weight: 400; }
.bni-nol   { color: var(--muted); font-style: italic; }

/* Baris "Dibayar (kelipatan 10)" dan keterangannya. Muncul cuma kalau
   pembulatannya memang menggeser angka — baris yang selalu ada akan dilewati
   mata, dan hari dia benar-benar penting tidak ada yang membacanya. */
.bn-brs.bni-bulat b { font-weight: 600; }
.bni-bulatket {
  margin: 6px 0 0; font-size: 11.5px; line-height: 1.6; color: var(--muted);
}
.bni-tol {
  margin: 10px 0 0; font-size: 11.8px; line-height: 1.65; color: var(--muted);
}
.bni-tol b { color: var(--ink-2); font-weight: 600; }

/* ── CENTANG "ERROR FORMAT" ───────────────────────────────────────────────
   abby, 11 September 2026: yang menentukan apakah selisih ini cuma salah
   tulis adalah ORANG yang bisa membuka sistem admin, bukan halaman yang
   cuma melihat dua tulisan. */
/* Lebarnya TIDAK dipatok di sini, dan itu disengaja: judul dua barisnya
   ("Error / format") sudah memberi kolomnya lebar yang cukup, dan pematah
   membuktikan menambah width/min-width tidak mengubah satu jawaban pun.
   Penjaga yang tidak menahan apa pun terbaca seperti dua penjagaan, dan yang
   asli bisa terhapus tanpa satu tes berubah.

   Yang BENAR-BENAR dikerjakan aturan ini: menengahkan. Tanpanya kotak
   centangnya menempel ke tepi kiri dan judul kolomnya rata kiri (dari aturan
   `.bni-tabel th` umum), jadi mata harus mencari kotaknya baris per baris di
   kolom yang justru dibaca paling sering. */
.bni-tabel th.bni-cek, .bni-tabel td.bni-cek {
  text-align: center; padding-right: 8px; line-height: 1.25;
}
.bni-tabel td.bni-cek input { width: 15px; height: 15px; margin: 2px 0 0; cursor: pointer; }

/* Baris yang sudah ditandai TIDAK hilang — dia diredupkan. Baris yang hilang
   berarti minggu depan tidak ada satu pun yang tahu bahwa ID itu pernah
   dilewatkan dengan tangan. Warna sebabnya (merah/kuning/biru) ditimpa jadi
   netral supaya matanya berhenti di yang MASIH perlu dikerjakan. */
/* Tanpa !important, dan itu disengaja: kekhususannya sama persis dengan
   aturan warna sebab di atas (.bni-tabel tr.t-bukan-hak td), jadi yang
   menentukan URUTANNYA — dan aturan ini ada di bawah. Pematah membuktikan
   !important tidak mengubah satu jawaban pun; penjaga yang tidak menahan apa
   pun terbaca seperti dua penjagaan, dan yang asli bisa terhapus tanpa satu
   tes berubah.

   Yang menjaga urutannya: bonusidn.js memeriksa DUA baris bertanda yang
   sebabnya berbeda punya warna yang SAMA. Kalau aturan ini naik ke atas
   aturan sebab, keduanya langsung berbeda. */
.bni-tabel tr.t-ditandai td {
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  color: var(--muted);
}
.bni-tabel tr.t-ditandai td b { font-weight: 500; }
.bni-tandaket {
  display: block; margin-top: 3px; font-size: 11px; font-weight: 400;
  color: var(--muted);
}
.bni-tandaket b { color: var(--ink-2); font-weight: 600; }
.bni-tanda { color: var(--ink-2); }

.bni-tandaikaki {
  display: flex; gap: 12px; align-items: flex-start;
  margin-top: 11px; padding-top: 11px; border-top: 1px solid var(--grid);
}
.bni-tandaikaki > span { font-size: 11.8px; line-height: 1.65; color: var(--muted); }
.bni-tandaikaki > span b { color: var(--ink-2); font-weight: 600; }
.bni-tandaikaki .btn { flex: 0 0 auto; white-space: nowrap; }
.bni-tandaipetunjuk { margin-top: 11px; }

@media (max-width: 640px) {
  .bni-tandaikaki { flex-direction: column; gap: 8px; }
}

/* ====================================================================
   No. paspor di Data Staff, panel export, dan tanda "sedang diperpanjang"
   13 September 2026
   ==================================================================== */

/* ---- chip nomor paspor di kolom Staff ---- */
.sf-pp {
  display: inline-block; margin-left: 7px; padding: 1px 6px;
  font-size: 11.5px; letter-spacing: .03em; vertical-align: 1px;
  border: 1px solid var(--border); border-radius: 5px;
  background: var(--surface-2); color: var(--ink-2); white-space: nowrap;
}
/* "Belum diisi" sengaja TIDAK bermonospasi dan tidak berbingkai penuh: dia
   bukan sebuah nomor, dan kalau bentuknya sama dengan nomor, mata membacanya
   sebagai nomor yang aneh. */
.sf-pp-kosong {
  font-style: italic; letter-spacing: 0; color: var(--muted);
  background: transparent; border-style: dashed;
}

/* Di layar sempit chip-nya turun ke baris sendiri, bukan mendorong nama staff
   keluar kolom (permintaan abby: "bebas, yang penting rapi"). */
@media (max-width: 700px) {
  .sf-pp { display: block; margin: 4px 0 0; width: fit-content; }
}

/* ---- panel export ----
   Barisnya sendiri, di bawah kepala kartu. Tombolnya rata kanan lewat
   text-align, bukan lewat flex: di dalam flex kepala kartu, panel yang sedang
   TERTUTUP tetap ikut menentukan lebar — isinya tidak digambar, tapi masih
   mendorong halaman melebihi layar di lebar tablet. */
.sf-expbar { text-align: right; margin-bottom: 14px; }
.sf-exp { display: block; }
.sf-exp > summary { list-style: none; cursor: pointer; display: inline-block; }
.sf-exp > summary::-webkit-details-marker { display: none; }
.sf-expbox {
  margin-top: 10px; padding: 13px 15px; text-align: left;
  box-sizing: border-box; max-width: 100%;
  border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2);
}
.sf-expjudul { margin: 0 0 10px; font-size: 12px; }
.sf-expgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 7px 20px; }
.sf-expgrid label { font-size: 12.5px; display: flex; gap: 7px; align-items: flex-start; }
.sf-expgrid label .k { display: block; font-size: 11px; color: var(--muted); }
.sf-expfoot {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  margin-top: 13px; padding-top: 11px; border-top: 1px solid var(--border);
}
.sf-expfmt { font-size: 12.5px; display: flex; gap: 6px; align-items: center; }
.sf-expfoot .sub { margin: 0; font-size: 11.5px; }
.sf-expcatat { font-size: 11.5px; color: var(--warn); }

@media (max-width: 700px) {
  .sf-expgrid { grid-template-columns: 1fr; }
  .sf-exp > summary { width: 100%; }
}

/* ---- tanda "sedang diperpanjang" ----
   Satu kelas dipakai di TIGA layar (Paspor & Visa, daftar yang mendekat habis,
   dan Persetujuan cuti) supaya tandanya tidak pernah terlihat berbeda-beda
   tergantung halamannya. */
.urus-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 7px; border-radius: 999px;
  font-size: 11px; font-weight: 600; white-space: nowrap;
  border: 1px solid var(--s1); color: var(--s1); background: transparent;
}
.urus-sejak { font-size: 10.5px; color: var(--muted); }
.urus-sejak-blok { display: block; margin-top: 3px; line-height: 1.5; }

/* Barisnya diredam, TIDAK dihilangkan: yang ditandai lalu terlupakan harus
   tetap kelihatan. Warna teksnya dikembalikan ke warna badan halaman supaya
   merahnya tidak lagi berteriak — bukan disamarkan jadi abu-abu, karena
   dokumennya memang masih habis. */
.exprow.exp-urus b        { color: var(--ink); }
.exprow.exp-urus .exp-tgl,
.exprow.exp-urus .exp-sisa { opacity: .7; }

/* Di layar Persetujuan justru KEBALIKANNYA: warnanya tidak diredam sama
   sekali. Yang menekan Approve harus tetap melihat dokumennya habis; tandanya
   cuma menambah kabar, tidak menguranginya. Jadi .dok-urus tidak mengubah
   warna apa pun — dia cuma memberi tempat chip-nya bernapas. */
.dokblok.dok-urus .dokblok-atas { flex-wrap: wrap; gap: 5px; }

/* ---- tombol di halaman Paspor & Visa ---- */
.dok-urusbox { margin-top: 5px; }
.dok-urusnote {
  margin: 0 0 12px; font-size: 12px; padding: 7px 11px;
  border-left: 3px solid var(--s1); background: var(--surface-2); border-radius: 0 6px 6px 0;
}

/* ---- baris daftar "yang mendekat habis", sesudah tombolnya ikut masuk ----
   Dua kotak: kiri (nama, nomor, chip) dan kanan (tanggal, sisa hari, tombol).
   Bentuk lamanya menaruh semuanya sebagai saudara langsung dalam satu baris
   flex, dan begitu tombolnya ikut, yang menyusut duluan justru nama orangnya —
   nama panjang pecah jadi tiga baris sementara tanggal dan tombol berdesakan.
   Dengan dua kotak, yang berpindah ke baris berikutnya seluruh sisi kanan
   sekaligus, utuh. */
.exp-kiri  { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; min-width: 0; }
.exp-kanan { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; }

/* Tombolnya tidak ikut menyusut: tombol yang tulisannya terpotong jadi tombol
   yang tidak jelas menekan apa. */
.exp-tombol { flex: 0 0 auto; }


/* ====================================================================
   Library — isi panjang dipendekkan, dibaca di jendela kecil
   13 September 2026
   ==================================================================== */

/* Yang memendekkan CSS, bukan PHP: seluruh tulisannya tetap ada di halaman,
   jadi Ctrl+F peramban dan pembaca layar tetap menemukannya, dan jendela
   bacanya tidak perlu mengambil apa pun dari server. */
.lib-bodywrap { position: relative; }
.lib-penuhkan .lib-body {
  max-height: 132px; overflow: hidden;
  /* Dipotong dengan bayangan, bukan dengan garis lurus: potongan lurus di
     tengah kalimat terbaca seperti tulisan yang hilang, bukan tulisan yang
     dilipat. */
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
}
/* Kotak catatan yang dipendekkan tidak lagi punya penggulung sendiri —
   dua penggulung bersarang di satu kartu membuat roda tetikus menggulir
   yang salah. */
.lib-penuhkan .lib-body.lib-note { overflow-y: hidden; max-height: 132px; }
.lib-lagi { margin-top: 7px; }

/* ---- jendela bacanya ---- */
.lib-baca { max-width: 760px; display: flex; flex-direction: column; }
.lib-baca-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.lib-baca-head h3 { margin: 0; min-width: 0; overflow-wrap: break-word; }
.lib-baca-isi {
  font-size: 13px; line-height: 1.65; color: var(--ink-2);
  overflow-y: auto; max-height: 68vh;
  padding: 13px 15px; border-radius: var(--r-sm);
  background: var(--surface-sunk); border: 1px solid var(--border);
  overflow-wrap: break-word; word-break: break-word;
}
/* Enter dan spasinya dipertahankan apa pun jenisnya — alasannya sama dengan
   .lib-body di atas, dan jendela ini harus terbaca persis seperti kartunya. */
.lib-baca-isi { white-space: pre-wrap; }

@media (max-width: 700px) {
  .lib-baca-isi { max-height: 60vh; }
}

/* ── Daftar berkas yang hilang (/admin/health/berkas) ─────────────────────
   Awalan .bk- ; diperiksa tidak menumpang nama yang sudah punya pemilik
   (pelajaran .lg-row dan .rowmore, dua kali). */
.bk-grup { padding: 16px 18px; }
.bk-grup.bad  { border-left: 4px solid var(--crit); }
.bk-grup.warn { border-left: 4px solid var(--warn); }
.bk-grup.good { border-left: 4px solid var(--good); }
.bk-grup.unknown { border-left: 4px solid var(--muted); }

.bk-head { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }
.bk-head b { font-size: 15px; }
.bk-head .btn { margin-left: auto; }
.bk-folder { color: var(--muted); font-size: 12px; }

.bk-sebab { margin: 0 0 10px; line-height: 1.65; color: var(--muted); max-width: 820px; }

.bk-tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.bk-tbl th, .bk-tbl td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.bk-tbl th { color: var(--muted); font-weight: 600; font-size: 12px; white-space: nowrap; }
.bk-tbl tr:last-child td { border-bottom: 0; }
.bk-jalur { font-size: 12px; word-break: break-all; }

.bk-potong { margin: 8px 0 0; }
/* Kolom tombol "lepas penunjuknya". Lebarnya dipatok supaya dia tidak ikut
   melar waktu jalur berkasnya panjang — tombol yang tempatnya bergeser antar
   baris adalah cara termudah menekan tombol di baris yang salah, dan yang
   ditekan di sini melepas sesuatu yang tidak bisa dikembalikan. */
.bk-aksi { width: 1%; white-space: nowrap; text-align: right; }
.bk-aksi form { margin: 0; }
.bk-pesan { padding: 12px 16px; line-height: 1.6; }
.bk-pesan.bad  { border-left: 4px solid var(--crit); }
.bk-pesan.good { border-left: 4px solid var(--good); }
.bk-gagal { border-left: 4px solid var(--crit); padding: 14px 18px; }
.bk-gagal ul { margin: 8px 0 6px 18px; }
.bk-cara { line-height: 1.7; }
.bk-balik { margin-top: 16px; }

/* Ponsel: tabel tiga kolom di layar 360px tidak terbaca. Jalurnya yang
   dikorbankan lebih dulu — dia yang paling panjang dan paling jarang dibaca
   di ponsel, karena memulihkannya tetap harus dari komputer. */
@media (max-width: 640px) {
  .bk-tbl th:nth-child(2), .bk-tbl td:nth-child(2) { display: none; }
  /* Tanggal unggahnya ikut dikorbankan sejak 22 September, dan itu bukan
     penghematan tempat: tanpa ini tombol "lepas penunjuknya" terdorong ke
     x=509 di layar 360px — ada di halaman, di luar layar, dan cuma bisa
     ditemukan dengan menggeser tabelnya ke samping. abby memasang dan membuka
     AUDITOR dari iPad, jadi tombol yang cuma bisa ditekan dari komputer adalah
     tombol yang tidak ada untuk dia. Tanggalnya tetap terbaca dari komputer,
     dan dia memang tidak menentukan apa pun di layar ini. */
  .bk-tbl th:nth-child(3), .bk-tbl td:nth-child(3) { display: none; }
  /* Judul kolomnya boleh membungkus di ponsel. `white-space: nowrap` di .bk-tbl
     th membuat "Yang kehilangan berkasnya" memaksa kolom pertama selebar 252px
     di layar 360px — dan sisanya, termasuk tombolnya, terdorong ke luar kotak
     tabel lalu terpotong. Judulnya masih terbaca dalam dua baris; tombol yang
     terpotong tidak ada sama sekali. */
  .bk-tbl th { white-space: normal; }
  .bk-head .btn { margin-left: 0; }
}

/* ── Kesalahan Auditor per periode (/workforce/kesalahan) ─────────────────
   Awalan .ka- ; semuanya nama baru, tidak menumpang milik halaman lain
   (pelajaran .lg-row dan .rowmore, dua kali). */
.ka-head { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; padding: 16px 18px; }
.ka-head h3 { margin: 0 0 4px; }
.ka-head .sub { margin: 0; }
.ka-head-kanan { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

.ka-sop { padding: 14px 18px; }
.ka-sop summary { cursor: pointer; }
.ka-sop-isi { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
              gap: 18px; margin-top: 12px; }
.ka-sop-isi ul { margin: 6px 0; padding-left: 18px; }
.ka-sop-isi p { margin: 4px 0 0; }
.ka-warn { color: var(--crit); }

.ka-bulan-tab { display: flex; gap: 8px; margin: 14px 0 10px; flex-wrap: wrap; }

.ka-papan-bungkus { padding: 12px 0 4px; }
/* Gulir ke samping ada DI SINI, bukan di seluruh halaman: halaman yang ikut
   bergeser membuat menu dan tombolnya ikut hilang dari layar. */
.ka-gulir { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.ka-papan { border-collapse: separate; border-spacing: 0; font-size: 13px; width: max-content; min-width: 100%; }
.ka-papan th, .ka-papan td { border-bottom: 1px solid var(--border); padding: 5px 6px; }

/* ATURAN 2: kolom nama menempel waktu digeser. */
.ka-papan .ka-nama {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface-1); text-align: left; min-width: 180px; max-width: 230px;
  border-right: 1px solid var(--border);
}
.ka-kode { display: block; font-size: 11px; color: var(--muted); }

/* ── BARIS TANGGAL TETAP DI ATAS (15 Oktober 2026) ───────────────────────
 *
 * abby: *"yg ini saya butuh freeze kan tanggalnya biar kita ga susah lihat
 * poin yg di input tidak salah tanggal"*.
 *
 * Papan ini punya 36 auditor. Digulir ke bawah, baris tanggalnya hilang —
 * dan yang tersisa cuma deretan kotak kosong yang semuanya terlihat sama.
 *
 * KENAPA TINGGINYA DIPATOK. Ini persis pelajaran yang sudah dibayar di
 * papan Chat Recap (.mxwrap): `position: sticky; top: 0` TIDAK bisa
 * menempel ke gulungan HALAMAN di sini, karena .ka-gulir sudah jadi wadah
 * bergulir sendiri — `overflow-x: auto` membuat sumbu satunya ikut `auto`
 * menurut spesifikasinya, walaupun tidak pernah ditulis. Jadi kepalanya
 * menempel ke puncak .ka-gulir, dan puncak itu tidak pernah bergerak.
 *
 * Akibatnya: tanpa tinggi maksimum, aturan di bawah ini TIDAK melakukan
 * apa-apa sama sekali, dan tidak ada satu pun galat yang menyebutkannya.
 * Itu sebabnya dua baris ini tidak boleh dipisah.
 */
.ka-gulir-papan { max-height: 70vh; overflow: auto; }

/* LATARNYA WAJIB PEKAT. .ka-hari tadinya tanpa latar sama sekali; sel yang
   menempel dan tembus pandang akan menampilkan angka baris di bawahnya
   menembus kepala kolomnya — tanggal dan poin bertumpuk di satu tempat,
   dan tidak ada yang terbaca. */
.ka-gulir-papan .ka-papan thead th {
  position: sticky; top: 0; z-index: 4;
  background: var(--surface-1);
}

/* Sudut kiri-atas menempel ke DUA arah sekaligus — ke atas sebagai kepala,
   ke kiri sebagai kolom nama. Lapisannya harus di atas dua-duanya, kalau
   tidak "Nama" tertimpa angka tanggal waktu digulir menyerong. */
.ka-gulir-papan .ka-papan thead .ka-nama { z-index: 6; }

/* Di luar kotak bergulir, kolom nama tetap satu lapis di atas kepala
   barisnya sendiri. Nilai lama (3) dipertahankan untuk papan lain. */
.ka-papan thead .ka-nama { z-index: 3; }

/* min-width, BUKAN width.
   `width` pada sel tabel cuma sebuah usulan: waktu wadahnya sempit, mesin
   tata letak tetap memerasnya sampai muat — dan di 360px seluruh papan
   memampat jadi 334px, jadi TIDAK ADA yang perlu digeser dan tidak ada satu
   pun angka yang terbaca. `min-width` yang menahannya. */
.ka-hari { min-width: 26px; text-align: center; color: var(--muted); font-size: 11px; font-weight: 600; }
.ka-sel { min-width: 26px; text-align: center; cursor: pointer; }
.ka-sel.ada { font-weight: 700; background: color-mix(in srgb, var(--warn) 16%, transparent); }
.ka-sel:hover { background: color-mix(in srgb, var(--muted) 14%, transparent); }

/* ATURAN 3: ringkasannya tidak ikut tergeser. */
.ka-sum { text-align: right; white-space: nowrap; background: var(--surface-1); }
.ka-sum-bulan { border-left: 1px solid var(--border); }
.ka-total { min-width: 128px; }
.ka-asal { display: block; font-size: 11px; color: var(--muted); }
.ka-petunjuk { margin: 8px 18px 0; }

.ka-telat { padding: 14px 0 4px; }
.ka-telat .ka-head { padding: 0 18px 10px; }
.ka-telat h3 { margin: 0 0 2px; font-size: 15px; }
.ka-tbl-telat td { text-align: center; vertical-align: top; }
.ka-telat-sel { min-width: 96px; }
.ka-menit { display: block; font-size: 11px; color: var(--muted); }
.ka-baris-cut .ka-nama { border-left: 3px solid var(--crit); }
.ka-cut { min-width: 72px; }

/* Kotak isian. Tanpa <dialog>: peramban lama di ponsel kantor tidak punya
   showModal(), dan tombol yang diam waktu ditekan lebih buruk daripada
   tombol yang tidak ada. */
.ka-tirai { position: fixed; inset: 0; z-index: 60; background: rgba(8,12,22,.55);
            display: flex; align-items: center; justify-content: center; padding: 16px; }
.ka-tirai[hidden] { display: none; }
.ka-kotak { background: var(--surface-1); border: 1px solid var(--border); border-radius: 14px;
            width: min(520px, 100%); max-height: 88vh; overflow-y: auto; padding: 16px 18px; }
.ka-kotak-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.ka-kotak-head b { font-size: 15px; }
.ka-kotak-head .btn { margin-left: auto; }
.ka-form label { display: block; margin: 10px 0 0; font-size: 13px; }
.ka-form input[type="text"], .ka-form input[type="number"], .ka-form input[type="date"],
.ka-form select, .ka-form textarea {
  width: 100%; margin-top: 4px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface-2, transparent); color: inherit; font: inherit;
}
.ka-form .btn { margin-top: 14px; }
.ka-saran { margin: 6px 0 0; }

.ka-daftar { margin-bottom: 4px; }
.ka-item { border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; margin-bottom: 8px; }
.ka-item p { margin: 4px 0; white-space: pre-wrap; }
.ka-item form { display: inline; }

.ka-jenis { padding: 14px 18px; }
.ka-jenis summary { cursor: pointer; }
.ka-jenis-daftar { margin: 10px 0; padding-left: 18px; }
.ka-jenis-form { display: flex; gap: 8px; flex-wrap: wrap; }
.ka-jenis-form input { flex: 1 1 200px; padding: 8px 10px; border: 1px solid var(--border);
                       border-radius: 8px; background: transparent; color: inherit; font: inherit; }

.ka-belum { padding: 18px 20px; }
.ka-belum h3 { margin: 0 0 6px; }

@media (max-width: 700px) {
  .ka-papan .ka-nama { min-width: 130px; max-width: 150px; }
  .ka-head-kanan { margin-left: 0; width: 100%; }
}

/* ── Bonus IDN: pembagian lain di hari yang sama (21 Sep 2026) ───────────
   Dua pembagian jatuh di satu hari, history koinnya satu berkas. Kotak
   tambahan + keterangan jenis mana yang ikut diadu. */
.bni-koinlain { margin: 10px 0 4px; border: 1px dashed var(--border); border-radius: 10px; padding: 10px 12px; }
.bni-koinlain summary { cursor: pointer; }
.bni-koinlain p.sub { margin: 8px 0; }
.bni-koinjenis { margin: 10px 0 4px; font-size: 12.5px; line-height: 1.6; }
.bni-koinluar { display: block; color: var(--muted); margin-top: 3px; }
.bni-koinjam { margin: 6px 0 4px; font-size: 12.5px; color: var(--muted); line-height: 1.6; }

/* ── Bonus IDN: satu halaman, enam kotak (21 Sep 2026) ───────────────────
   Tab jadwal diganti bagian per jadwal, masing-masing dengan kotak
   periodenya sendiri; plus pemindah hasil waktu satu kiriman memuat dua. */
.bni-hariini { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; padding: 12px 16px; }
.bni-hariini.sepi { opacity: .85; }
.bni-bagian { border: 1px solid var(--border); border-radius: 14px; padding: 12px 14px; margin: 14px 0; }
.bni-bagian summary { cursor: pointer; display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.bni-bagian.hariini { border-color: color-mix(in srgb, var(--good) 45%, var(--border)); }
/* BAGIAN TERTUTUP HARUS BENAR-BENAR TERTUTUP.
   Peramban menyembunyikan isi <details> yang tidak `open` lewat aturan
   bawaannya sendiri — dan aturan bawaan KALAH dari aturan mana pun yang
   ditulis di berkas ini, berapa pun kekhususannya. `.bn-periode{display:flex}`
   dan `.bn-grid{display:grid}` dua-duanya anak langsung <details>, jadi
   tanpa baris di bawah ini ketiga bagian tergambar terbuka semua: enam kotak
   berkas dan tiga kotak periode berjejer sekaligus, dan panah summary-nya
   menunjuk "tertutup". */
.bni-bagian:not([open]) > *:not(summary) { display: none; }
.bni-tandahari {
  font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--good); border: 1px solid currentColor; border-radius: 99px; padding: 1px 8px;
}
.bni-periodekel { margin-top: 12px; }

.bni-pindah { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; padding: 12px 16px; }
.bni-pindahform { display: inline; }
.bni-pindahtab {
  display: inline-flex; flex-direction: column; gap: 1px; text-align: left;
  border: 1px solid var(--border); border-radius: 10px; padding: 6px 12px;
  background: transparent; color: inherit; font: inherit; cursor: pointer;
}
.bni-pindahtab.on { border-color: var(--s1, #155eef); cursor: default; }
.bni-pindahtab em { font-style: normal; font-size: 11px; color: var(--muted); }

/* ============================================================
   KOLOM TAMBAHAN DI DATA STAFF  (abby, 8 Oktober 2026)
   Kolom yang dibuat sendiri: teks, angka, atau rahasia.
   ============================================================ */

.sk-belumsql { margin: 0 0 14px; font-size: 12.5px; }

.sk-kelola { margin-bottom: 16px; }
.sk-kelola > summary {
  cursor: pointer; padding: 14px 16px; font-size: 13.5px;
}
/* Aturan bawaan peramban untuk <details> tertutup kalah dari aturan author
   mana pun — dan di sini .sk-kelola-isi memang punya display sendiri, jadi
   salinan ini WAJIB. Sudah tiga kali dibayar proyek ini (revisi 14, 25, 26). */
.sk-kelola:not([open]) > .sk-kelola-isi { display: none; }
.sk-kelola-isi { display: block; padding: 0 16px 16px; }

.sk-baris-bungkus { padding: 8px 0; border-bottom: 1px solid var(--border); }
.sk-baris-bungkus:last-child { border-bottom: 0; }

.sk-baris, .sk-baru {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 0;
}
.sk-baris input[type=text], .sk-baru input[type=text] { min-width: 150px; flex: 1 1 150px; }
.sk-baris select, .sk-baru select { min-width: 120px; }
.sk-sembunyi { display: inline-block; margin: 6px 0 0; }
.sk-baru { margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--border); }
.sk-nb { margin-top: 12px; font-size: 12px; }

/* ---------- isinya, di dalam sel Staff ---------- */
.sk-data { margin-top: 8px; }
.sk-data > summary {
  cursor: pointer; font-size: 11.5px; color: var(--muted); width: fit-content;
}
.sk-data > summary:hover { color: var(--ink); }

/* ── DATA LAIN — abby, 14 Oktober 2026 ────────────────────────────────

   "tolong kalau bisa diubah tampilannya supaya tidak susah lihatnya."
   Empat sebab yang dia sebut sendiri, dan keempatnya dijawab di sini.

   GRID, BUKAN FLEX PER BARIS. Ini jawaban untuk "isinya tidak sejajar",
   dan bedanya bukan gaya: flex menata tiap baris SENDIRI-SENDIRI, jadi
   isi kolom "Email Kerja" mulai di tempat yang berbeda dari isi kolom
   "PIN Laptop" — mata harus mencari di mana isinya tiap kali turun satu
   baris. Grid menyelaraskan kolomnya melintasi SEMUA baris sekaligus.

   Supaya itu bisa terjadi, .sk-item harus display: contents — nama, isi
   dan kotak isiannya jadi sel grid yang SEBENARNYA. Dibiarkan jadi div
   biasa, grid-nya cuma menyelaraskan enam div, dan isinya kembali tidak
   sejajar persis seperti sebelumnya. */
.sk-grid {
  display: grid;
  /* Kolom ISI dapat jatah paling besar, bukan dibagi rata: yang dibaca
     orang isinya, dan email kerja (auditor03.smb88@gmail.com, 25 huruf)
     adalah isi terpanjang yang wajar di sini. Kolom yang terlalu sempit
     memotongnya jadi dua baris — dan dua baris untuk satu nilai
     mengembalikan persis keluhan yang sedang dibetulkan. */
  grid-template-columns: max-content minmax(215px, 1fr) max-content max-content;
  gap: 7px 14px;
  align-items: center;
  margin-top: 8px;
}
.sk-item { display: contents; }

/* Garis putus-putus di tiap baris DIBUANG — keluhan keempat abby. Enam
   kolom berarti enam garis, dan tiap baris jadi terasa seperti kotak
   tersendiri. Yang memisahkan sekarang jaraknya. */
.sk-nama {
  color: var(--muted); font-size: 12px; white-space: nowrap;
  justify-self: start;
}
.sk-val { font-size: 12.5px; min-width: 0; word-break: break-word; }
.sk-kosong { color: var(--muted); font-style: italic; }
/* break-word, BUKAN break-all: break-all memotong di tengah kata walau
   masih muat, jadi "auditor03.smb88@gmail.com" terpenggal jadi
   "auditor03.smb88@gma / il.com" padahal kolomnya cukup. */
.sk-nilai { overflow-wrap: anywhere; }

/* Yang masih tersandi digambar redup; yang sudah dibuka justru ditegaskan,
   supaya terlihat jelas bahwa ada sesuatu yang sedang terbuka di layar. */
.sk-sandi { color: var(--muted); letter-spacing: 2px; }
.sk-val .btn.xs { margin-left: 6px; vertical-align: baseline; }

/* Kotak isiannya SEL GRID-nya sendiri, dan centangnya sel tersendiri lagi —
   lihat catatan di _datalain.php. Dibungkus jadi satu, baris yang tidak
   punya centang kotaknya tergeser sejauh lebar centang itu. */
input.sk-isi { width: 165px; padding: 4px 8px; font-size: 12px; justify-self: end; }
.sk-hapus-slot { min-width: 84px; }

/* Centang "kosongkan" sengaja kecil dan redup: dia perbuatan yang jarang,
   dan perbuatan jarang yang digambar sebesar perbuatan sehari-hari akan
   tertekan tanpa sengaja. */
.sk-hapus {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 10.5px; color: var(--muted); white-space: nowrap;
}
.sk-hapus input[type=checkbox] { margin: 0; }

/* Baris yang TIDAK boleh diubah orang ini tetap menempati kolom ketiga,
   kosong. Dihilangkan sama sekali, kolom ketiga menyusut untuk SEMUA
   baris dan isian baris lain ikut bergeser — penyelarasan yang baru saja
   dibuat batal karena satu baris yang tidak punya isian. */
span.sk-isi-kunci { min-width: 165px; }

/* Barisnya sendiri di tabel: menempel pada baris staff di atasnya. */
.sf-table tr.sk-panel > td {
  border-top: 0; padding-top: 0; padding-bottom: 14px;
}
.sk-data > summary { font-size: 12px; color: var(--ink-2); cursor: pointer; }

/* Tombolnya SATU, di kaki daftarnya, dan kalimat "kotak yang dibiarkan
   kosong tidak diubah" berdiri di sebelahnya. Kalimat itu bukan hiasan:
   di formulir per kolom yang lama kotak kosong berarti "kosongkan", jadi
   yang sudah terbiasa dengan aturan lama akan mengira satu tekan Simpan
   menghapus lima kolom yang dia biarkan kosong. */
.sk-borong-kaki {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 11px; padding-top: 9px;
  border-top: 1px solid var(--border);
}
.sk-borong-nb { font-size: 11px; }

/* DUA KOLOM BERDAMPINGAN di layar lebar — jawaban untuk "terlalu sempit,
   sebelahnya kosong". Barisnya sudah selebar tabel (~1000px); satu
   pasang nama+isi+isian cuma memakai separuhnya, dan separuh sisanya
   kosong lagi. Enam kolom jadi tiga baris.

   Penyelarasannya TETAP: kolom kiri dan kolom kanan masing-masing punya
   tiga kolom grid sendiri yang sejajar ke bawah. */
@media (min-width: 1150px) {
  .sk-grid {
    grid-template-columns:
      max-content minmax(215px, 1fr) max-content max-content
      max-content minmax(215px, 1fr) max-content max-content;
    column-gap: 26px;
  }
}

@media (max-width: 760px) {
  /* Di ponsel kolomnya ditumpuk lagi — tiga kolom di layar 360px membuat
     isian selebar 190px mendorong halaman keluar layar. */
  .sk-grid { grid-template-columns: 1fr; gap: 2px; }
  .sk-item { display: block; padding: 7px 0; border-bottom: 1px solid var(--border); }
  .sk-item:last-child { border-bottom: 0; }
  input.sk-isi { width: 100%; margin-top: 4px; justify-self: stretch; }
  .sk-hapus-slot { min-width: 0; display: block; margin-top: 3px; }
  span.sk-isi-kunci { min-width: 0; }
  .sk-baris input[type=text], .sk-baru input[type=text],
  .sk-baris select, .sk-baru select { flex: 1 1 100%; min-width: 0; }
}

@media (max-width: 760px) {
  .sk-isi input[type=text] { width: 100%; }
  .sk-isi { width: 100%; }
  .sk-baris input[type=text], .sk-baru input[type=text],
  .sk-baris select, .sk-baru select { flex: 1 1 100%; min-width: 0; }
}

/* ============================================================
   ABSENSI  (abby, 8 Oktober 2026)
   Jam mulai bertugas + izin keluar-masuk, satu menu.
   ============================================================ */

.ab-saya { border-left: 3px solid var(--side-accent); }
.ab-jam { font-size: 15px; font-weight: 650; color: var(--muted); }

.ab-aksi {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 4px 16px 14px;
}
.ab-aksi form { margin: 0; }
.ab-izin-form { display: flex; gap: 7px; align-items: center; }
.ab-izin-form input[type=text] { width: 190px; padding: 6px 10px; font-size: 12.5px; }
.ab-jatah { padding: 0 16px 14px; margin: 0; }
.ab-lebih { color: var(--crit); }

/* Tiga keadaan, tiga warna. "Di bawah SOP" sengaja KUNING, bukan merah:
   dia belum kena potongan, dan merah membuat orang mengira bonusnya sudah
   terpotong padahal belum. */
.ab-status { font-size: 12.5px; }
.ab-aman      { color: var(--good); }
.ab-bawah_sop { color: var(--warn); }
.ab-telat     { color: var(--crit); }
.ab-belum     { color: var(--muted); font-style: italic; }
.ab-nb, .ab-diubah { font-size: 11.5px; margin-top: 2px; }

.ab-tbl { width: 100%; border-collapse: collapse; min-width: 820px; }
.ab-tbl th {
  text-align: left; font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted); padding: 9px 10px; border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.ab-tbl td { padding: 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.ab-tbl tr:last-child td { border-bottom: 0; }
.ab-me td { background: var(--wash); }

.ab-total { font-weight: 650; font-size: 12.5px; margin-bottom: 4px; }
.ab-w-good { color: var(--good); }
.ab-w-warn { color: var(--warn); }
.ab-w-crit { color: var(--crit); }
.ab-satu { font-size: 11.5px; color: var(--muted); }
.ab-satu-lewat { color: var(--crit); }

.ab-betul { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin: 0; }
.ab-betul input[type=time] { width: 104px; padding: 4px 7px; font-size: 12px; }
.ab-betul input[type=text] { width: 128px; padding: 4px 7px; font-size: 12px; min-width: 0; }
.ab-sekarang { margin: 6px 0 0; }

.ab-sop { margin-top: 16px; }
.ab-sop > summary { cursor: pointer; padding: 14px 16px; font-size: 13.5px; }
/* Bawaan peramban untuk <details> tertutup kalah dari aturan author mana pun,
   dan .ab-sop-isi memang punya display sendiri — jadi salinan ini WAJIB. */
.ab-sop:not([open]) > .ab-sop-isi { display: none; }
.ab-sop-isi {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 18px; padding: 0 16px 16px; font-size: 12.5px;
}
.ab-sop-isi ul { margin: 6px 0 0; padding-left: 18px; }
.ab-sop-isi li { margin-bottom: 3px; }

@media (max-width: 760px) {
  .ab-izin-form, .ab-izin-form input[type=text] { width: 100%; }
  .ab-betul input[type=text], .ab-betul input[type=time] { flex: 1 1 100%; width: auto; }
}

/* ===================================================================
   KONSOL IZIN  (abby, 8 Oktober 2026)

   *"untuk tampilan kotaknya pas mau izin keluar mungkin bisa digedein
     lagi dan juga bisa nampak by warna"* — dengan contoh papan lamanya.

   Yang digedein bukan hiasan: kotak ini ditekan sambil berdiri, sering
   dari HP, dan yang salah di sini bukan tombol yang salah tekan —
   yang salah adalah menit yang terus berjalan tanpa ada yang sadar.
   Karena itu keadaannya satu kalimat sebesar mungkin, dan warnanya
   beda jauh, bukan dua baris teks yang beda satu kata.

   SEMUA warna lewat var() yang sudah ada — tidak ada satu pun warna
   mentah di sini. Warna mentah akan terbaca benar di satu tema dan
   hilang di tema yang lain, dan yang hilang adalah tanda merah.
   =================================================================== */
.ab-konsol { border-left: 3px solid var(--side-accent); padding-bottom: 14px; }
.ab-konsol-out { border-left-color: var(--crit); }

.ab-clock {
  display: flex; align-items: flex-start; gap: 12px;
  margin: 14px 16px; padding: 16px 20px;
  border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface-sunk);
}
.ab-clock-tgl {
  font-size: 11.5px; font-weight: 700; letter-spacing: .14em;
  color: var(--ink-2); text-transform: uppercase;
}
.ab-clock-nama { font-size: 13px; color: var(--ink-2); margin-top: 3px; }
.ab-clock-jam {
  font-size: 46px; line-height: 1.05; font-weight: 800;
  letter-spacing: -.01em; margin-top: 2px; color: var(--ink);
}
.ab-live-dot {
  font-size: 10px; font-weight: 800; letter-spacing: .1em;
  color: var(--good); white-space: nowrap;
}

/* --- dua tombol besar --- */
.ab-duo, .ab-mulai-form { display: flex; gap: 12px; margin: 0 16px 10px; }
.ab-mulai-form { margin-bottom: 10px; }
.ab-duo-f { display: flex; flex-direction: column; gap: 7px; flex: 1 1 0; margin: 0; }
.ab-duo-f input[type=text] { width: 100%; padding: 7px 11px; font-size: 12.5px; }
.ab-duo > .ab-big, .ab-duo-f > .ab-big, .ab-mulai-form > .ab-big { flex: 1 1 0; }

.ab-big {
  min-height: 52px; font-size: 16px; font-weight: 700;
  border-radius: 12px; letter-spacing: .01em;
}
.ab-big-start {
  background: linear-gradient(100deg, var(--side-accent),
              color-mix(in srgb, var(--side-accent) 40%, #6d4cf0) 95%);
  border-color: transparent; color: #fff;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--side-accent) 28%, transparent);
}
.ab-big[disabled] {
  opacity: .42; box-shadow: none; cursor: not-allowed; filter: grayscale(.55);
}

/* --- satu kalimat keadaan, sebesar mungkin --- */
.ab-state {
  margin: 0 16px 12px; padding: 16px; border-radius: 12px;
  text-align: center; font-size: 19px; font-weight: 800;
  letter-spacing: .22em; text-transform: uppercase;
  border: 1px solid var(--border); background: var(--surface-sunk);
  color: var(--ink-2);
}
.ab-state-out {
  background: var(--crit); border-color: transparent; color: #fff;
  letter-spacing: .18em;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--crit) 22%, transparent),
              0 10px 30px color-mix(in srgb, var(--crit) 40%, transparent);
}
.ab-sejak {
  margin: 0 16px 10px; padding: 10px; border-radius: 10px;
  text-align: center; font-size: 12.5px; color: var(--ink-2);
  background: var(--surface-sunk); border: 1px solid var(--border);
}
.ab-masuk-baris { padding: 0 16px 10px; }

/* --- tiga angka hari ini --- */
.ab-kotak3 {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; margin: 0 16px 10px;
}
.ab-kotak {
  padding: 13px 15px; border-radius: 12px;
  border: 1px solid var(--border); background: var(--surface-sunk);
}
.ab-kotak-tit {
  font-size: 10.5px; font-weight: 700; letter-spacing: .1em;
  color: var(--ink-2); text-transform: uppercase;
}
.ab-kotak-ang { font-size: 25px; font-weight: 800; margin-top: 5px; }

/* --- notice lewat 15 menit --- */
/* KELAS PENYEMBUNYI, dan satu-satunya tempat !important dipakai di
   berkas ini — dengan alasan yang sudah terbukti, bukan untuk menang
   cepat.

   11 Oktober 2026: kotak kamera menggambar KOTAK HITAM di layar abby,
   padahal elemennya sudah diberi .ab-hide. Sebabnya kekhususan CSS:

       .ab-cam-panggung img  ->  (0,1,1)   display: block
       .ab-hide              ->  (0,1,0)   display: none

   Pemilih yang menyebut nama elemen MENANG, jadi .ab-hide diam-diam
   tidak melakukan apa pun di dalam panggung kamera. Tidak ada error,
   tidak ada peringatan — cuma kotak hitam yang tidak pernah hilang.

   Perangkapnya akan terulang di tiap pembungkus yang menata anaknya
   lewat nama elemen, jadi yang dibetulkan kelasnya, bukan satu
   pemakaiannya. Kelas penyembunyi yang kadang menyembunyikan bukan
   kelas penyembunyi. */
.ab-hide { display: none !important; }
.ab-notice {
  margin: 0 16px 12px; padding: 14px 16px; border-radius: 12px;
  border: 1px solid var(--crit);
  background: color-mix(in srgb, var(--crit) 11%, var(--surface-sunk));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit) 14%, transparent);
}
.ab-notice-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 15px; color: var(--warn); margin-bottom: 10px;
}
.ab-notice-tag {
  font-size: 10.5px; font-weight: 800; letter-spacing: .08em;
  padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--crit); color: var(--crit);
  background: color-mix(in srgb, var(--crit) 12%, transparent);
}
.ab-notice-isi { display: grid; gap: 7px; font-size: 12.5px; color: var(--ink-2); }
.ab-notice-isi label { display: block; }
.ab-notice-isi textarea {
  display: block; width: 100%; margin-top: 6px; padding: 9px 11px;
  font: inherit; font-size: 12.5px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--surface-1);
  color: var(--ink); resize: vertical;
}
.ab-notice-kaki { margin: 11px 0 0; font-size: 12.5px; color: var(--warn); }

/* --- siapa yang sedang di luar --- */
.ab-diluar { margin-top: 16px; }
.ab-sepi { padding: 0 16px 16px; margin: 0; }
.ab-luar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 16px 8px; padding: 11px 14px;
  border-radius: 11px; border: 1px solid var(--border);
  background: var(--surface-sunk); font-size: 12.5px;
}
.ab-luar-lewat {
  border-color: var(--crit);
  background: color-mix(in srgb, var(--crit) 10%, var(--surface-sunk));
}
.ab-luar-mnt { font-size: 14px; }
.ab-luar-lewat .ab-luar-mnt { color: var(--crit); }
.ab-luar-tutup { display: flex; gap: 6px; align-items: center; margin: 0; }
.ab-luar-tutup input[type=text] { width: 150px; padding: 4px 8px; font-size: 12px; min-width: 0; }
.ab-luar-nb { margin: -3px 16px 10px; padding: 0 4px; font-size: 11.5px; color: var(--warn); }

@media (max-width: 760px) {
  .ab-clock-jam { font-size: 36px; }
  .ab-duo { flex-direction: column; }
  .ab-kotak3 { grid-template-columns: 1fr; }
  .ab-state { font-size: 15px; letter-spacing: .14em; }
}

/* --- rekap absensi per periode --- */
.ab-rk-cut {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em;
  background: var(--crit); color: #fff;
}
.ab-rk-det > td { padding: 0 10px 10px !important; }
.ab-rk-det summary { cursor: pointer; font-size: 11.5px; color: var(--ink-2); padding: 4px 0; }
.ab-rk-hari { display: grid; gap: 5px; padding: 6px 0 2px; }
.ab-rk-h {
  display: flex; gap: 9px; align-items: center; flex-wrap: wrap;
  font-size: 11.5px; padding: 6px 10px; border-radius: 9px;
  background: var(--surface-sunk); border: 1px solid var(--border);
}
.ab-shift-nb { margin-top: 3px; font-size: 11.5px; }

/* ===================================================================
   SOP & SOUNDINGAN — DUA TAB, dan rak CATATAN

   abby, 9 Oktober 2026: *"catatannya terlalu memanjang ke bawah,
   sepertinya kita bikin tab pisah aja deh"*.

   Versi 8 Oktober memakai dua kolom bersebelahan. Itu salah untuk isi
   seperti ini: satu catatan dengan empat tangkapan layar tingginya
   sudah melebihi layar, jadi kolom catatan menjulur jauh ke bawah
   sementara kolom SOP sudah habis — dan yang membaca SOP harus
   menggulir melewati catatan yang tidak sedang dia cari.

   Dua kolom cocok untuk dua daftar yang panjangnya sebanding. Dua rak
   ini tidak. Aturan .sop-dua / .sop-kol DICABUT, bukan disimpan
   "siapa tahu dipakai lagi": aturan CSS yang tidak dipakai satu pun
   halaman adalah aturan yang suatu hari menempel di kelas bernama mirip
   dan tidak ada yang tahu dari mana datangnya.
   =================================================================== */
.cat-head { padding-bottom: 2px; }
/* Angka di tab Catatan. Kecil dan redup — dia keterangan, bukan
   peringatan: catatan tidak ada yang perlu dikejar. */
.tab-n {
  display: inline-block; margin-left: 5px; padding: 0 6px;
  border-radius: 999px; font-size: 10.5px; font-weight: 700;
  background: var(--surface-sunk); color: var(--ink-2);
  border: 1px solid var(--border);
}
.cat-head h3 { margin: 0 0 2px; font-size: 15px; }

.cat { border-left: 3px solid var(--side-accent); }
.cat-sepi { padding: 26px 16px; }

/* --- petak foto: banyak dalam satu catatan --- */
.cat-foto {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 8px;
  margin-top: 11px;
}
.cat-f { position: relative; }
.cat-f img {
  display: block; width: 100%; height: 92px; object-fit: cover;
  border-radius: 9px; border: 1px solid var(--border);
  background: var(--surface-sunk);
}
/* Tombol hapus foto: kecil, di sudut, dan HANYA untuk atasan. Ditaruh
   di tengah atau dibuat besar, dia akan tertekan oleh orang yang cuma
   mau membuka fotonya — dan foto yang terhapus tidak bisa kembali. */
.cat-f-x { position: absolute; top: 4px; right: 4px; margin: 0; }
.cat-f-x button {
  width: 22px; height: 22px; padding: 0; line-height: 1;
  border-radius: 7px; cursor: pointer; font-size: 14px; font-weight: 700;
  border: 1px solid var(--border);
  background: color-mix(in srgb, var(--crit) 78%, transparent);
  color: #fff;
}
.cat-f-x button:hover { background: var(--crit); }

.ab-bukan-hari-ini { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ab-bukan-hari-ini .btn { margin-left: auto; }

/* ===================================================================
   FOTO ABSENSI  (abby, 11 Oktober 2026)

   Kotak kamera dibuat BESAR dan di tengah konsol — ini layar yang
   dilihat orang sambil berdiri, sering dari HP, dan tombol jepret
   yang meleset berarti dia mengulang seluruh prosesnya.
   =================================================================== */
.ab-cam {
  margin: 0 16px 12px; padding: 14px 16px; border-radius: 14px;
  border: 1px solid var(--side-accent);
  background: var(--surface-sunk);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--side-accent) 14%, transparent);
}
.ab-cam-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }

/* Panggungnya persegi dan tetap tingginya, supaya halaman tidak
   melompat waktu video berganti jadi gambar hasilnya. */
.ab-cam-panggung {
  position: relative; width: 100%; max-width: 320px; margin: 0 auto;
  aspect-ratio: 1 / 1; border-radius: 12px; overflow: hidden;
  background: #000; border: 1px solid var(--border);
}
.ab-cam-panggung video,
.ab-cam-panggung img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* Pratinjau dicerminkan supaya terasa seperti cermin waktu mengambil
   foto. HASILNYA TIDAK — yang tersimpan harus searah dengan wajah
   aslinya, kalau tidak tulisan apa pun di belakangnya terbaca terbalik
   dan foto itu berhenti berguna sebagai bukti. */
.ab-cam-panggung video { transform: scaleX(-1); }

.ab-cam-aksi { display: flex; gap: 10px; margin-top: 12px; }
.ab-cam-aksi .btn { flex: 1 1 0; }

.ab-cam-gagal {
  margin-top: 12px; padding: 12px 14px; border-radius: 11px;
  border: 1px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
}
.ab-cam-gagal-teks { margin: 0 0 9px; font-size: 12.5px; color: var(--ink); }
/* "Coba lagi" selebar kotaknya dan DI ATAS isian alasan: yang baru
   memberi izin harus menemukannya tanpa membaca apa pun. Jalan keluarnya
   ada di bawah, tetap satu tekan, cuma tidak lagi jadi satu-satunya
   yang terlihat. */
#ab-cam-coba { width: 100%; margin-bottom: 11px; }
.ab-cam-gagal label { display: block; font-size: 12.5px; margin-bottom: 9px; }
.ab-cam-gagal input[type=text] { width: 100%; padding: 7px 10px; font-size: 12.5px; margin-top: 5px; }

/* --- penanda di papan --- */
.ab-foto { display: inline-block; margin-bottom: 4px; }
.ab-foto img {
  display: block; width: 46px; height: 46px; object-fit: cover;
  border-radius: 9px; border: 1px solid var(--border);
}
.ab-foto-kecil img { width: 26px; height: 26px; border-radius: 7px; }
.ab-tanpa-foto { font-size: 11.5px; color: var(--warn); }
.ab-foto-nb { padding: 0 16px 8px; margin: 0; }

@media (max-width: 760px) {
  .ab-cam-panggung { max-width: 100%; }
  .ab-cam-aksi { flex-direction: column; }
}

/* Selesai bertugas. Warnanya TENANG, bukan merah: pulang itu akhir yang
   wajar, bukan peringatan. Merah di sini akan bersaing dengan OUT!!!
   yang memang perlu ditakuti. */
.ab-big-pulang {
  background: var(--surface-sunk); border-color: var(--border); color: var(--ink);
}
.ab-big-pulang:hover { border-color: var(--side-accent); }
.ab-sudah-pulang {
  margin: 0 16px 10px; padding: 12px; border-radius: 11px;
  text-align: center; font-size: 13px; color: var(--ink-2);
  background: var(--surface-sunk); border: 1px solid var(--border);
}

/* =====================================================================
   BAR WAKTU IZIN — abby, 13 Oktober 2026:
   "bisa dibikin seperti ini aja? jadi tidak bentuk angka-angka jelek gini."

   ── WARNANYA DIHITUNG, BUKAN DIPILIH DENGAN MATA ───────────────────

   Tiga isian: bertugas, izin, izin yang lewat 15 menit. Ketiganya lolos
   pemeriksaan buta warna dan kontras di DUA mode, dan angkanya ditulis
   di sini supaya yang menggantinya tahu ada yang harus dijalankan ulang:

     terang  #047857 / #d97706 / #be123c
             pemisahan buta warna ΔE 11,0 (protan) — LULUS
             kontras ke latar bar   >= 3:1 ketiganya — LULUS
     gelap   #10b981 / #fbbf24 / #fb7185
             pemisahan ΔE 12,3 (protan) — LULUS
             kontras 9,6 / 11,1 / 6,9 — LULUS

   Warna gelapnya DIPILIH, bukan hasil membalik warna terangnya: #047857
   di atas latar gelap kontrasnya cuma 2,1 — hampir tidak terlihat.

   ── KENAPA YANG LEWAT 15 MENIT JUGA BERGARIS ───────────────────────

   Merah sendirian tidak cukup. Yang tidak bisa membedakan merah dari
   oranye adalah 1 dari 12 laki-laki, dan papan ini dibaca leader untuk
   MENILAI orang — penilaian yang bergantung pada warna saja adalah
   penilaian yang salah untuk sebagian pembacanya. Jadi yang lewat batas
   diberi garis miring, dan menitnya tetap tertulis sebagai angka di
   atas barnya.
   ===================================================================== */
.ab-bar { margin: 6px 0 2px; }

/* Jalurnya = jendela shift + 8 jam. Tingginya tetap supaya baris tabel
   tidak melompat waktu seseorang belum punya izin sama sekali. */
.ab-bar-jalur {
  position: relative; height: 10px; border-radius: 6px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  overflow: hidden;
}
.ab-bar-besar .ab-bar-jalur { height: 18px; border-radius: 9px; }

.ab-bar-jalur > span {
  position: absolute; top: 0; bottom: 0;
  border-radius: 4px;
  min-width: 2px;
}

/* Batang bertugas — di bawah, karena izin terjadi DI DALAM jam kerja.

   background-color, BUKAN background. 13 Okt 2026: ditulis dengan
   singkatan `background:`, pengaturan mode gelap (kekhususannya lebih
   tinggi) ikut mengosongkan background-image — dan garis miring penanda
   "lewat 15 menit" hilang sama sekali di mode gelap. Tidak ada error,
   tidak ada peringatan; cuma satu penanda yang diam-diam tidak ada
   justru di mode yang dipakai tim malam. */
.ab-bar-kerja { background-color: #047857; }
.ab-bar-izin  { background-color: #d97706; }
.ab-bar-lewat { background-color: #be123c; }

:root:not([data-theme="light"]) .ab-bar-kerja,
:root[data-theme="dark"] .ab-bar-kerja { background-color: #10b981; }
:root:not([data-theme="light"]) .ab-bar-izin,
:root[data-theme="dark"] .ab-bar-izin  { background-color: #fbbf24; }
:root:not([data-theme="light"]) .ab-bar-lewat,
:root[data-theme="dark"] .ab-bar-lewat { background-color: #fb7185; }

/* Cincin selebar 2px warna latar di sekeliling potongan izin. Tanpa dia,
   izin yang menempel di tepi batang hijau terbaca menyatu dengannya, dan
   dua izin yang berdempetan terbaca satu izin panjang. */
.ab-bar-izin { box-shadow: 0 0 0 2px var(--surface-sunk); }

/* Yang lewat batas: garis miring DI ATAS warnanya.

   GARISNYA PUNYA DUA WARNA, dan itu bukan kelebihan. Garis putih di atas
   #be123c yang gelap memang terlihat; di atas #fb7185 yang terang (mode
   gelap) dia hampir hilang — dan yang hilang justru satu-satunya penanda
   yang tidak bergantung pada warna. Jadi di mode gelap garisnya gelap. */
.ab-bar-lewat {
  --ab-garis: rgba(255,255,255,0.62);
  background-image: repeating-linear-gradient(135deg,
    var(--ab-garis) 0 2px, transparent 2px 5px);
}
:root:not([data-theme="light"]) .ab-bar-lewat,
:root[data-theme="dark"] .ab-bar-lewat { --ab-garis: rgba(12,16,26,0.58); }

/* ── BENTUK UJUNGNYA ────────────────────────────────────────────────
   DITULIS `.ab-bar-jalur > .ab-bar-jalan`, bukan `.ab-bar-jalan` saja.

   Sebabnya sudah pernah menggigit proyek ini: `.ab-bar-jalur > span`
   kekhususannya (0,1,1) mengalahkan `.ab-bar-jalan` (0,1,0), jadi
   border-radius: 4px miliknya menang dan ujung tumpulnya tidak pernah
   tergambar. Tidak ada error — cuma penanda "masih berjalan" yang
   diam-diam tidak ada. Sama persis dengan .ab-hide lawan
   .ab-cam-panggung img pada 11 Oktober. */
.ab-bar-jalur > .ab-bar-jalan {
  border-top-right-radius: 0; border-bottom-right-radius: 0;
}
.ab-bar-jalur > .ab-bar-pkiri {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
  border-left: 2px dotted var(--surface-1);
}
.ab-bar-jalur > .ab-bar-pkanan {
  border-top-right-radius: 0; border-bottom-right-radius: 0;
  border-right: 2px dotted var(--surface-1);
}

/* Sasaran sentuh lebih besar daripada gambarnya — potongan 2 menit
   lebarnya 3px di layar, dan jari tidak bisa mengenai 3px. */
.ab-bar-izin { cursor: pointer; }
.ab-bar-jalur > span:focus-visible { outline: 2px solid var(--side-accent); outline-offset: 1px; }

.ab-bar-kaki {
  display: flex; align-items: center; gap: 8px;
  margin-top: 3px; font-size: 10.5px; color: var(--muted);
}
.ab-bar-besar .ab-bar-kaki { font-size: 12px; margin-top: 5px; }
.ab-bar-acuan { font-size: 10px; }

/* Keterangan sentuhan. Tingginya DIPESAN sejak awal — diisi belakangan
   tanpa tinggi pesanan, seluruh tabel bergeser tiap kali orang menyentuh
   satu potongan. */
.ab-bar-ket {
  min-height: 15px; font-size: 11px; color: var(--ink-2);
  margin-top: 1px;
}
.ab-bar-besar .ab-bar-ket { min-height: 18px; font-size: 12.5px; }

/* Izin yang jatuh DI LUAR jendela. Satu-satunya hal di sini yang tidak
   boleh disembunyikan di balik sentuhan. */
.ab-bar-luar {
  margin-top: 4px; font-size: 11px; color: var(--crit);
}

@media (max-width: 640px) {
  .ab-bar-besar .ab-bar-jalur { height: 22px; }
}

/* Judul bar konsol. Legenda ditulis sebagai KALIMAT, bukan kotak-kotak
   warna: tiga kotak kecil berjajar di atas bar yang juga berwarna membuat
   mata harus mencocokkan dua tempat. Kalimatnya menyebut warnanya sekali,
   dan sesudah itu barnya bisa dibaca sendiri. */
.ab-bar-judul { font-size: 12px; color: var(--ink-2); margin-bottom: 6px; }
.ab-bar-judul .muted { font-size: 11px; }

/* Baris bar — abby, 13 Okt 2026: "di bagian bawah aja jadi bisa lebih
   panjang barnya".

   MENEMPEL pada baris staff di atasnya: garis atasnya dihapus, dan
   latarnya mengikuti. Dibiarkan punya garis sendiri, dia terbaca sebagai
   BARIS KEDUA — orang kedelapan dan barnya terbaca seperti dua orang. */
.ab-tbl tr.ab-bar-baris > td {
  border-top: 0;
  padding-top: 0;
  padding-bottom: 14px;
}
.ab-bar-kepala {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 5px; font-size: 11.5px;
}
.ab-bar-kepala .ab-total { margin: 0; }

/* Barnya sendiri lebih tinggi di sini daripada waktu masih di dalam
   kolom: sekarang dia selebar tabel, dan jalur setinggi 10px yang
   panjangnya 1100px terbaca seperti garis, bukan seperti batang. */
.ab-bar-baris .ab-bar-jalur { height: 14px; border-radius: 7px; }

/* ── SIAPA YANG MENGISI ANGKA CHAT (16 Oktober 2026) ─────────────────────
 *
 * abby, melihat "CS TOTAL (936) DOES NOT MATCH WEBSITE TOTAL (1.996)":
 * *"mau saya cek siapa yang salah cek"*.
 *
 * Penanda kecil di kanan kotak isian. Keterangannya muncul di SATU baris
 * di bawah tabel (.cs-siapa-ket), bukan kotak melayang per baris — di
 * papan 14 baris, kotak melayang menutupi justru angka yang sedang
 * dibandingkan.
 */
.cs-isi { position: relative; white-space: nowrap; }

/* Penandanya TIDAK position:absolute di atas kotaknya.
   Ditumpuk di atas kotak isian, dia menutupi digit terakhir angka empat
   huruf — dan angka itulah seluruh isi kolom ini. Jadi dia mengambil
   tempatnya sendiri di sebelah kanan. */
.cs-siapa {
  margin-left: 5px; padding: 0; border: 0; background: none;
  /* LEBARNYA DIPATOK, tidak dibiarkan mengikuti isinya. Ikon "orang" dan
     ikon "pensil" tidak sama lebarnya, jadi tanpa ini kotak isian di baris
     yang pernah dibetulkan melenceng dari tetangganya — dan lebar yang
     sama ini juga yang dipakai tempat kosongnya di bawah. */
  display: inline-block; width: 14px; text-align: center;
  font-size: 11px; line-height: 1; cursor: pointer; vertical-align: middle;
  opacity: .45; color: var(--muted);
  transition: opacity .12s ease;
}
.cs-siapa:hover, .cs-siapa:focus-visible, .cs-siapa.pilih { opacity: 1; }
.cs-siapa:focus-visible { outline: 2px solid var(--side-accent); outline-offset: 2px; border-radius: 4px; }

/* PERNAH DIBETULKAN ORANG LAIN — satu-satunya keadaan yang diberi warna.
   Warnanya oranye peringatan, bukan hijau/merah: angkanya tidak sedang
   dinilai benar atau salah, cuma disebut bahwa dia pernah berpindah
   tangan. Lencana ini yang paling dicari abby waktu mencari "siapa yang
   salah cek". */
.cs-siapa.diubah { opacity: .85; color: var(--warn); }

/* Tempat kosong untuk baris yang belum diisi siapa pun. Lebar dan
   margin-nya SAMA PERSIS dengan .cs-siapa di atas — dibiarkan berbeda,
   kotak isian di baris itu melar 4px ke kanan, dan deretan kotak yang
   tidak sejajar itu persis keluhan abby di panel Data lain (revisi 49). */
.cs-siapa-slot {
  display: inline-block; margin-left: 5px; width: 14px; height: 11px;
  vertical-align: middle;
}

/* TINGGINYA DIPATOK walau kosong. Baris keterangan yang muncul-hilang
   membuat seluruh tabel di atasnya bergeser naik-turun tiap kali
   penandanya ditekan — dan yang bergeser waktu ditekan terbaca seperti
   halaman yang rusak. */
.cs-siapa-ket {
  min-height: 16px; margin: 7px 0 0;
  font-size: 11.5px; color: var(--muted);
}

/* ── POTONGAN LANGSUNG BENTUK PERSEN (18 Oktober 2026) ───────────────────
 *
 * abby: *"pemotongan langsung bentuk persen jadi bukan poin... contoh hari
 * ini ada staff melakukan kesalahan fatal dan akan di potong 25%
 * sekaligus."*
 *
 * Satu sel papan bisa berisi DUA SATUAN. Angka telanjang "25" di sel yang
 * selama ini berarti poin akan dibaca sebagai 25 POIN oleh semua orang yang
 * sudah terbiasa dengan papan ini — dan 25 poin artinya 30%, bukan 25%.
 * Jadi persennya dibedakan dua kali: tanda % di tulisannya, dan warna.
 */
.ka-sel-persen {
  display: block; font-size: 10.5px; font-weight: 800;
  color: var(--crit); line-height: 1.2;
}

/* Selnya sendiri ditandai lebih tegas daripada sel poin biasa. Sel poin
   memakai --warn 16%; yang ini --crit, karena potongan langsung tidak naik
   pelan-pelan lewat tangga — dia sudah final waktu dicatat. */
.ka-sel.ka-langsung {
  background: color-mix(in srgb, var(--crit) 15%, transparent);
}

/* Asal TOTAL: potongan langsung disebut terpisah dari poin dan telat. */
.ka-asal-langsung { color: var(--crit); }

/* Pilihan poin / persen di kotak isian. */
.ka-mode { display: flex; gap: 16px; flex-wrap: wrap; margin: 12px 0 2px; }
.ka-mode label {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 0; font-size: 13px; cursor: pointer;
}
.ka-mode input { margin: 0; }

.ka-persen-nb { margin: 6px 0 0; }
.ka-persen-nb[hidden], .ka-saran[hidden] { display: none; }

/* Keterangan "SQL-nya belum dijalankan" di kotak isian Kesalahan Auditor.
   Warnanya warn, bukan crit: tidak ada yang rusak, cuma ada satu langkah
   yang belum dikerjakan. */
.ka-sql-nb { margin: 12px 0 2px; color: var(--warn); }

/* Keterangan "SQL-nya belum dijalankan" di Kelola Shift. */
.sh-sql-nb { display: block; margin-top: 6px; color: var(--warn); }
